/////////////////////////////////////////////////// // Folders. /////////////// // David Grant /////////////// // An experiment in... folders. /////////////// /////////////////////////////////////////////////// ///////////// Constants /////////////////////////// //============================================Sizes //http://seizethedave.com/etc/p5/folders/folders.pde int folder_width = 15; int folder_height = 8; int folder_spacing = 10; //===========================================Colors color background_color = #ffffff; color folder_color = #ffe2bb; color folder_color_open = #ffe222; color folder_color_empty = #eeeeee; color folder_color_border = #aaaaaa; color folder_font_color = #000000; //=====================================Easing stuff //gC go2 = new gC(2,9,12); gC goo = new gC(3,12,12); //gC go3 = new gC(2,12,2); void setColor(){ goo.setHueTab(16,6); // go2.setHueTab(6,2); //go3.setHueTab(6,2); //folder_color = goo.C[0]; //folder_color_open = go2.C[0]; //goo.setHueTab(10,1); } double ease_duration = 60; double ease_iter_count = ease_duration; double ease_begin_x, ease_begin_y, ease_dest_x, ease_dest_y; float translateX, translateY, previousX, previousY; Folder[] startupFolders = new Folder[] { /*new Folder ("x", new Folder[] { new Folder ("Goo"), new Folder ("est"), new Folder ("osti"), new Folder ("de con", new Folder[]{new Folder ("noexit")}), new Folder ("d'pas fiab"), }),*/ new Folder ("x") }; Folder activeFolder = null; //============================================Stuff void setup () { size (700, 80); textFont (loadFont("Futura-Medium.vlw.gz"), 12); textMode (ALIGN_CENTER); colorMode(HSB, 12); setColor(); startupFolders[0].split(); startupFolders[0].setLevel(0); previousX = width / 2 - folder_width / 2; previousY = 0; translateX = 0; translateY = height / 2 - folder_height / 2; } void loop () { background (background_color); line(width/4,0,width*0.75,0); // Shift view if necessary. if (ease_iter_count < ease_duration) { ease_iter_count++; translateX = width / 2 - folder_width / 2 - (float) EaseOutQuart(ease_iter_count, ease_begin_x, ease_dest_x - ease_begin_x, ease_duration); translateY = height / 2 - folder_height / 2 - (float) EaseOutQuart(ease_iter_count, ease_begin_y, ease_dest_y - ease_begin_y, ease_duration); } translate (translateX, translateY); int widthEach = folder_width + folder_spacing; int intOffset = widthEach * startupFolders.length / 2; for (int i = 0; i < startupFolders.length; i++) { DrawFolder(startupFolders[i], width / 2 + i * widthEach - intOffset, 10); } } // t: current time, b: beginning value, c: change in value, d: duration // t and d can be frames or seconds/milliseconds double EaseOutQuart (double t, double b, double c, double d) { return -c * ((t=t/d-1)*t*t*t - 1) + b; } void DrawFolder (Folder origin, int x, int y) { origin.Render(x, y); if (origin.HasChildren() && origin.m_bExpanded) { // Draw little stublet beneath parent folder. //stroke (folder_color_border); line (x + folder_width / 2, y + folder_height, x + folder_width / 2, y + folder_height + folder_spacing / 2); // Recurse subfolders. int widthEach = folder_width + folder_spacing; int intOffset = widthEach * (origin.m_children.length - 1) / 2; int drawX, drawY = y + folder_height + folder_spacing; int lineX1 = 0, lineX2 = 0; for (int i = 0; i < origin.m_children.length; i++) { drawX = x + i * widthEach - intOffset; DrawFolder(origin.m_children[i], drawX, drawY); // Draw stub above folder. //stroke (folder_color_border); line (drawX + folder_width / 2, drawY, drawX + folder_width / 2, drawY - folder_spacing / 2); // Make note of horizontal connector start/end points. if (1 < origin.m_children.length) { if (0 == i) lineX1 = drawX + folder_width / 2; else if (origin.m_children.length - 1 == i) lineX2 = drawX + folder_width / 2; } } // For each child folder. // Finally draw horizontal line. if (1 < origin.m_children.length) { line (lineX1, drawY - folder_spacing / 2, lineX2, drawY - folder_spacing / 2); } } } void mousePressed () { // Account for translation in passing mouse coordinates. Folder folderClicked = GetClickedFolder (null, mouseX - (int) translateX, mouseY - (int) translateY); // We'll only act upon a folder if it bears children. if (folderClicked != null && folderClicked.HasChildren()) { // Allow clicking on expanded parent without collapse. // Ease to folder when clicked first time. // Folder will collapse when clicked second time. if (!folderClicked.m_bExpanded || folderClicked == activeFolder) folderClicked.Click(); activeFolder = folderClicked; // Initiate easing. if (ease_iter_count < ease_duration) { // User clicked before ease had finished. // Figure begin point from translation. ease_begin_x = width / 2 - translateX - folder_width / 2; ease_begin_y = height / 2 - translateY - folder_height / 2; } else { ease_begin_x = previousX; ease_begin_y = previousY; } ease_dest_x = previousX = folderClicked.m_x; ease_dest_y = previousY = folderClicked.m_y; ease_iter_count = 0; } } Folder GetClickedFolder (Folder origin, int x, int y) { Folder returnFolder = null; if (null == origin) { // null passed - process top-level folders. for (int i = 0; i < startupFolders.length; i++) { returnFolder = GetClickedFolder(startupFolders[i], x, y); if (null != returnFolder) break; } } else { // Folder passed; recurse children. if (origin.HitTest(x, y)) { returnFolder = origin; } else if (origin.HasChildren() && origin.m_bExpanded) { for (int i = 0; i < origin.m_children.length; i++) { returnFolder = GetClickedFolder (origin.m_children[i], x, y); if (null != returnFolder) break; } } } return returnFolder; } class Folder { int level=0; public String m_name; public Folder m_parent = null; public Folder[] m_children; public int m_x, m_y; public boolean m_bExpanded = false; public Folder (String name) { m_name = name; m_name = level+""; if(random(5)>4) split(); } public Folder (String name, Folder parent) { m_name = name; m_parent = parent; level=m_parent.level+1; m_name = level+""; if(level<15) if(random(2+level/5)>1+level/5) split(); } public Folder (String name, Folder[] children) { m_name = name; m_children = children; for (int i = 0; i < m_children.length; i++) m_children[i].m_parent = this; } void split(){ m_children = new Folder[int(random(10))+1]; for (int i = 0; i < m_children.length; i++) { m_children[i]=new Folder("",this); } } public void Render (int x, int y) { m_x = x; m_y = y; // Draw folder rectangle. if (m_bExpanded) //fill (folder_color_open); fill (goo.C[level].setLum(9)); else if (HasChildren()) //fill (folder_color); fill (goo.C[level].getC()); else fill (goo.C[level].setSat(2)); stroke (goo.C[level].setLum(8)); rect (x, y, folder_width, folder_height); // Draw text. //noStroke(); fill (folder_font_color); text (m_name, x + folder_width / 2, y + folder_height / 2 + 4); } public boolean HitTest (int x, int y) { return x >= m_x && x <= m_x + folder_width && y >= m_y && y <= m_y + folder_height; } public void Click () { if (HasChildren()) { if (!m_bExpanded && null != m_parent) m_parent.CloseChildren(); m_bExpanded = !m_bExpanded; } } void setLevel(int L){ if (HasChildren()) for(int i=0; i