void Gource::deleteFile(RFile* file) { debugLog("removing file %s\n", file->getFullPath().c_str()); root->removeFile(file); if(hoverFile == file) { hoverFile = 0; } if(selectedFile == file) { selectFile(0); } //remove from any users with actions against this file - wrong way around? meh for(std::map<std::string,RUser*>::iterator it = users.begin(); it!=users.end(); it++) { RUser* user = it->second; user->fileRemoved(file); } files.erase(file->getFullPath()); tagfilemap.erase(file->getTagID()); debugLog("removed file %s\n", file->getFullPath().c_str()); delete file; }
void Gource::interactUsers() { // update quad tree Bounds2D quadtreebounds = user_bounds; quadtreebounds.min -= vec2f(1.0f, 1.0f); quadtreebounds.max += vec2f(1.0f, 1.0f); update_user_tree_time = SDL_GetTicks(); if(userTree != 0) delete userTree; int max_depth = 1; //dont use deep quad tree initially when all the nodes are in one place if(dir_bounds.area() > 10000.0) { max_depth = gGourceMaxQuadTreeDepth; } userTree = new QuadTree(quadtreebounds, max_depth, 1); for(std::map<std::string,RUser*>::iterator it = users.begin(); it!=users.end(); it++) { RUser* user = it->second; user->updateQuadItemBounds(); userTree->addItem(user); } //move users - interact with other users and files for(std::map<std::string,RUser*>::iterator ait = users.begin(); ait!=users.end(); ait++) { RUser* a = ait->second; UserForceFunctor uff(a); userTree->visitItemsInBounds(a->quadItemBounds, uff); gGourceUserInnerLoops += uff.getLoopCount(); a->applyForceToActions(); } update_user_tree_time = SDL_GetTicks() - update_user_tree_time; }
void Gource::updateUsers(float t, float dt) { std::vector<RUser*> inactiveUsers; int idle_users = 0; //recalc the user bounds user_bounds.reset(); // move users for(std::map<std::string,RUser*>::iterator it = users.begin(); it!=users.end(); it++) { RUser* u = it->second; u->logic(t, dt); //deselect user if fading out from inactivity if(u->isFading() && selectedUser == u) { selectUser(0); } if(u->isInactive()) { inactiveUsers.push_back(u); } if(u->isIdle()) { idle_users++; } else { user_bounds.update(u->getPos()); //if nothing is selected, and this user is active and this user is the specified user to follow, select them if(selectedUser == 0 && selectedFile == 0) { for(std::vector<std::string>::iterator ui = follow_users.begin(); ui != follow_users.end(); ui++) { std::string follow = *ui; if(follow.size() && u->getName() == follow) { selectUser(u); } } } } } //nothing is moving so increment idle if(idle_users==users.size()) { idle_time += dt; //if stop_on_idle is set and either no stop condition is set or the condition has been reached, exit if(stop_on_idle && (stop_position == 0.0f && !stop_at_end || stop_position_reached)) appFinished = true; } else { idle_time = 0; } // delete inactive users for(std::vector<RUser*>::iterator it = inactiveUsers.begin(); it != inactiveUsers.end(); it++) { deleteUser(*it); } }
void Gource::updateBounds() { user_bounds.reset(); for(std::map<std::string,RUser*>::iterator it = users.begin(); it!=users.end(); it++) { RUser* u = it->second; if(!u->isIdle()) { user_bounds.update(u->getPos()); } } dir_bounds.reset(); for(std::map<std::string,RDirNode*>::iterator it = gGourceDirMap.begin(); it!=gGourceDirMap.end(); it++) { RDirNode* node = it->second; if(node->isVisible()) { dir_bounds.update(node->getPos()); } } }
void Gource::updateUsers(float t, float dt) { std::vector<RUser*> inactiveUsers; int idle_users = 0; //recalc the user bounds user_bounds.reset(); // move users for(std::map<std::string,RUser*>::iterator it = users.begin(); it!=users.end(); it++) { RUser* u = it->second; u->logic(t, dt); //deselect user if fading out from inactivity if(u->isFading() && selectedUser == u) { selectUser(0); } if(u->isInactive()) { inactiveUsers.push_back(u); } if(u->isIdle()) { idle_users++; } else { user_bounds.update(u->getPos()); //if nothing is selected, and this user is active and this user is the specified user to follow, select them if(selectedUser == 0 && selectedFile == 0) { for(std::vector<std::string>::iterator ui = follow_users.begin(); ui != follow_users.end(); ui++) { std::string follow = *ui; if(follow.size() && u->getName() == follow) { selectUser(u); } } } } } //nothing is moving so increment idle if(idle_users==static_cast<int>(users.size())) { idle_time += dt; } else { idle_time = 0.0f; } // delete inactive users for(std::vector<RUser*>::iterator it = inactiveUsers.begin(); it != inactiveUsers.end(); it++) { deleteUser(*it); } }
void Gource::selectNextUser() { debugLog("selectNextUser()\n"); int currTagId = -1; if(selectedUser != 0) { currTagId = selectedUser->getTagID(); } RUser* newSelectedUser = 0; // find next user after this user for(std::map<int,RUser*>::iterator it = tagusermap.begin(); it != tagusermap.end(); it++) { RUser* user = it->second; if(!user->isInactive() && user->getTagID() > currTagId && user->getAlpha() >= 1.0) { newSelectedUser = user; break; } } // just get first user if(newSelectedUser == 0) { for(std::map<int,RUser*>::iterator it = tagusermap.begin(); it != tagusermap.end(); it++) { RUser* user = it->second; if(!user->isInactive() && user->getAlpha() >= 1.0) { newSelectedUser = user; break; } } } selectUser(newSelectedUser); }
void Gource::processCommit(RCommit& commit, float t) { //find user of this commit or create them RUser* user = 0; //see if user already exists but if not wait until //we actually use one of their files before adding them std::map<std::string, RUser*>::iterator seen_user = users.find(commit.username); if(seen_user != users.end()) user = seen_user->second; //find files of this commit or create it for(std::list<RCommitFile>::iterator it = commit.files.begin(); it != commit.files.end(); it++) { RCommitFile& cf = *it; bool filtered_filename = false; //check filename against filters for(std::vector<Regex*>::iterator ri = filters.begin(); ri != filters.end(); ri++) { Regex* r = *ri; if(r->match(cf.filename)) { filtered_filename = true; break; } } if(filtered_filename) continue; std::map<std::string, RFile*>::iterator seen_file; seen_file = files.find(cf.filename); RFile* file = 0; if(seen_file != files.end()) { file = seen_file->second; } else { //if we already have max files in circulation //we cant add any more if(files.size() >= gGourceMaxFiles) continue; int tagid = tag_seq++; file = new RFile(cf.filename, cf.colour, vec2f(0.0,0.0), tagid); files[cf.filename] = file; tagfilemap[tagid] = file; root->addFile(file); while(root->getParent() != 0) { debugLog("parent changed to %s\n", root->getPath().c_str()); root = root->getParent(); } } //create user if havent yet. do it here to ensure at least one of there files //was added (incase we hit gGourceMaxFiles!) if(user == 0) { vec2f pos; if(dir_bounds.area() > 0) { pos = dir_bounds.centre(); } else { pos = vec2f(0,0); } int tagid = tag_seq++; user = new RUser(commit.username, pos, tagid); users[commit.username] = user; tagusermap[tagid] = user; if(gGourceHighlightAllUsers) { user->setHighlighted(true); } else { // set the highlighted flag if name matches a highlighted user for(std::vector<std::string>::iterator hi = highlight_users.begin(); hi != highlight_users.end(); hi++) { std::string highlight = *hi; if(highlight.size() && user->getName() == highlight) { user->setHighlighted(true); break; } } } debugLog("added user %s, tagid = %d\n", commit.username.c_str(), tagid); } //create action RAction* action = 0; int commitNo = commit_seq++; if(cf.action == "D") { action = new RemoveAction(user, file, t); } else { if(cf.action == "A") { action = new CreateAction(user, file, t); } else { action = new ModifyAction(user, file, t); } } user->addAction(action); } }
void Gource::mousetrace(Frustum& frustum, float dt) { GLuint buffer[512]; GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); glSelectBuffer(512, buffer); glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); (void) glRenderMode(GL_SELECT); glInitNames(); glPushName(0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPickMatrix((GLdouble) mousepos.x, (GLdouble) (viewport[3]-mousepos.y), 1.0f, 1.0f, viewport); gluPerspective(90.0f, (GLfloat)display.width/(GLfloat)display.height, 0.1f, camera.getZFar()); camera.look(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); for(std::map<std::string,RUser*>::iterator it = users.begin(); it!=users.end(); it++) { it->second->drawSimple(dt); } glDisable(GL_TEXTURE_2D); glColor4f(1.0, 1.0, 1.0, 1.0); root->drawSimple(frustum, dt); glMatrixMode(GL_MODELVIEW); mouse_hits = glRenderMode(GL_RENDER); RFile* fileSelection = 0; RUser* userSelection = 0; if (mouse_hits > 0) { int choice = buffer[3]; GLuint depth = buffer[1]; for (int loop = 1; loop < mouse_hits; loop++) { if (buffer[loop*4+1] < depth) { choice = buffer[loop*4+3]; depth = buffer[loop*4+1]; } } if(choice != 0) { selectionDepth = depth; std::map<int, RFile*>::iterator filetest; std::map<int, RUser*>::iterator usertest; if((filetest = tagfilemap.find(choice)) != tagfilemap.end()) { fileSelection = filetest->second; } else if((usertest = tagusermap.find(choice)) != tagusermap.end()) { userSelection = usertest->second; } } } glDisable(GL_DEPTH_TEST); // is over a file if(fileSelection != 0) { // un hover a user if(hoverUser != 0) { hoverUser->setMouseOver(false); hoverUser = 0; } if(fileSelection != hoverFile) { //deselect previous selection if(hoverFile !=0) hoverFile->setMouseOver(false); //select new fileSelection->setMouseOver(true); hoverFile = fileSelection; } // is over a user } else if(userSelection != 0) { // un hover a file if(hoverFile != 0) { hoverFile->setMouseOver(false); hoverFile = 0; } if(userSelection != hoverUser) { //deselect previous selection if(hoverUser !=0) hoverUser->setMouseOver(false); //select new userSelection->setMouseOver(true); hoverUser = userSelection; } } else { if(hoverFile!=0) hoverFile->setMouseOver(false); if(hoverUser!=0) hoverUser->setMouseOver(false); hoverFile=0; hoverUser=0; } if(mouseclicked) { if(hoverUser!=0) selectUser(hoverUser); else if(hoverFile!=0) selectFile(hoverFile); else selectUser(0); } }
void Gource::interactUsers() { // update quad tree Bounds2D quadtreebounds = user_bounds; quadtreebounds.min -= vec2f(1.0f, 1.0f); quadtreebounds.max += vec2f(1.0f, 1.0f); update_user_tree_time = SDL_GetTicks(); if(userTree != 0) delete userTree; int max_depth = 1; //dont use deep quad tree initially when all the nodes are in one place if(dir_bounds.area() > 10000.0) { max_depth = 6; } userTree = new QuadTree(quadtreebounds, max_depth, 1); for(std::map<std::string,RUser*>::iterator it = users.begin(); it!=users.end(); it++) { RUser* user = it->second; user->updateQuadItemBounds(); userTree->addItem(user); } //move users - interact with other users and files for(std::map<std::string,RUser*>::iterator ait = users.begin(); ait!=users.end(); ait++) { RUser* a = ait->second; std::set<int> seen; std::set<int>::iterator seentest; std::vector<QuadItem*> inbounds; int found = userTree->getItemsInBounds(inbounds, a->quadItemBounds); for(std::vector<QuadItem*>::iterator it = inbounds.begin(); it != inbounds.end(); it++) { RUser* b = (RUser*) (*it); if(b==a) continue; if((seentest = seen.find(b->getTagID())) != seen.end()) { continue; } seen.insert(b->getTagID()); a->applyForceUser(b); gGourceUserInnerLoops++; } a->applyForceToActions(); } update_user_tree_time = SDL_GetTicks() - update_user_tree_time; }
void Gource::logic(float t, float dt) { if(draw_loading) return; if(message_timer>0.0f) message_timer -= dt; if(splash>0.0f) splash -= dt; //init log file if(commitlog == 0) { try { commitlog = determineFormat(logfile); } catch(SeekLogException& exception) { throw SDLAppException("unable to read log file"); } if(commitlog == 0) { //if not in a git dir and no log file, show help if(logfile.size() == 0 || logfile == ".") { SDL_Quit(); SDLAppException exception(""); exception.setShowHelp(true); throw exception; } else if(SDLAppDirExists(logfile)) { throw SDLAppException("directory not supported"); } else { throw SDLAppException("unsupported log format (you may need to regenerate your log file)"); } } if(gGourceSettings.start_position>0.0) { seekTo(gGourceSettings.start_position); } } slider.logic(dt); //apply rotation if(rotate_angle != 0.0f) { float s = sinf(rotate_angle); float c = cosf(rotate_angle); root->rotate(s, c); for(std::map<std::string,RUser*>::iterator it = users.begin(); it!=users.end(); it++) { RUser* user = it->second; vec2f userpos = user->getPos(); user->setPos(userpos.rotate(s, c)); } rotate_angle = 0.0f; } //still want to update camera while paused if(paused) { updateBounds(); updateQuadTree(); updateCamera(dt); return; } // get more entries if(commitqueue.size() == 0) { readLog(); } //loop in attempt to find commits if(commitqueue.size()==0 && commitlog->isSeekable() && gGourceSettings.loop) { first_read=true; seekTo(0.0); readLog(); } if(currtime==0 && commitqueue.size()) { currtime = lasttime = commitqueue[0].timestamp; subseconds = 0.0; } //set current time float time_inc = (dt * 86400.0 * gGourceSettings.days_per_second); int seconds = (int) time_inc; subseconds += time_inc - ((float) seconds); if(subseconds >= 1.0) { currtime += (int) subseconds; subseconds -= (int) subseconds; } currtime += seconds; // delete files for(std::vector<RFile*>::iterator it = gGourceRemovedFiles.begin(); it != gGourceRemovedFiles.end(); it++) { deleteFile(*it); } gGourceRemovedFiles.clear(); //add commits up until the current time while(commitqueue.size() > 0) { RCommit commit = commitqueue[0]; //auto skip ahead, unless stop_position_reached if(gGourceSettings.auto_skip_seconds>=0.0 && idle_time >= gGourceSettings.auto_skip_seconds && !stop_position_reached) { currtime = lasttime = commit.timestamp; idle_time = 0.0; } if(commit.timestamp > currtime) break; processCommit(commit, t); currtime = lasttime = commit.timestamp; subseconds = 0.0; commitqueue.pop_front(); } //reset loop counters gGourceUserInnerLoops = 0; gGourceDirNodeInnerLoops = 0; gGourceFileInnerLoops = 0; interactUsers(); updateUsers(t, dt); updateQuadTree(); updateBounds(); updateDirs(dt); updateCamera(dt); updateTime(commitqueue.size() > 0 ? currtime : lasttime); }