1 /*
2     Copyright (C) 2009 Andrew Caudwell (acaudwell@gmail.com)
3 
4     This program is free software; you can redistribute it and/or
5     modify it under the terms of the GNU General Public License
6     as published by the Free Software Foundation; either version
7     3 of the License, or (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17 
18 #include "dirnode.h"
19 
20 float gGourceMinDirSize   = 15.0;
21 
22 float gGourceForceGravity = 10.0;
23 float gGourceDirPadding   = 1.5;
24 
25 bool  gGourceNodeDebug    = false;
26 bool  gGourceGravity      = true;
27 
28 //debugging
29 int  gGourceDirNodeInnerLoops = 0;
30 int  gGourceFileInnerLoops = 0;
31 
32 std::map<std::string, RDirNode*> gGourceDirMap;
33 
RDirNode(RDirNode * parent,const std::string & abspath)34 RDirNode::RDirNode(RDirNode* parent, const std::string & abspath) {
35 
36     changePath(abspath);
37 
38     parent = 0;
39     setParent(parent);
40 
41     accel = spos = prev_accel = vel = vec2(0.0f);
42 
43     //NOTE: parent is always being set to 0 so this never gets called ...
44 
45     //figure out starting position
46     if(parent !=0) {
47         vec2 parentPos = parent->getPos();
48         vec2 offset;
49 
50         pos = parentPos;
51     } else {
52         pos = vec2(0.0f, 0.0f);
53     }
54 
55     float padded_file_radius  = gGourceFileDiameter * 0.5;
56 
57     file_area  = padded_file_radius * padded_file_radius * PI;
58 
59     visible_count = 0;
60 
61     visible = false;
62     position_initialized = false;
63 
64     since_node_visible = 0.0;
65     since_last_file_change = 0.0;
66     since_last_node_change = 0.0;
67 
68     calcRadius();
69     calcColour();
70 }
71 
changePath(const std::string & abspath)72 void RDirNode::changePath(const std::string & abspath) {
73     //fix up path
74 
75     gGourceDirMap.erase(this->abspath);
76     this->abspath = abspath;
77 
78     if(abspath.empty() || abspath[abspath.size()-1] != '/') {
79         this->abspath += std::string("/");
80     }
81 
82     //debugLog("new dirnode %s\n", abspath.c_str());
83 
84     gGourceDirMap[this->abspath] = this;
85 }
86 
~RDirNode()87 RDirNode::~RDirNode() {
88     for(std::list<RDirNode*>::iterator it = children.begin(); it != children.end(); it++) {
89         delete (*it);
90     }
91 
92     gGourceDirMap.erase(abspath);
93 }
94 
getTokenOffset() const95 int RDirNode::getTokenOffset() const{
96     return path_token_offset;
97 }
98 
fileUpdated(bool userInitiated)99 void RDirNode::fileUpdated(bool userInitiated) {
100     calcRadius();
101 
102     since_last_file_change = 0.0;
103 
104     nodeUpdated(userInitiated);
105 }
106 
nodeUpdated(bool userInitiated)107 void RDirNode::nodeUpdated(bool userInitiated) {
108     if(userInitiated) since_last_node_change = 0.0;
109 
110     calcRadius();
111     updateFilePositions();
112     if(visible && noDirs() && noFiles()) visible = false;
113     if(parent !=0) parent->nodeUpdated(true);
114 }
115 
rotate(float s,float c)116 void RDirNode::rotate(float s, float c) {
117     pos  = rotate_vec2(pos,  s, c);
118     spos = rotate_vec2(spos, s, c);
119 
120     for(std::list<RDirNode*>::iterator it = children.begin(); it != children.end(); it++) {
121         RDirNode* child = (*it);
122         child->rotate(s, c);
123     }
124 }
125 
rotate(float s,float c,const vec2 & centre)126 void RDirNode::rotate(float s, float c, const vec2& centre) {
127 
128     pos  = rotate_vec2(pos - centre,  s, c) + centre;
129     spos = rotate_vec2(spos - centre, s, c) + centre;
130 
131     for(std::list<RDirNode*>::iterator it = children.begin(); it != children.end(); it++) {
132         RDirNode* child = (*it);
133         child->rotate(s, c, centre);
134     }
135 }
136 
setPos(const vec2 & pos)137 void RDirNode::setPos(const vec2 & pos) {
138     this->pos = pos;
139 }
140 
141 //returns true if supplied path prefixes the nodes path
prefixedBy(const std::string & path) const142 bool RDirNode::prefixedBy(const std::string & path) const {
143     if(path.empty()) return false;
144 
145     if(path[path.size()-1] != '/')
146         return abspath.find(path + std::string("/")) == 0;
147     else
148         return abspath.find(path) == 0;
149 }
150 
getPath() const151 const std::string & RDirNode::getPath() const{
152     return abspath;
153 }
154 
getParent() const155 RDirNode* RDirNode::getParent() const{
156     return parent;
157 }
158 
159 
isDir(const std::string & path) const160 bool RDirNode::isDir(const std::string& path) const {
161 
162     if(prefixedBy(path)) return true;
163 
164     if(path.find(abspath) != 0) return false;
165 
166     for(std::list<RDirNode*>::const_iterator it = children.begin(); it != children.end(); it++) {
167         if((*it)->isDir(path)) return true;
168     }
169 
170     return false;
171 }
172 
173 //finds directories closest to the root directory prefixed by path (eg foo/ may match just foo/ or could also match foo/bar1, foo/bar2, ... if foo/ doesn't exist).
findDirs(const std::string & path,std::list<RDirNode * > & dirs)174 void RDirNode::findDirs(const std::string& path, std::list<RDirNode*>& dirs) {
175 
176     if(prefixedBy(path)) {
177         dirs.push_back(this);
178         return;
179     }
180 
181     for(std::list<RDirNode*>::const_iterator it = children.begin(); it != children.end(); it++) {
182         (*it)->findDirs(path, dirs);
183     }
184 }
185 
getFilesRecursive(std::list<RFile * > & files) const186 void RDirNode::getFilesRecursive(std::list<RFile*>& files) const {
187 
188     //add this dirs files
189     files.insert(files.begin(), this->files.begin(), this->files.end());
190 
191     for(std::list<RDirNode*>::const_iterator it = children.begin(); it != children.end(); it++) {
192         (*it)->getFilesRecursive(files);
193     }
194 }
195 
getDepth() const196 int RDirNode::getDepth() const{
197     return depth;
198 }
199 
adjustPath()200 void RDirNode::adjustPath() {
201 
202     //update display path
203     int parent_token_offset = 0;
204 
205     path_token_offset = abspath.size();
206 
207     if(parent != 0) {
208         parent_token_offset  = parent->getTokenOffset();
209 
210         //debugLog("abspath.substr arguments: %d %d %s size = %d\n", parent_token_offset, abspath.size()-parent_token_offset-1, abspath.c_str(), abspath.size());
211 
212         path_token        = abspath.substr(parent_token_offset, abspath.size()-parent_token_offset-1);
213         path_token_offset = abspath.size();
214 
215         //debugLog("new token %s\n", path_token.c_str());
216     }
217 }
218 
setParent(RDirNode * parent)219 void RDirNode::setParent(RDirNode* parent) {
220     if(parent != 0 && this->parent == parent) return;
221 
222     this->parent = parent;
223 
224     adjustPath();
225     adjustDepth();
226 }
227 
adjustDepth()228 void RDirNode::adjustDepth() {
229 
230     if(parent == 0) {
231         depth = 1;
232     } else {
233         depth = parent->getDepth() + 1;
234     }
235 
236     for(RDirNode* child : children) {
237         child->adjustDepth();
238     }
239 }
240 
addNode(RDirNode * node)241 void RDirNode::addNode(RDirNode* node) {
242     // does this node prefix any other nodes, if so, add them to it
243 
244     std::vector<RDirNode*> matches;
245     std::string path = node->getPath();
246 
247     //debugLog("adding node %s to %s\n", path.c_str(), abspath.c_str());
248 
249     for(std::list<RDirNode*>::iterator it = children.begin(); it != children.end(); ) {
250         RDirNode* child = (*it);
251 
252         if(child->prefixedBy(path)) {
253             it = children.erase(it);
254             node->addNode(child);
255         } else {
256             it++;
257         }
258     }
259 
260     // add to this node
261     children.push_back(node);
262     node->setParent(this);
263 
264     //debugLog("added node %s to %s\n", node->getPath().c_str(), getPath().c_str());
265 
266     nodeUpdated(false);
267 }
268 
getRoot()269 RDirNode* RDirNode::getRoot() {
270     if(parent==0) return this;
271     return parent->getRoot();
272 }
273 
274 // note - you still need to delete the file yourself
removeFile(RFile * f)275 bool RDirNode::removeFile(RFile* f) {
276     //doesnt match this path at all
277     if(f->path.find(abspath) != 0) {
278         return false;
279     }
280 
281     //is this dir - add to this node
282     if(f->path.compare(abspath) == 0) {
283 
284         for(std::list<RFile*>::iterator it = files.begin(); it != files.end(); it++) {
285             if((*it)==f) {
286                 files.erase(it);
287                 if(!f->isHidden()) visible_count--;
288 
289                 fileUpdated(false);
290 
291                 return true;
292             }
293         }
294 
295         return false;
296     }
297 
298     //does this belong to one of the children ?
299     for(std::list<RDirNode*>::iterator it = children.begin(); it != children.end(); it++) {
300         RDirNode* node = (*it);
301         bool removed = node->removeFile(f);
302 
303         if(removed) {
304             //fprintf(stderr, "%s file removed from a child. child file count=%d, dir count =%d\n", getPath().c_str(), node->fileCount(), node->dirCount());
305             //node->printFiles();
306 
307             //node is now empty, reap!
308             if(node->noFiles() && node->noDirs()) {
309                 children.erase(it);
310                 //fprintf(stderr, "deleting node %s from %s\n", node->getPath().c_str(), getPath().c_str());
311                 delete node;
312                 nodeUpdated(false);
313             }
314 
315             return true;
316         }
317     }
318 
319     return false;
320 }
321 
322 
printFiles()323 void RDirNode::printFiles() {
324     for(std::list<RFile*>::iterator it = files.begin(); it != files.end(); it++) {
325         RFile* file = (*it);
326         fprintf(stderr, "%s: %s %s\n", getPath().c_str(), file->fullpath.c_str() , file->isHidden() ? "hidden " : "");
327     }
328 }
329 
330 
addVisible()331 void RDirNode::addVisible() {
332     visible_count++;
333     visible = true;
334 }
335 
isVisible()336 bool RDirNode::isVisible() {
337 
338     if(visible) return true;
339 
340     for(std::list<RDirNode*>::const_iterator it = children.begin(); it != children.end(); it++) {
341         if((*it)->isVisible()) {
342             visible = true;
343             return true;
344         }
345     }
346 
347     return false;
348 }
349 
visibleFileCount() const350 int RDirNode::visibleFileCount() const{
351     return visible_count;
352 }
353 
fileCount() const354 int RDirNode::fileCount() const{
355     return files.size();
356 }
357 
noFiles() const358 bool RDirNode::noFiles() const{
359     return files.empty();
360 }
361 
commonPathPrefix(const std::string & str) const362 std::string RDirNode::commonPathPrefix(const std::string & str) const{
363     size_t c = 0;
364     int slash = -1;
365 
366     while(c<abspath.size() && c<str.size() && abspath[c] == str[c]) {
367         if(abspath[c] == '/') {
368             slash = c;
369         }
370         c++;
371     }
372 
373     if(slash==-1) return "";
374     return str.substr(0,slash+1);
375 }
376 
addFile(RFile * f)377 bool RDirNode::addFile(RFile* f) {
378 
379     //doesnt match this path at all
380     if(f->path.find(abspath) != 0) {
381 
382         if(parent != 0) return false;
383 
384         //if this is the root node (ie no parent), we fork it
385         //if we encounter a file with a non matching path to the
386         //current root path. the calling process then checks if
387         //the root now has a parent node, and changes the pointer.
388 
389         RDirNode* newparent;
390 
391         std::string common = commonPathPrefix(f->path);
392         if(common.size()==0) common = "/";
393 
394         newparent = new RDirNode(0, common);
395         newparent->addNode(this);
396         return newparent->addFile(f);
397     }
398 
399     //simply change path of node and add this to it
400     if(   parent==0 && abspath == "/"
401        && f->path.compare(abspath) != 0 && noFiles() && noDirs()) {
402         debugLog("modifying root path to %s", f->path.c_str());
403         changePath(f->path);
404     }
405 
406     //is this dir - add to this node
407     if(f->path.compare(abspath) == 0) {
408         //debugLog("addFile %s to %s\n", f->fullpath.c_str(), abspath.c_str());
409 
410         files.push_back(f);
411         if(!f->isHidden()) visible_count++;
412         f->setDir(this);
413 
414         fileUpdated(false);
415 
416         return true;
417     }
418 
419     bool added = false;
420 
421     //does this belong to one of the children ?
422     for(std::list<RDirNode*>::iterator it = children.begin(); it != children.end(); it++) {
423         RDirNode* child =  (*it);
424 
425         added = child->addFile(f);
426 
427         if(added) break;
428     }
429 
430     if(added && parent != 0) return true;
431 
432     //do we have a file in this directory thats fullpath is a prefix of this file, if so
433     //that file is actually a directory - the file should be removed, and a directory with that path added
434     //if this is the root node we do this regardless of if the file was added to a child node
435 
436     for(std::list<RFile*>::const_iterator it = files.begin(); it != files.end(); it++) {
437         RFile* file = (*it);
438 
439         if(f->path.find(file->fullpath) == 0) {
440             //fprintf(stderr, "removing %s as is actually the directory of %s\n", file->fullpath.c_str(), f->fullpath.c_str());
441             file->remove();
442             break;
443         }
444     }
445 
446     if(added) return true;
447 
448     //add new child, add it to that
449     //if commonpath is longer than abspath, add intermediate node, else just add at the files path
450     RDirNode* node = new RDirNode(this, f->path);
451 
452     node->addFile(f);
453 
454     addNode(node);
455 
456     // do we have dir nodes, with a common path element greater than abspath,
457     // if so create another node, and move those nodes there
458 
459      std::string commonpath;
460      vec2 commonPos;
461      for(std::list<RDirNode*>::iterator it = children.begin(); it != children.end(); it++) {
462          RDirNode* child =  (*it);
463 
464          std::string common = child->commonPathPrefix(f->path);
465          if(common.size() > abspath.size() && common != f->path) {
466             commonpath = common;
467             commonPos = child->getPos();
468             break;
469          }
470      }
471 
472     // redistribute to new common node
473     if(commonpath.size() > abspath.size()) {
474         //debugLog("common path %s\n", commonpath.c_str());
475 
476         RDirNode* cnode = new RDirNode(this, commonpath);
477         cnode->setPos(commonPos);
478 
479         for(std::list<RDirNode*>::iterator it = children.begin(); it != children.end();) {
480             RDirNode* child =  (*it);
481 
482             if(child->prefixedBy(commonpath)) {
483                 //debugLog("this path = %s, commonpath = %s, path = %s\n", abspath.c_str(), commonpath.c_str(), child->getPath().c_str());
484                 it = children.erase(it);
485                 cnode->addNode(child);
486                 continue;
487             }
488 
489             it++;
490         }
491 
492         addNode(cnode);
493     }
494 
495     return true;
496 }
497 
getParentRadius() const498 float RDirNode::getParentRadius() const{
499     return parent_radius;
500 }
501 
getRadius() const502 float RDirNode::getRadius() const{
503     return dir_radius;
504 }
505 
getRadiusSqrt() const506 float RDirNode::getRadiusSqrt() const{
507     return dir_radius_sqrt;
508 }
509 
averageFileColour() const510 vec3 RDirNode::averageFileColour() const{
511 
512     vec3 av;
513     int count = 0;
514 
515     for(std::list<RFile*>::const_iterator it = files.begin(); it != files.end(); it++) {
516         RFile* file = (*it);
517 
518         if(file->isHidden()) continue;
519 
520         av += file->getColour();
521 
522         count++;
523     }
524 
525     if(count>0) av *= (1.0f/(float)count);
526 
527     count = 0;
528 
529     for(std::list<RDirNode*>::const_iterator it = children.begin(); it != children.end();it++) {
530             RDirNode* child =  (*it);
531 
532             av += child->averageFileColour();
533             count++;
534     }
535 
536     if(count>0) av *= (1.0f/(float)count);
537 
538     return av;
539 }
540 
getColour() const541 const vec4 & RDirNode::getColour() const{
542     return col;
543 }
544 
calcColour()545 void RDirNode::calcColour() {
546 
547     // make branch brighter if recently accessed
548     float brightness = std::max(0.6f, 1.0f - std::min(1.0f, since_last_node_change / 3.0f));
549 
550     col = vec4(brightness, brightness, brightness, 1.0);
551 
552     int fcount = 0;
553 
554     for(std::list<RFile*>::iterator it = files.begin(); it != files.end(); it++) {
555         RFile* file = (*it);
556 
557         if(file->isHidden()) continue;;
558 
559         vec3 filecol = file->getColour() * brightness;
560         float a       = file->getAlpha();
561 
562         col += vec4(filecol.x, filecol.y, filecol.z, a);
563 
564         fcount++;
565     }
566 
567     this->col /= (float) fcount + 1.0;
568 }
569 
getArea() const570 float RDirNode::getArea() const{
571     return dir_area;
572 }
573 
calcRadius()574 void RDirNode::calcRadius() {
575 
576     float total_file_area = file_area * visible_count;
577 
578     dir_area = total_file_area;
579 
580     //float parent_circ        = 0.0;
581 
582     for(std::list<RDirNode*>::iterator it = children.begin(); it != children.end(); it++) {
583         RDirNode* node = (*it);
584 
585         dir_area += node->getArea();
586     //    parent_circ += node->getRadiusSqrt();
587     }
588 
589     this->dir_radius = std::max(1.0f, (float)sqrt(dir_area)) * gGourceDirPadding;
590     //this->dir_radius_sqrt = sqrt(dir_radius); //dir_radius_sqrt is not used
591 
592 //    this->parent_radius = std::max(1.0, parent_circ / PI);
593     this->parent_radius = std::max(1.0f, (float) sqrt(total_file_area) * gGourceDirPadding);
594 }
595 
distanceToParent() const596 float RDirNode::distanceToParent() const{
597 
598     float posd     = glm::length(parent->getPos() - pos);
599     float distance = posd - (dir_radius + parent->getParentRadius());
600 
601     return distance;
602 }
603 
applyForceDir(RDirNode * node)604 void RDirNode::applyForceDir(RDirNode* node) {
605     if(node == this) return;
606 
607     vec2 dir = node->getPos() - pos;
608 
609     float posd2       = glm::length2(dir);
610     float myradius    = getRadius();
611     float your_radius = node->getRadius();
612 
613     float sumradius = (myradius + your_radius);
614 
615     float distance2 = posd2 - sumradius*sumradius;
616 
617     if(distance2>0.0) return;
618 
619     float posd = sqrt(posd2);
620 
621     float distance = posd - myradius - your_radius;
622 
623     //resolve overlap
624     if(posd < 0.00001) {
625         accel += normalise(vec2( (rand() % 100) - 50, (rand() % 100) - 50));
626         return;
627     }
628 
629     accel += distance * normalise(dir);
630 }
631 
getPos() const632 const vec2 & RDirNode::getPos() const{
633     return pos;
634 }
635 
isParent(RDirNode * node) const636 bool RDirNode::isParent(RDirNode* node) const {
637     if(node==parent) return true;
638     if(parent==0) return false;
639 
640     return parent->isParent(node);
641 }
642 
empty() const643 bool RDirNode::empty() const{
644     return (visible_count==0 && noDirs()) ? true : false;
645 }
646 
applyForces(QuadTree & quadtree)647 void RDirNode::applyForces(QuadTree & quadtree) {
648 
649     //child nodes
650     for(std::list<RDirNode*>::iterator it = children.begin(); it != children.end(); it++) {
651         RDirNode* node = (*it);
652 
653         node->applyForces(quadtree);
654     }
655 
656     if(parent == 0) return;
657 
658     DirForceFunctor dff(this);
659     quadtree.visitItemsInBounds(quadItemBounds, dff);
660     gGourceDirNodeInnerLoops += dff.getLoopCount();
661 
662     //always call on parent no matter how far away
663     applyForceDir(parent);
664 
665     //pull towards parent
666     float parent_dist = distanceToParent();
667 
668     //  * dirs should attract to sit on the radius of the parent dir ie:
669     //    should attract to distance_to_parent * normal_to_parent
670 
671 
672 
673     accel += gGourceForceGravity * parent_dist * normalise(parent->getPos() - pos);
674 
675     //  * dirs should be pushed along the parent_parent to parent normal by a force smaller than the parent radius force
676     RDirNode* pparent = parent->getParent();
677 
678     if(pparent != 0) {
679         vec2 parent_edge = (parent->getPos() - pparent->getPos());
680         vec2 parent_edge_normal = normalise(parent_edge);
681 
682         vec2 dest = (parent->getPos() + (parent->getRadius() + getRadius()) * parent_edge_normal) - pos;
683 
684         accel += dest;
685     }
686 
687     //  * dirs should repulse from other dirs of this parent
688     const std::list<RDirNode*> & siblings = parent->getChildren();
689     if(!siblings.empty()) {
690         vec2 sib_accel;
691 
692         int visible = 1;
693 
694         for(std::list<RDirNode*>::const_iterator it = siblings.begin(); it != siblings.end(); it++) {
695             RDirNode* node = (*it);
696 
697             if(node == this) continue;
698             if(!node->isVisible()) continue;
699 
700             visible++;
701 
702             sib_accel -= normalise(node->getPos() - pos);
703         }
704 
705         //parent circumfrence divided by the number of visible child nodes
706         if(visible>1) {
707             float slice_size = (parent->getRadius() * PI) / (float) (visible+1);
708             sib_accel *= slice_size;
709 
710             accel += sib_accel;
711         }
712     }
713 
714 }
715 
debug(int indent) const716 void RDirNode::debug(int indent) const{
717     std::string indentstr;
718     while(indentstr.size() < indent) indentstr += " ";
719 
720     debugLog("%s%s", indentstr.c_str(), abspath.c_str());
721 
722     for(std::list<RDirNode*>::const_iterator it = children.begin(); it != children.end(); it++) {
723         RDirNode* node = (*it);
724         node->debug(indent+1);
725     }
726 }
727 
totalFileCount() const728 int RDirNode::totalFileCount() const{
729     int total = visibleFileCount();
730 
731     for(std::list<RDirNode*>::const_iterator it = children.begin(); it != children.end(); it++) {
732         RDirNode* node = (*it);
733         total += node->visibleFileCount();
734     }
735 
736     return total;
737 }
738 
totalDirCount() const739 int RDirNode::totalDirCount() const{
740     int total = 1;
741 
742     for(std::list<RDirNode*>::const_iterator it = children.begin(); it != children.end(); it++) {
743         RDirNode* node = (*it);
744         total += node->totalDirCount();
745     }
746 
747     return total;
748 }
749 
dirCount() const750 int RDirNode::dirCount() const{
751     return children.size();
752 }
753 
noDirs() const754 bool RDirNode::noDirs() const{
755     return children.empty();
756 }
757 
getChildren() const758 const std::list<RDirNode*> & RDirNode::getChildren() const{
759     return children;
760 }
761 
updateSplinePoint(float dt)762 void RDirNode::updateSplinePoint(float dt) {
763     if(parent == 0) return;
764 
765     //update the spline point
766     vec2 td = (parent->getPos() - pos) * 0.5f;
767 
768     vec2 mid = pos + td;// - td.perpendicular() * pos.normal();// * 10.0;
769 
770     vec2 delta = (mid - spos);
771 
772     //dont let spos get more than half the length of the distance behind
773     if(glm::length2(delta) > glm::length2(td)) {
774         spos += normalise(delta) * (glm::length(delta) - glm::length(td));
775     }
776 
777     spos += delta * std::min(1.0f, dt * 2.0f);
778 }
779 
setInitialPosition()780 void RDirNode::setInitialPosition() {
781     RDirNode* parentP = parent->getParent();
782 
783     pos = parent->getPos();
784 
785     //offset position by some pseudo-randomness
786     if(parentP != 0) {
787         //pos += ((parent->getPos() - parentP->getPos()).normal() * 2.0 + vec2Hash(abspath)).normal();
788         pos += normalise(normalise(parent->getPos() - parentP->getPos()) * 2.0f + vec2Hash(abspath));
789 
790     }  else {
791         pos += vec2Hash(abspath);
792     }
793 
794     //the spline point
795     spos = pos - (parent->getPos() - pos) * 0.5f;
796     position_initialized=true;
797 }
798 
move(float dt)799 void RDirNode::move(float dt) {
800 
801     //the root node is the centre of the world
802     if(parent == 0) {
803         return;
804     }
805 
806     //initial position
807     if(!empty() && !position_initialized) {
808         setInitialPosition();
809     }
810 
811     pos += accel * dt;
812 
813     if(gGourceSettings.elasticity>0.0) {
814         vec2 diff = (accel - prev_accel);
815 
816         float m = dt * gGourceSettings.elasticity;
817 
818         vec2 accel3 = prev_accel * (1.0f-m) + diff * m;
819         pos += accel3;
820         prev_accel = accel3;
821     }
822 
823     //accel = accel * std::max(0.0f, (1.0f - dt*10.0f));
824     accel = vec2(0.0, 0.0);
825 }
826 
getNodeNormal() const827 const vec2 & RDirNode::getNodeNormal() const{
828     return node_normal;
829 }
830 
calcFileDest(int max_files,int file_no)831 vec2 RDirNode::calcFileDest(int max_files, int file_no) {
832 
833     float arc   = 1.0/(float)max_files;
834 
835     float frac = arc * 0.5 + arc * file_no;
836 
837     vec2 dest = vec2(sinf(frac*PI*2.0), cosf(frac*PI*2.0));
838 
839     return dest;
840 }
841 
updateFilePositions()842 void RDirNode::updateFilePositions() {
843 
844     int max_files = 1;
845     int diameter  = 1;
846     int file_no   = 0;
847     float d = 0.0;
848 
849     int files_left = visible_count;
850 
851     for(std::list<RFile*>::iterator it = files.begin(); it!=files.end(); it++) {
852         RFile* f = *it;
853 
854         if(f->isHidden()) {
855             f->setDest(vec2(0.0,0.0));
856             f->setDistance(0.0f);
857             continue;
858         }
859 
860         vec2 dest = calcFileDest(max_files, file_no);
861 
862         f->setDest(dest);
863         f->setDistance(d);
864 
865         files_left--;
866         file_no++;
867 
868         if(file_no>=max_files) {
869             diameter++;
870             d += gGourceFileDiameter;
871             max_files = (int) std::max(1.0, diameter*PI);
872 
873             if(files_left<max_files) {
874                 max_files = files_left;
875             }
876 
877             file_no=0;
878         }
879     }
880 }
881 
calcEdges()882 void RDirNode::calcEdges() {
883 
884     if(parent != 0) {
885         spline.update(parent->getProjectedPos(), parent->getColour(), projected_pos, col, projected_spos);
886     }
887 
888     for(std::list<RDirNode*>::iterator it = children.begin(); it != children.end(); it++) {
889         RDirNode* child = *it;
890 
891         child->calcEdges();
892     }
893 }
894 
logic(float dt)895 void RDirNode::logic(float dt) {
896 
897     //move
898     move(dt);
899     updateSplinePoint(dt);
900 
901     //update node normal
902     if(parent != 0) {
903         node_normal = normalise(pos - parent->getPos());
904     }
905 
906     //update files
907      for(std::list<RFile*>::iterator it = files.begin(); it!=files.end(); it++) {
908          RFile* f = *it;
909 
910          f->logic(dt);
911      }
912 
913     //update child nodes
914     for(std::list<RDirNode*>::iterator it = children.begin(); it != children.end(); it++) {
915         RDirNode* node = (*it);
916 
917         node->logic(dt);
918     }
919 
920     //update colour
921     calcColour();
922 
923     //update tickers
924     if(visible) since_node_visible += dt;
925 
926     since_last_file_change += dt;
927     since_last_node_change += dt;
928 }
929 
drawDirName(FXFont & dirfont) const930 void RDirNode::drawDirName(FXFont& dirfont) const{
931     if(parent==0) return;
932     if(gGourceSettings.hide_dirnames) return;
933     if(gGourceSettings.dir_name_depth > 0 && gGourceSettings.dir_name_depth < (depth-1)) return;
934 
935     if(!gGourceSettings.highlight_dirs && since_last_node_change > 5.0) return;
936 
937     float alpha = gGourceSettings.highlight_dirs ? 1.0 : std::max(0.0f, 5.0f - since_last_node_change) / 5.0f;
938 
939     vec2 label_pos = spline.getLabelPos();
940 
941     dirfont.setAlpha(alpha);
942     dirfont.draw(label_pos.x, label_pos.y, path_token);
943 }
944 
calcScreenPos(GLint * viewport,GLdouble * modelview,GLdouble * projection)945 void RDirNode::calcScreenPos(GLint* viewport, GLdouble* modelview, GLdouble* projection) {
946 
947     static GLdouble screen_x, screen_y, screen_z;
948 
949     gluProject( pos.x, pos.y, 0.0f, modelview, projection, viewport, &screen_x, &screen_y, &screen_z);
950     screen_y = (float)viewport[3] - screen_y;
951     projected_pos.x = screen_x;
952     projected_pos.y = screen_y;
953 
954     gluProject( spos.x, spos.y, 0.0f, modelview, projection, viewport, &screen_x, &screen_y, &screen_z);
955     screen_y = (float)viewport[3] - screen_y;
956     projected_spos.x = screen_x;
957     projected_spos.y = screen_y;
958 
959     static vec2 selected_offset(5.5f, -2.0f);
960     static vec2 unselected_offset(5.5f, -1.0f);
961 
962     if(!gGourceSettings.hide_filenames) {
963 
964         //first pass - calculate positions of names
965         for(std::list<RFile*>::const_iterator it = files.begin(); it!=files.end(); it++) {
966             RFile* f = *it;
967             f->calcScreenPos(viewport, modelview, projection);
968         }
969     }
970 
971     for(std::list<RDirNode*>::const_iterator it = children.begin(); it != children.end(); it++) {
972         RDirNode* node = (*it);
973         node->calcScreenPos(viewport, modelview, projection);
974     }
975 }
976 
drawNames(FXFont & dirfont)977 void RDirNode::drawNames(FXFont& dirfont) {
978 
979     if(!gGourceSettings.hide_dirnames && isVisible()) {
980         drawDirName(dirfont);
981     }
982 
983     if(!gGourceSettings.hide_filenames) {
984 
985         if(!(gGourceSettings.hide_filenames || gGourceSettings.hide_files) && in_frustum) {
986             for(std::list<RFile*>::const_iterator it = files.begin(); it!=files.end(); it++) {
987                 RFile* f = *it;
988                 if(!f->isSelected()) f->drawName();
989             }
990         }
991 
992     }
993 
994     for(std::list<RDirNode*>::const_iterator it = children.begin(); it != children.end(); it++) {
995         RDirNode* node = (*it);
996         node->drawNames(dirfont);
997     }
998 }
999 
checkFrustum(const Frustum & frustum)1000 void RDirNode::checkFrustum(const Frustum& frustum) {
1001 
1002     in_frustum = frustum.intersects(quadItemBounds);
1003 
1004     for(std::list<RDirNode*>::const_iterator it = children.begin(); it != children.end(); it++) {
1005         RDirNode* node = (*it);
1006         node->checkFrustum(frustum);
1007     }
1008 }
1009 
drawShadows(float dt) const1010 void RDirNode::drawShadows(float dt) const{
1011 
1012     if(in_frustum) {
1013 
1014         glPushMatrix();
1015         glTranslatef(pos.x, pos.y, 0.0);
1016 
1017         //draw files
1018         for(std::list<RFile*>::const_iterator it = files.begin(); it!=files.end(); it++) {
1019             RFile* f = *it;
1020             if(f->isHidden()) continue;
1021 
1022             f->drawShadow(dt);
1023         }
1024 
1025         glPopMatrix();
1026     }
1027 
1028     for(std::list<RDirNode*>::const_iterator it = children.begin(); it != children.end(); it++) {
1029         RDirNode* node = (*it);
1030         node->drawShadows(dt);
1031     }
1032 }
1033 
updateFilesVBO(quadbuf & buffer,float dt) const1034 void RDirNode::updateFilesVBO(quadbuf& buffer, float dt) const{
1035 
1036     if(in_frustum) {
1037 
1038         for(std::list<RFile*>::const_iterator it = files.begin(); it!=files.end(); it++) {
1039             RFile* f = *it;
1040 
1041             if(f->isHidden()) continue;
1042 
1043             vec3 col   = f->getColour();
1044             float alpha = f->getAlpha();
1045 
1046             buffer.add(f->graphic->textureid, f->getAbsolutePos() - f->dims*0.5f, f->dims, vec4(col.x, col.y, col.z, alpha));
1047         }
1048     }
1049 
1050     for(std::list<RDirNode*>::const_iterator it = children.begin(); it != children.end(); it++) {
1051         RDirNode* node = (*it);
1052         node->updateFilesVBO(buffer,dt);
1053     }
1054 }
1055 
updateBloomVBO(bloombuf & buffer,float dt)1056 void RDirNode::updateBloomVBO(bloombuf& buffer, float dt) {
1057 
1058     if(in_frustum && isVisible()) {
1059 
1060         float bloom_radius   = dir_radius * 2.0 * gGourceSettings.bloom_multiplier;
1061         float bloom_diameter = bloom_radius * 2.0;
1062         vec4 bloom_col      = col * gGourceSettings.bloom_intensity;
1063 
1064         vec4 bloom_texcoords(bloom_radius, pos.x, pos.y, 0.0f);
1065 
1066         vec2 bloom_dims(bloom_diameter, bloom_diameter);
1067 
1068         buffer.add(0, pos - bloom_dims*0.5f,bloom_dims, vec4(bloom_col.x, bloom_col.y, bloom_col.z, 1.0f), bloom_texcoords);
1069     }
1070 
1071     for(std::list<RDirNode*>::const_iterator it = children.begin(); it != children.end(); it++) {
1072         RDirNode* node = (*it);
1073         node->updateBloomVBO(buffer,dt);
1074     }
1075 }
1076 
drawFiles(float dt) const1077 void RDirNode::drawFiles(float dt) const{
1078 
1079     if(in_frustum) {
1080         glPushMatrix();
1081             glTranslatef(pos.x, pos.y, 0.0);
1082 
1083             //draw files
1084 
1085             for(std::list<RFile*>::const_iterator it = files.begin(); it!=files.end(); it++) {
1086                 RFile* f = *it;
1087                 if(f->isHidden()) continue;
1088 
1089                 f->draw(dt);
1090             }
1091 
1092         glPopMatrix();
1093     }
1094 
1095     for(std::list<RDirNode*>::const_iterator it = children.begin(); it != children.end(); it++) {
1096         RDirNode* node = (*it);
1097         node->drawFiles(dt);
1098     }
1099 }
1100 
getSPos() const1101 const vec2 & RDirNode::getSPos() const{
1102     return projected_spos;
1103 }
1104 
getProjectedPos() const1105 const vec2 & RDirNode::getProjectedPos() const{
1106     return projected_pos;
1107 }
1108 
updateEdgeVBO(quadbuf & buffer) const1109 void RDirNode::updateEdgeVBO(quadbuf& buffer) const {
1110 
1111     if(parent!=0 && (!gGourceSettings.hide_root || parent->parent !=0)) spline.drawToVBO(buffer);
1112 
1113     for(std::list<RDirNode*>::const_iterator it = children.begin(); it != children.end(); it++) {
1114         RDirNode* child = (*it);
1115 
1116         if(child->isVisible()) {
1117             child->updateEdgeVBO(buffer);
1118         }
1119     }
1120 }
1121 
drawEdgeShadows() const1122 void RDirNode::drawEdgeShadows() const{
1123 
1124     if(parent!=0 && (!gGourceSettings.hide_root || parent->parent !=0)) spline.drawShadow();
1125 
1126     for(std::list<RDirNode*>::const_iterator it = children.begin(); it != children.end(); it++) {
1127         RDirNode* child = (*it);
1128 
1129         //draw edge - assumes calcEdges() called before hand so spline will exist
1130         if(child->isVisible()) {
1131            child->drawEdgeShadows();
1132         }
1133     }
1134 }
1135 
drawEdges() const1136 void RDirNode::drawEdges() const{
1137 
1138    if(parent!=0 && (!gGourceSettings.hide_root || parent->parent !=0)) spline.draw();
1139 
1140     for(std::list<RDirNode*>::const_iterator it = children.begin(); it != children.end(); it++) {
1141         RDirNode* child = (*it);
1142 
1143         //draw edge - assumes calcEdges() called before hand so spline will exist
1144         if(child->isVisible()) {
1145            child->drawEdges();
1146         }
1147     }
1148 }
1149 
drawBloom(float dt)1150 void RDirNode::drawBloom(float dt){
1151 
1152     if(in_frustum && isVisible()) {
1153 
1154         float bloom_radius = dir_radius * 2.0 * gGourceSettings.bloom_multiplier;
1155 
1156         vec4 bloom_col = col * gGourceSettings.bloom_intensity;
1157 
1158         glColor4f(bloom_col.x, bloom_col.y, bloom_col.z, 1.0);
1159 
1160         glPushMatrix();
1161             glTranslatef(pos.x, pos.y, 0.0);
1162 
1163             glBegin(GL_QUADS);
1164                 glTexCoord2f(1.0f, 1.0f);
1165                 glVertex2f(bloom_radius,bloom_radius);
1166                 glTexCoord2f(1.0f, 0.0f);
1167                 glVertex2f(bloom_radius,-bloom_radius);
1168                 glTexCoord2f(0.0f, 0.0f);
1169                 glVertex2f(-bloom_radius,-bloom_radius);
1170                 glTexCoord2f(0.0f, 1.0f);
1171                 glVertex2f(-bloom_radius,bloom_radius);
1172             glEnd();
1173         glPopMatrix();
1174     }
1175 
1176     for(std::list<RDirNode*>::const_iterator it = children.begin(); it != children.end(); it++) {
1177         RDirNode* node = (*it);
1178         node->drawBloom(dt);
1179     }
1180 }
1181 
updateQuadItemBounds()1182 void RDirNode::updateQuadItemBounds() {
1183     float radius = getRadius();
1184 
1185     vec2 radoffset(radius, radius);
1186 
1187     //set bounds
1188     quadItemBounds.set(pos - radoffset, pos + radoffset);
1189 }
1190 
1191