diff --git a/source/blender/blenlib/BLI_string.h b/source/blender/blenlib/BLI_string.h index 786e5e9d9d8..b249bc720c6 100644 --- a/source/blender/blenlib/BLI_string.h +++ b/source/blender/blenlib/BLI_string.h @@ -64,6 +64,8 @@ char *BLI_sprintfN(const char *__restrict format, ...) ATTR_WARN_UNUSED_RESULT A size_t BLI_strescape(char *__restrict dst, const char *__restrict src, const size_t maxncpy) ATTR_NONNULL(); +size_t BLI_str_format_int_grouped(char dst[16], int num) ATTR_NONNULL(); + int BLI_strcaseeq(const char *a, const char *b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); char *BLI_strcasestr(const char *s, const char *find) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); int BLI_strcasecmp(const char *s1, const char *s2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c index f396abbeb8d..aa705aaf63e 100644 --- a/source/blender/blenlib/intern/string.c +++ b/source/blender/blenlib/intern/string.c @@ -741,3 +741,38 @@ size_t BLI_str_partition_ex(const char *str, const char delim[], char **sep, cha return strlen(str); } + +/** + * Format ints with decimal grouping. + * 1000 -> 1,000 + * + * \param dst The resulting string + * \param num Number to format + * \return The length of \a dst + */ +size_t BLI_str_format_int_grouped(char dst[16], int num) +{ + char src[16]; + char *p_src = src; + char *p_dst = dst; + + const char separator = ','; + int num_len, commas; + + num_len = sprintf(src, "%d", num); + + if (*p_src == '-') { + *p_dst++ = *p_src++; + num_len--; + } + + for (commas = 2 - num_len % 3; *p_src; commas = (commas + 1) % 3) { + *p_dst++ = *p_src++; + if (commas == 1) { + *p_dst++ = separator; + } + } + *--p_dst = '\0'; + + return (size_t)(p_dst - dst); +} diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index 8664ebf30b7..d28b0bfe534 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -57,6 +57,7 @@ #include "ED_armature.h" #define MAX_INFO_LEN 512 +#define MAX_INFO_NUM_LEN 16 typedef struct SceneStats { int totvert, totvertsel; @@ -65,11 +66,22 @@ typedef struct SceneStats { int totbone, totbonesel; int totobj, totobjsel; int totlamp, totlampsel; - int tottri, totmesh; + int tottri; char infostr[MAX_INFO_LEN]; } SceneStats; +typedef struct SceneStatsFmt { + /* Totals */ + char totvert[MAX_INFO_NUM_LEN], totvertsel[MAX_INFO_NUM_LEN]; + char totface[MAX_INFO_NUM_LEN], totfacesel[MAX_INFO_NUM_LEN]; + char totedge[MAX_INFO_NUM_LEN], totedgesel[MAX_INFO_NUM_LEN]; + char totbone[MAX_INFO_NUM_LEN], totbonesel[MAX_INFO_NUM_LEN]; + char totobj[MAX_INFO_NUM_LEN], totobjsel[MAX_INFO_NUM_LEN]; + char totlamp[MAX_INFO_NUM_LEN], totlampsel[MAX_INFO_NUM_LEN]; + char tottri[MAX_INFO_NUM_LEN]; +} SceneStatsFmt; + static void stats_object(Object *ob, int sel, int totob, SceneStats *stats) { switch (ob->type) { @@ -79,8 +91,6 @@ static void stats_object(Object *ob, int sel, int totob, SceneStats *stats) DerivedMesh *dm = ob->derivedFinal; int totvert, totedge, totface, totloop; - stats->totmesh += totob; - if (dm) { totvert = dm->getNumVerts(dm); totedge = dm->getNumEdges(dm); @@ -367,6 +377,7 @@ static void stats_string(Scene *scene) { #define MAX_INFO_MEM_LEN 64 SceneStats *stats = scene->stats; + SceneStatsFmt stats_fmt; Object *ob = (scene->basact) ? scene->basact->object : NULL; uintptr_t mem_in_use, mmap_in_use; char memstr[MAX_INFO_MEM_LEN]; @@ -376,6 +387,34 @@ static void stats_string(Scene *scene) mem_in_use = MEM_get_memory_in_use(); mmap_in_use = MEM_get_mapped_memory_in_use(); + + /* Generate formatted numbers */ +#define SCENE_STATS_FMT_INT(_id) \ + BLI_str_format_int_grouped(stats_fmt._id, stats->_id) + + SCENE_STATS_FMT_INT(totvert); + SCENE_STATS_FMT_INT(totvertsel); + + SCENE_STATS_FMT_INT(totedge); + SCENE_STATS_FMT_INT(totedgesel); + + SCENE_STATS_FMT_INT(totface); + SCENE_STATS_FMT_INT(totfacesel); + + SCENE_STATS_FMT_INT(totbone); + SCENE_STATS_FMT_INT(totbonesel); + + SCENE_STATS_FMT_INT(totobj); + SCENE_STATS_FMT_INT(totobjsel); + + SCENE_STATS_FMT_INT(totlamp); + SCENE_STATS_FMT_INT(totlampsel); + + SCENE_STATS_FMT_INT(tottri); + +#undef SCENE_STATS_FMT_INT + + /* get memory statistics */ s = memstr; ofs += BLI_snprintf(s + ofs, MAX_INFO_MEM_LEN - ofs, IFACE_(" | Mem:%.2fM"), @@ -394,32 +433,36 @@ static void stats_string(Scene *scene) if (scene->obedit->type == OB_MESH) { ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, - IFACE_("Verts:%d/%d | Edges:%d/%d | Faces:%d/%d | Tris:%d"), - stats->totvertsel, stats->totvert, stats->totedgesel, stats->totedge, - stats->totfacesel, stats->totface, stats->tottri); + IFACE_("Verts:%s/%s | Edges:%s/%s | Faces:%s/%s | Tris:%s"), + stats_fmt.totvertsel, stats_fmt.totvert, stats_fmt.totedgesel, stats_fmt.totedge, + stats_fmt.totfacesel, stats_fmt.totface, stats_fmt.tottri); } else if (scene->obedit->type == OB_ARMATURE) { - ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Verts:%d/%d | Bones:%d/%d"), stats->totvertsel, - stats->totvert, stats->totbonesel, stats->totbone); + ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Verts:%s/%s | Bones:%s/%s"), stats_fmt.totvertsel, + stats_fmt.totvert, stats_fmt.totbonesel, stats_fmt.totbone); } else { - ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Verts:%d/%d"), stats->totvertsel, stats->totvert); + ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Verts:%s/%s"), stats_fmt.totvertsel, + stats_fmt.totvert); } ofs += BLI_strncpy_rlen(s + ofs, memstr, MAX_INFO_LEN - ofs); } else if (ob && (ob->mode & OB_MODE_POSE)) { - ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Bones:%d/%d %s"), - stats->totbonesel, stats->totbone, memstr); + ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Bones:%s/%s %s"), + stats_fmt.totbonesel, stats_fmt.totbone, memstr); } else if (stats_is_object_dynamic_topology_sculpt(ob)) { - ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Verts:%d | Tris:%d"), stats->totvert, stats->tottri); + ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Verts:%s | Tris:%s"), stats_fmt.totvert, + stats_fmt.tottri); } else { ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, - IFACE_("Verts:%d | Faces:%d | Tris:%d | Objects:%d/%d | Lamps:%d/%d%s"), stats->totvert, - stats->totface, stats->tottri, stats->totobjsel, stats->totobj, stats->totlampsel, - stats->totlamp, memstr); + IFACE_("Verts: %s | Faces:%s | Tris:%s | Objects:%s/%s | Lamps:%s/%s%s"), + stats_fmt.totvert, stats_fmt.totface, + stats_fmt.tottri, stats_fmt.totobjsel, + stats_fmt.totobj, stats_fmt.totlampsel, + stats_fmt.totlamp, memstr); } if (ob) diff --git a/tests/gtests/blenlib/BLI_string_test.cc b/tests/gtests/blenlib/BLI_string_test.cc index 13091d9e074..4c5c410dcb2 100644 --- a/tests/gtests/blenlib/BLI_string_test.cc +++ b/tests/gtests/blenlib/BLI_string_test.cc @@ -267,3 +267,37 @@ TEST(string, StrRPartitionUtf8) EXPECT_EQ(NULL, suf); } } + +/* BLI_str_format_int_grouped */ +TEST(string, StrFormatIntGrouped) +{ + char num_str[16]; + int num; + + BLI_str_format_int_grouped(num_str, num = 0); + EXPECT_STREQ("0", num_str); + + BLI_str_format_int_grouped(num_str, num = 1); + EXPECT_STREQ("1", num_str); + + BLI_str_format_int_grouped(num_str, num = -1); + EXPECT_STREQ("-1", num_str); + + BLI_str_format_int_grouped(num_str, num = -2147483648); + EXPECT_STREQ("-2,147,483,648", num_str); + + BLI_str_format_int_grouped(num_str, num = 2147483647); + EXPECT_STREQ("2,147,483,647", num_str); + + BLI_str_format_int_grouped(num_str, num = 1000); + EXPECT_STREQ("1,000", num_str); + + BLI_str_format_int_grouped(num_str, num = -1000); + EXPECT_STREQ("-1,000", num_str); + + BLI_str_format_int_grouped(num_str, num = 999); + EXPECT_STREQ("999", num_str); + + BLI_str_format_int_grouped(num_str, num = -999); + EXPECT_STREQ("-999", num_str); +}