Skip to content

Commit 85baa92

Browse files
committed
(draft) "Positive/negative" Graph meter style
Signed-off-by: Kang-Che Sung <[email protected]>
1 parent 3ef876e commit 85baa92

File tree

1 file changed

+76
-94
lines changed

1 file changed

+76
-94
lines changed

Meter.c

Lines changed: 76 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ in the source distribution for its full text.
3636
#define DEFAULT_GRAPH_HEIGHT 4 /* Unit: rows (lines) */
3737
#define MAX_GRAPH_HEIGHT 8191 /* == (int)(UINT16_MAX / 8) */
3838

39+
#if 0 /* Unused code */
3940
typedef struct GraphColorCell_ {
4041
/* Meter item number for the cell's color. Item numbers [1, 255] correspond
4142
to array indices [0, 254] respectively. 0 means no color for the cell. */
@@ -46,11 +47,11 @@ typedef struct GraphColorCell_ {
4647
specially. See the code of the "printCellDetails" function. */
4748
uint8_t details;
4849
} GraphColorCell;
50+
#endif /* End of unused code */
4951

5052
typedef union GraphDataCell_ {
5153
int16_t scaleExp;
5254
uint16_t numDots;
53-
GraphColorCell c;
5455
} GraphDataCell;
5556

5657
typedef struct GraphDrawContext_ {
@@ -255,6 +256,7 @@ static void GraphMeterMode_reallocateGraphBuffer(Meter* this, const GraphDrawCon
255256
data->nValues = nValues;
256257
}
257258

259+
#if 0 /* Unused code */
258260
static inline size_t GraphMeterMode_valueCellIndex(unsigned int h, bool isPercentChart, int deltaExp, unsigned int y, unsigned int* scaleFactor, unsigned int* increment) {
259261
assert(deltaExp >= 0);
260262
assert(deltaExp < UINT16_WIDTH);
@@ -830,9 +832,9 @@ static void GraphMeterMode_computeColors(Meter* this, const GraphDrawContext* co
830832
}
831833
}
832834
}
835+
#endif /* End of unused code */
833836

834837
static void GraphMeterMode_recordNewValue(Meter* this, const GraphDrawContext* context) {
835-
uint8_t maxItems = context->maxItems;
836838
bool isPercentChart = context->isPercentChart;
837839
size_t nCellsPerValue = context->nCellsPerValue;
838840
if (!nCellsPerValue)
@@ -851,23 +853,23 @@ static void GraphMeterMode_recordNewValue(Meter* this, const GraphDrawContext* c
851853
valueStart = (GraphDataCell*)data->buffer;
852854
valueStart = &valueStart[(nValues - 1) * nCellsPerValue];
853855

854-
// Sum the values of all items
855-
double sum = 0.0;
856-
if (this->curItems > 0) {
857-
sum = Meter_computeSum(this);
858-
assert(sum >= 0.0);
859-
assert(sum <= DBL_MAX);
860-
}
861-
862856
// "total" refers to the value that we would draw as full in graph
863-
double total = MAXIMUM(this->total, sum);
857+
double total = 0.0;
858+
if (isPercentChart) {
859+
assert(this->total >= 0.0);
860+
total = this->total;
861+
valueStart[0].scaleExp = 0;
862+
} else {
863+
if (isPositive(this->values[0]))
864+
total = this->values[0];
865+
866+
if (this->curItems > 1 && isgreater(this->values[1], total))
867+
total = this->values[1];
868+
869+
total = MINIMUM(DBL_MAX, total);
864870

865-
if (!isPercentChart) {
866-
// Dynamic scale. "this->total" is ignored.
867-
// Determine the scale and "total" that we need afterward. The "total" is
868-
// the "sum" value rounded up to a power of 2.
869871
int scaleExp = 0;
870-
(void)frexp(sum, &scaleExp);
872+
(void)frexp(total, &scaleExp);
871873
scaleExp = MAXIMUM(0, scaleExp);
872874

873875
// It's safe to assume "scaleExp" never overflows when IEEE 754
@@ -882,57 +884,26 @@ static void GraphMeterMode_recordNewValue(Meter* this, const GraphDrawContext* c
882884
// Prevent overflow from "this->total" or ldexp().
883885
total = MINIMUM(DBL_MAX, total);
884886

885-
// The total number of dots that we would draw for this record
886887
assert(h <= UINT16_MAX / 8);
887888
double maxDots = (double)(int32_t)(h * 8);
888-
int numDots = 0;
889-
if (total > 0.0) {
890-
numDots = (int)ceil((sum / total) * maxDots);
891-
assert(numDots >= 0);
892-
if (sum > 0.0 && numDots <= 0) {
893-
numDots = 1; // Division of (sum / total) underflows
889+
for (uint8_t i = 0; i < 2; i++) {
890+
double value = 0.0;
891+
if (i < this->curItems && isPositive(this->values[i]))
892+
value = this->values[i];
893+
894+
value = MINIMUM(total, value);
895+
896+
int numDots = 0;
897+
if (total > 0.0) {
898+
numDots = (int)ceil((value / total) * maxDots);
899+
assert(numDots >= 0);
900+
if (value > 0.0 && numDots <= 0) {
901+
numDots = 1; // Division of (value / total) underflows
902+
}
894903
}
895-
}
896904

897-
// For a one-item meter, record the number of dots in the graph data buffer
898-
// and we are done.
899-
if (maxItems == 1) {
900905
assert(numDots <= UINT16_MAX);
901-
valueStart[isPercentChart ? 0 : 1].numDots = (uint16_t)numDots;
902-
return;
903-
}
904-
905-
// For a meter of multiple items, we will precompute the colors of each cell
906-
// and store them in a record. First clear the cells, which might contain
907-
// data of the previous record.
908-
unsigned int y = ((unsigned int)numDots + 8 - 1) / 8; // Round up
909-
size_t i = GraphMeterMode_valueCellIndex(h, isPercentChart, 0, y, NULL, NULL);
910-
if (i < nCellsPerValue) {
911-
memset(&valueStart[i], 0, (nCellsPerValue - i) * sizeof(*valueStart));
912-
}
913-
914-
if (sum <= 0.0) {
915-
// The record is empty. No colors needed.
916-
return;
917-
}
918-
919-
int deltaExp = 0;
920-
double scaledTotal = total;
921-
assert(scaledTotal > 0.0);
922-
while (true) {
923-
numDots = (int)ceil((sum / scaledTotal) * maxDots);
924-
if (numDots <= 0) {
925-
numDots = 1; // Division of (sum / scaledTotal) underflows
926-
}
927-
928-
GraphMeterMode_computeColors(this, context, valueStart, deltaExp, scaledTotal, (unsigned int)numDots);
929-
930-
if (isPercentChart || !(scaledTotal < DBL_MAX) || (1U << deltaExp) >= h) {
931-
break;
932-
}
933-
934-
deltaExp++;
935-
scaledTotal = MINIMUM(DBL_MAX, scaledTotal * 2.0);
906+
valueStart[1 + i].numDots = (uint16_t)numDots;
936907
}
937908
}
938909

@@ -952,6 +923,7 @@ static void GraphMeterMode_printScale(int exponent) {
952923
}
953924
}
954925

926+
#if 0 /* Unused code */
955927
static uint8_t GraphMeterMode_scaleCellDetails(uint8_t details, unsigned int scaleFactor) {
956928
// This scaling routine is only used on the "top cell" of a record.
957929
// (The "top cell" never uses the special meaning of bit 4.)
@@ -981,6 +953,7 @@ static uint8_t GraphMeterMode_scaleCellDetails(uint8_t details, unsigned int sca
981953
}
982954
return 0x00;
983955
}
956+
#endif /* End of unused code */
984957

985958
static int GraphMeterMode_lookupCell(const Meter* this, const GraphDrawContext* context, int scaleExp, size_t valueIndex, unsigned int y, uint8_t* details) {
986959
unsigned int h = (unsigned int)this->h;
@@ -1007,50 +980,59 @@ static int GraphMeterMode_lookupCell(const Meter* this, const GraphDrawContext*
1007980
int deltaExp = isPercentChart ? 0 : scaleExp - valueStart[0].scaleExp;
1008981
assert(deltaExp >= 0);
1009982

1010-
if (maxItems == 1) {
1011-
unsigned int numDots = valueStart[isPercentChart ? 0 : 1].numDots;
983+
unsigned int numDots = valueStart[1].numDots;
984+
if (numDots >= 1) {
985+
if (deltaExp + 1 < UINT16_WIDTH) {
986+
numDots = ((numDots - 1) >> (deltaExp + 1)) + 1;
987+
} else {
988+
numDots = 1;
989+
}
990+
}
991+
unsigned int blanksAtEnd = h * 4 - numDots;
1012992

1013-
if (numDots < 1)
1014-
goto cellIsEmpty;
993+
numDots = valueStart[2].numDots;
994+
if (numDots >= 1) {
995+
if (deltaExp + 1 < UINT16_WIDTH) {
996+
numDots = ((numDots - 1) >> (deltaExp + 1)) + 1;
997+
} else {
998+
numDots = 1;
999+
}
1000+
}
1001+
unsigned int blanksAtStart = h * 4 - numDots;
10151002

1016-
// Scale according to exponent difference. Round up.
1017-
numDots = deltaExp < UINT16_WIDTH ? ((numDots - 1) >> deltaExp) : 0;
1018-
numDots++;
1003+
if (h - 1 - y < blanksAtEnd / 8)
1004+
goto cellIsEmpty;
1005+
if (y < blanksAtStart / 8)
1006+
goto cellIsEmpty;
10191007

1020-
if (y > (numDots - 1) / 8)
1021-
goto cellIsEmpty;
1008+
if (y * 2 == h - 1 && !(valueStart[1].numDots || valueStart[2].numDots))
1009+
goto cellIsEmpty;
10221010

1011+
if (maxItems <= 1 || y * 2 > h - 1) {
10231012
itemIndex = 0;
1024-
*details = 0xFF;
1025-
if (y == (numDots - 1) / 8) {
1026-
const uint8_t dotAlignment = 2;
1027-
unsigned int blanksAtTopCell = (8 - 1 - (numDots - 1) % 8) / dotAlignment * dotAlignment;
1028-
*details <<= blanksAtTopCell;
1029-
}
1013+
} else if (y * 2 < h - 1) {
1014+
itemIndex = 1;
10301015
} else {
1031-
int deltaExpArg = MINIMUM(UINT16_WIDTH - 1, deltaExp);
1032-
1033-
unsigned int scaleFactor;
1034-
size_t i = GraphMeterMode_valueCellIndex(h, isPercentChart, deltaExpArg, y, &scaleFactor, NULL);
1035-
if (i >= nCellsPerValue)
1036-
goto cellIsEmpty;
1016+
itemIndex = valueStart[1].numDots >= valueStart[2].numDots ? 0 : 1;
1017+
}
10371018

1038-
if (deltaExp >= UINT16_WIDTH) {
1039-
// Any "scaleFactor" value greater than 8 behaves the same as 8 for the
1040-
// "scaleCellDetails" function.
1041-
scaleFactor = 8;
1019+
if (y * 2 == h - 1 && valueStart[1].numDots > 8 && valueStart[2].numDots > 8) {
1020+
*details = valueStart[1].numDots >= valueStart[2].numDots ? 0x0F : 0xF0;
1021+
} else {
1022+
*details = 0xFF;
1023+
const uint8_t dotAlignment = 2;
1024+
if (y == blanksAtStart / 8) {
1025+
blanksAtStart = (blanksAtStart % 8) / dotAlignment * dotAlignment;
1026+
*details >>= blanksAtStart;
1027+
}
1028+
if ((h - 1 - y) == blanksAtEnd / 8) {
1029+
blanksAtEnd = (blanksAtEnd % 8) / dotAlignment * dotAlignment;
1030+
*details = (uint8_t)((*details >> blanksAtEnd) << blanksAtEnd);
10421031
}
1043-
1044-
const GraphDataCell* cell = &valueStart[i];
1045-
itemIndex = cell->c.itemNum - 1;
1046-
*details = GraphMeterMode_scaleCellDetails(cell->c.details, scaleFactor);
10471032
}
10481033
/* fallthrough */
10491034

10501035
cellIsEmpty:
1051-
if (y == 0)
1052-
*details |= 0xC0;
1053-
10541036
if (itemIndex == (uint8_t)-1)
10551037
return BAR_SHADOW;
10561038

0 commit comments

Comments
 (0)