From 71be7417d306d2a65dea4d6e64cb535e8c307251 Mon Sep 17 00:00:00 2001 From: Explorer09 Date: Sat, 13 Apr 2024 23:25:28 +0800 Subject: [PATCH] (draft) "Positive/negative" Graph meter style Signed-off-by: Kang-Che Sung --- Meter.c | 155 +++++++++++++++++++++++++++----------------------------- 1 file changed, 74 insertions(+), 81 deletions(-) diff --git a/Meter.c b/Meter.c index 741b1cbe2..45d8e77d4 100644 --- a/Meter.c +++ b/Meter.c @@ -243,6 +243,7 @@ static void GraphMeterMode_reallocateGraphBuffer(Meter* this, const GraphDrawCon data->nValues = nValues; } +#if 0 /* Unused code */ static size_t GraphMeterMode_valueCellIndex(unsigned int graphHeight, bool isPercentChart, int deltaExp, unsigned int y, unsigned int* scaleFactor, unsigned int* increment) { assert(deltaExp >= 0); assert(deltaExp < UINT16_WIDTH); @@ -821,9 +822,9 @@ static void GraphMeterMode_computeColors(Meter* this, const GraphDrawContext* co } } } +#endif /* End of unused code */ static void GraphMeterMode_recordNewValue(Meter* this, const GraphDrawContext* context) { - uint8_t maxItems = context->maxItems; bool isPercentChart = context->isPercentChart; size_t nCellsPerValue = context->nCellsPerValue; if (!nCellsPerValue) @@ -843,15 +844,23 @@ static void GraphMeterMode_recordNewValue(Meter* this, const GraphDrawContext* c valueStart = &valueStart[(nValues - 1) * nCellsPerValue]; // Compute "sum" and "total" - double sum = Meter_computeSum(this); - assert(sum >= 0.0); - assert(sum <= DBL_MAX); - double total; + double total = 0.0; int scaleExp = 0; if (isPercentChart) { - total = MAXIMUM(this->total, sum); + assert(this->total >= 0.0); + total = this->total; + valueStart[0].scaleExp = (int16_t)scaleExp; } else { - (void) frexp(sum, &scaleExp); + if (isPositive(this->values[0])) + total = this->values[0]; + + if (this->curItems > 1 && isgreater(this->values[1], total)) + total = this->values[1]; + + if (total > DBL_MAX) + total = DBL_MAX; + + (void) frexp(total, &scaleExp); if (scaleExp < 0) { scaleExp = 0; } @@ -867,47 +876,22 @@ static void GraphMeterMode_recordNewValue(Meter* this, const GraphDrawContext* c assert(graphHeight <= UINT16_MAX / 8); double maxDots = (double)(int32_t)(graphHeight * 8); - int numDots = (int) ceil((sum / total) * maxDots); - assert(numDots >= 0); - if (sum > 0.0 && numDots <= 0) { - numDots = 1; // Division of (sum / total) underflows - } - - if (maxItems == 1) { - assert(numDots <= UINT16_MAX); - valueStart[isPercentChart ? 0 : 1].numDots = (uint16_t)numDots; - return; - } - - // Clear cells - unsigned int y = ((unsigned int)numDots + 8 - 1) / 8; // Round up - size_t i = GraphMeterMode_valueCellIndex(graphHeight, isPercentChart, 0, y, NULL, NULL); - if (i < nCellsPerValue) { - memset(&valueStart[i], 0, (nCellsPerValue - i) * sizeof(*valueStart)); - } - - if (sum <= 0.0) - return; - - int deltaExp = 0; - double scaledTotal = total; - while (true) { - numDots = (int) ceil((sum / scaledTotal) * maxDots); - if (numDots <= 0) { - numDots = 1; // Division of (sum / scaledTotal) underflows - } - - GraphMeterMode_computeColors(this, context, valueStart, deltaExp, scaledTotal, (unsigned int)numDots); - - if (isPercentChart || !(scaledTotal < DBL_MAX) || (1U << deltaExp) >= graphHeight) { - break; + for (uint8_t i = 0; i < 2; i++) { + double value = 0.0; + if (i < this->curItems && isPositive(this->values[i])) + value = this->values[i]; + + if (value > total) + value = total; + + int numDots = (int) ceil((value / total) * maxDots); + assert(numDots >= 0); + if (value > 0.0 && numDots <= 0) { + numDots = 1; // Division of (value / total) underflows } - deltaExp++; - scaledTotal *= 2.0; - if (scaledTotal > DBL_MAX) { - scaledTotal = DBL_MAX; - } + assert(numDots <= UINT16_MAX); + valueStart[1 + i].numDots = (uint16_t)numDots; } } @@ -927,6 +911,7 @@ static void GraphMeterMode_printScale(int exponent) { } } +#if 0 /* Unused code */ static uint8_t GraphMeterMode_scaleCellDetails(uint8_t details, unsigned int scaleFactor) { // Only the "top cell" of a record may need scaling like this; the cell does // not use the special meaning of bit 4. @@ -954,6 +939,7 @@ static uint8_t GraphMeterMode_scaleCellDetails(uint8_t details, unsigned int sca } return 0x00; } +#endif /* End of unused code */ static int GraphMeterMode_lookupCell(const Meter* this, const GraphDrawContext* context, int scaleExp, size_t valueIndex, unsigned int y, uint8_t* details) { unsigned int graphHeight = (unsigned int)this->h; @@ -980,50 +966,59 @@ static int GraphMeterMode_lookupCell(const Meter* this, const GraphDrawContext* int deltaExp = isPercentChart ? 0 : scaleExp - valueStart[0].scaleExp; assert(deltaExp >= 0); - if (maxItems == 1) { - unsigned int numDots = valueStart[isPercentChart ? 0 : 1].numDots; + unsigned int numDots = valueStart[1].numDots; + if (numDots >= 1) { + if (deltaExp + 1 < UINT16_WIDTH) { + numDots = ((numDots - 1) >> (deltaExp + 1)) + 1; + } else { + numDots = 1; + } + } + unsigned int blanksAtEnd = graphHeight * 4 - numDots; - if (numDots < 1) - goto cellIsEmpty; + numDots = valueStart[2].numDots; + if (numDots >= 1) { + if (deltaExp + 1 < UINT16_WIDTH) { + numDots = ((numDots - 1) >> (deltaExp + 1)) + 1; + } else { + numDots = 1; + } + } + unsigned int blanksAtStart = graphHeight * 4 - numDots; - // Scale according to exponent difference. Round up. - numDots = deltaExp < UINT16_WIDTH ? ((numDots - 1) >> deltaExp) : 0; - numDots++; + if (graphHeight - 1 - y < blanksAtEnd / 8) + goto cellIsEmpty; + if (y < blanksAtStart / 8) + goto cellIsEmpty; - if (y > (numDots - 1) / 8) - goto cellIsEmpty; + if (y * 2 == graphHeight - 1 && !(valueStart[1].numDots || valueStart[2].numDots)) + goto cellIsEmpty; + if (maxItems <= 1 || y * 2 > graphHeight - 1) { itemIndex = 0; - *details = 0xFF; - if (y == (numDots - 1) / 8) { - const uint8_t dotAlignment = 2; - unsigned int blanksAtTopCell = (8 - 1 - (numDots - 1) % 8) / dotAlignment * dotAlignment; - *details <<= blanksAtTopCell; - } + } else if (y * 2 < graphHeight - 1) { + itemIndex = 1; } else { - int deltaExpArg = deltaExp >= UINT16_WIDTH ? UINT16_WIDTH - 1 : deltaExp; - - unsigned int scaleFactor; - size_t i = GraphMeterMode_valueCellIndex(graphHeight, isPercentChart, deltaExpArg, y, &scaleFactor, NULL); - if (i >= nCellsPerValue) - goto cellIsEmpty; + itemIndex = valueStart[1].numDots >= valueStart[2].numDots ? 0 : 1; + } - if (deltaExp >= UINT16_WIDTH) { - // Any "scaleFactor" value greater than 8 behaves the same as 8 for the - // "scaleCellDetails" function. - scaleFactor = 8; + if (y * 2 == graphHeight - 1 && valueStart[1].numDots > 8 && valueStart[2].numDots > 8) { + *details = valueStart[1].numDots >= valueStart[2].numDots ? 0x0F : 0xF0; + } else { + *details = 0xFF; + const uint8_t dotAlignment = 2; + if (y == blanksAtStart / 8) { + blanksAtStart = (blanksAtStart % 8) / dotAlignment * dotAlignment; + *details >>= blanksAtStart; + } + if ((graphHeight - 1 - y) == blanksAtEnd / 8) { + blanksAtEnd = (blanksAtEnd % 8) / dotAlignment * dotAlignment; + *details = (uint8_t)((*details >> blanksAtEnd) << blanksAtEnd); } - - const GraphDataCell* cell = &valueStart[i]; - itemIndex = cell->c.itemNum - 1; - *details = GraphMeterMode_scaleCellDetails(cell->c.details, scaleFactor); } /* fallthrough */ cellIsEmpty: - if (y == 0) - *details |= 0xC0; - if (itemIndex == (uint8_t)-1) return BAR_SHADOW; @@ -1121,9 +1116,7 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) { uint8_t maxItems = Meter_maxItems(this); bool isPercentChart = Meter_isPercentChart(this); - size_t nCellsPerValue = maxItems <= 1 ? maxItems : graphHeight; - if (!isPercentChart) - nCellsPerValue *= 2; + size_t nCellsPerValue = maxItems < 1 ? maxItems : 3; GraphDrawContext context = { .maxItems = maxItems,