diff --git a/README.md b/README.md index b1e5c2ec..326b5b06 100644 --- a/README.md +++ b/README.md @@ -283,6 +283,29 @@ options: --user-scale SCALE Change scale of user avatars. + --user-scale-by-commits + Scale user avatar size based on their commit count. + + --persist-user-scale + Remember user scale when they disappear and reappear. + + --commit-scale-factor FLOAT + Controls the intensity of commit-based scaling (default: 0.5). + + --commit-scale-type TYPE + Algorithm for commit-based scaling (default: log). + + log - Logarithmic scaling, tapers off for high values. + linear - Grows proportionally with commit count. + sqrt - Middle ground between log and linear. + exp - Aggressive growth + + --user-min-scale FLOAT + Minimum scale for commit-based user scaling (default: 1.0). + + --user-max-scale FLOAT + Maximum scale for commit-based user scaling (default: 3.0). + --camera-mode MODE Camera mode (overview,track). diff --git a/src/gource.cpp b/src/gource.cpp index cf86c4f9..d5dccba7 100644 --- a/src/gource.cpp +++ b/src/gource.cpp @@ -1032,6 +1032,14 @@ RUser* Gource::addUser(const std::string& username) { RUser* user = new RUser(username, pos, tagid); + // Restore persisted commit count + if(gGourceSettings.persist_user_scale && gGourceSettings.user_scale_by_commits) { + std::map::iterator it = user_commit_counts.find(username); + if(it != user_commit_counts.end()) { + user->setTotalCommitCount(it->second); + } + } + users[username] = user; tagusermap[tagid] = user; @@ -1050,6 +1058,11 @@ void Gource::deleteUser(RUser* user) { selectUser(0); } + // Save commit count for persistence + if(gGourceSettings.persist_user_scale && gGourceSettings.user_scale_by_commits) { + user_commit_counts[user->getName()] = user->getTotalCommitCount(); + } + users.erase(user->getName()); tagusermap.erase(user->getTagID()); diff --git a/src/gource.h b/src/gource.h index 338e7ac5..6dc44e97 100644 --- a/src/gource.h +++ b/src/gource.h @@ -182,6 +182,7 @@ class Gource : public SDLApp { std::map users; std::map files; std::map tagusermap; + std::map user_commit_counts; std::list captions; std::list active_captions; diff --git a/src/gource_settings.cpp b/src/gource_settings.cpp index 76ec9479..914f84ff 100644 --- a/src/gource_settings.cpp +++ b/src/gource_settings.cpp @@ -161,7 +161,13 @@ if(extended_help) { printf(" --user-friction SECONDS Change the rate users slow down (default: 0.67)\n"); printf(" --user-scale SCALE Change scale of users (default: 1.0)\n"); - printf(" --max-user-speed UNITS Speed users can travel per second (default: 500)\n\n"); + printf(" --max-user-speed UNITS Speed users can travel per second (default: 500)\n"); + printf(" --user-scale-by-commits Scale user size based on commit count\n"); + printf(" --persist-user-scale Remember user scale when they reappear\n"); + printf(" --commit-scale-factor F Commit scaling intensity (default: 0.5)\n"); + printf(" --commit-scale-type TYPE Scaling algorithm: log, linear, sqrt, exp (default: log)\n"); + printf(" --user-min-scale SCALE Minimum user scale (default: 1.0)\n"); + printf(" --user-max-scale SCALE Maximum user scale (default: 3.0)\n\n"); printf(" --follow-user USER Camera will automatically follow this user\n"); printf(" --highlight-dirs Highlight the names of all directories\n"); @@ -319,6 +325,13 @@ GourceSettings::GourceSettings() { arg_types["follow-user"] = "multi-value"; arg_types["highlight-user"] = "multi-value"; + arg_types["user-scale-by-commits"] = "bool"; + arg_types["persist-user-scale"] = "bool"; + arg_types["commit-scale-factor"] = "float"; + arg_types["commit-scale-type"] = "string"; + arg_types["user-min-scale"] = "float"; + arg_types["user-max-scale"] = "float"; + arg_types["log-level"] = "string"; arg_types["background-image"] = "string"; arg_types["logo"] = "string"; @@ -472,6 +485,13 @@ void GourceSettings::setGourceDefaults() { user_friction = 1.0f; user_scale = 1.0f; + user_scale_by_commits = false; + commit_scale_factor = 0.5f; + user_min_scale = 1.0f; + user_max_scale = 3.0f; + persist_user_scale = false; + commit_scale_type = COMMIT_SCALE_LOG; + follow_users.clear(); highlight_users.clear(); highlight_all_users = false; @@ -1432,6 +1452,70 @@ void GourceSettings::importGourceSettings(ConfFile& conffile, ConfSection* gourc } } + if(gource_settings->getBool("user-scale-by-commits")) { + user_scale_by_commits = true; + } + + if(gource_settings->getBool("persist-user-scale")) { + persist_user_scale = true; + } + + if((entry = gource_settings->getEntry("commit-scale-factor")) != 0) { + + if(!entry->hasValue()) conffile.entryException(entry, "specify commit-scale-factor (float)"); + + commit_scale_factor = entry->getFloat(); + + if(commit_scale_factor<=0.0 || commit_scale_factor>10.0) { + conffile.invalidValueException(entry); + } + } + + if((entry = gource_settings->getEntry("commit-scale-type")) != 0) { + + if(!entry->hasValue()) conffile.entryException(entry, "specify commit-scale-type (log,linear,sqrt,exp)"); + + std::string scale_type_str = entry->getString(); + + if(scale_type_str == "log") { + commit_scale_type = COMMIT_SCALE_LOG; + } else if(scale_type_str == "linear") { + commit_scale_type = COMMIT_SCALE_LINEAR; + } else if(scale_type_str == "sqrt") { + commit_scale_type = COMMIT_SCALE_SQRT; + } else if(scale_type_str == "exp") { + commit_scale_type = COMMIT_SCALE_EXP; + } else { + conffile.invalidValueException(entry); + } + } + + if((entry = gource_settings->getEntry("user-min-scale")) != 0) { + + if(!entry->hasValue()) conffile.entryException(entry, "specify user-min-scale (float)"); + + user_min_scale = entry->getFloat(); + + if(user_min_scale<=0.0 || user_min_scale>100.0) { + conffile.invalidValueException(entry); + } + } + + if((entry = gource_settings->getEntry("user-max-scale")) != 0) { + + if(!entry->hasValue()) conffile.entryException(entry, "specify user-max-scale (float)"); + + user_max_scale = entry->getFloat(); + + if(user_max_scale<=0.0 || user_max_scale>1000.0) { + conffile.invalidValueException(entry); + } + } + + if(user_min_scale > user_max_scale) { + throw ConfFileException("user-min-scale cannot be greater than user-max-scale", "", 0); + } + if((entry = gource_settings->getEntry("max-user-speed")) != 0) { if(!entry->hasValue()) conffile.entryException(entry, "specify max-user-speed (units)"); diff --git a/src/gource_settings.h b/src/gource_settings.h index 1975ec59..65628ae5 100644 --- a/src/gource_settings.h +++ b/src/gource_settings.h @@ -24,6 +24,14 @@ #include "core/settings.h" #include "core/regex.h" +// Commit-based user scaling algorithms +enum CommitScaleType { + COMMIT_SCALE_LOG, + COMMIT_SCALE_LINEAR, + COMMIT_SCALE_SQRT, + COMMIT_SCALE_EXP +}; + class GourceSettings : public SDLAppSettings { protected: void commandLineOption(const std::string& name, const std::string& value); @@ -136,6 +144,13 @@ class GourceSettings : public SDLAppSettings { float user_scale; float time_scale; + bool user_scale_by_commits; + float commit_scale_factor; + float user_min_scale; + float user_max_scale; + bool persist_user_scale; + CommitScaleType commit_scale_type; + bool highlight_dirs; bool highlight_all_users; diff --git a/src/user.cpp b/src/user.cpp index 9611f488..912dd9e3 100644 --- a/src/user.cpp +++ b/src/user.cpp @@ -45,6 +45,7 @@ RUser::RUser(const std::string& name, vec2 pos, int tagid) : Pawn(name,pos,tagid min_units_ps = 100.0; actionCount = activeCount = 0; + totalCommitCount = 0; } void RUser::addAction(RAction* action) { @@ -56,6 +57,9 @@ void RUser::addAction(RAction* action) { actions.push_back(action); actionCount++; + totalCommitCount++; + + updateSizeByCommits(); } // remove references to this file @@ -228,6 +232,52 @@ int RUser::getPendingActionCount() { return actionCount; } +size_t RUser::getTotalCommitCount() const { + return totalCommitCount; +} + +void RUser::setTotalCommitCount(size_t count) { + totalCommitCount = count; + updateSizeByCommits(); +} + +void RUser::updateSizeByCommits() { + if(!gGourceSettings.user_scale_by_commits) return; + + float commitScale = 1.0f; + float commits = (float)totalCommitCount; + float factor = gGourceSettings.commit_scale_factor; + + // tuned so factor=1 gives roughly 2x scale at 100 commits + const float LINEAR_COEFF = 0.01f; + const float SQRT_COEFF = 0.1f; + const float EXP_BASE_COEFF = 0.1f; + const float EXP_POWER_MULT = 3.0f; + + switch(gGourceSettings.commit_scale_type) { + case COMMIT_SCALE_LINEAR: + commitScale = 1.0f + commits * factor * LINEAR_COEFF; + break; + case COMMIT_SCALE_SQRT: + commitScale = 1.0f + sqrtf(commits) * factor * SQRT_COEFF; + break; + case COMMIT_SCALE_EXP: + commitScale = powf(1.0f + factor * EXP_BASE_COEFF, log10f(1.0f + commits) * EXP_POWER_MULT); + break; + case COMMIT_SCALE_LOG: + default: + commitScale = 1.0f + log10f(1.0f + commits) * factor; + break; + } + + commitScale = glm::clamp(commitScale, gGourceSettings.user_min_scale, gGourceSettings.user_max_scale); + + size = 20.0f * gGourceSettings.user_scale * commitScale; + shadowOffset = vec2(2.0, 2.0) * gGourceSettings.user_scale * commitScale; + + dims = vec2(size, size * graphic_ratio); +} + void RUser::logic(float t, float dt) { Pawn::logic(dt); diff --git a/src/user.h b/src/user.h index d0dbdd24..c2837193 100644 --- a/src/user.h +++ b/src/user.h @@ -39,6 +39,7 @@ class RUser : public Pawn { std::list activeActions; size_t actionCount; size_t activeCount; + size_t totalCommitCount; float action_interval; float action_dist; @@ -77,6 +78,10 @@ class RUser : public Pawn { int getActionCount(); int getPendingActionCount(); + size_t getTotalCommitCount() const; + void setTotalCommitCount(size_t count); + + void updateSizeByCommits(); float getAlpha() const;