Skip to content

Commit

Permalink
feat(status-timeline): visually differentiate value change when data …
Browse files Browse the repository at this point in the history
…points have no associated breached thresholds
  • Loading branch information
diehbria committed Feb 3, 2023
1 parent 320a59c commit 94f6181
Show file tree
Hide file tree
Showing 14 changed files with 115 additions and 73 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ export const STATUS_MARGIN_TOP_PX = 34 - MARGIN_FUDGE_FACTOR;
// This determines the maximum width in terms of duration, for a single raw point of data within the status chart.
export const MAX_RAW_RESOLUTION_DURATION = MINUTE_IN_MS;

export const DEFAULT_STATUS_BAR_COLOR = [165, 165, 165]; // (r, g, b) from 0 to 255
export const DEFAULT_STATUS_BAR_COLOR_1 = [213, 219, 219]; // (r, g, b) from 0 to 255
export const DEFAULT_STATUS_BAR_COLOR_2 = [135, 149, 150]; // (r, g, b) from 0 to 255
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NUM_STATUS_COMPONENTS, statusMesh, updateStatusMesh } from './statusMesh';
import { DEFAULT_STATUS_BAR_COLOR, HEIGHT } from './constants';
import { DEFAULT_STATUS_BAR_COLOR_1, DEFAULT_STATUS_BAR_COLOR_2, HEIGHT } from './constants';
import { DAY_IN_MS, MINUTE_IN_MS } from '../../../utils/time';
import { DataType } from '../../../utils/dataConstants';
import { DataPoint, DataStream } from '../../../utils/dataTypes';
Expand Down Expand Up @@ -121,6 +121,32 @@ describe('create status mesh', () => {
expect(mesh.geometry.attributes.status.array[3]).toBeLessThan(HEIGHT);
});

it('draws adjacent data points as the same gray color when the data points have the same y value', () => {
const mesh = statusMesh({
...BASE_PROPS,
dataStreams: [
{
id: 'data-stream',
name: 'name',
resolution: 0,
dataType: DataType.NUMBER,
data: [DATA_POINT_1, { ...DATA_POINT_2, y: DATA_POINT_1.y }], // 2 points with the same y
},
],
});
expect(mesh.count).toEqual(2);

// default status bar color 1
expect(mesh.geometry.attributes.color.array[0]).toBe(DEFAULT_STATUS_BAR_COLOR_1[0]);
expect(mesh.geometry.attributes.color.array[1]).toBe(DEFAULT_STATUS_BAR_COLOR_1[1]);
expect(mesh.geometry.attributes.color.array[2]).toBe(DEFAULT_STATUS_BAR_COLOR_1[2]);

// default status bar color 1
expect(mesh.geometry.attributes.color.array[3]).toBe(DEFAULT_STATUS_BAR_COLOR_1[0]);
expect(mesh.geometry.attributes.color.array[4]).toBe(DEFAULT_STATUS_BAR_COLOR_1[1]);
expect(mesh.geometry.attributes.color.array[5]).toBe(DEFAULT_STATUS_BAR_COLOR_1[2]);
});

it('draws two statuses with two point data set', () => {
const mesh = statusMesh({
...BASE_PROPS,
Expand All @@ -146,15 +172,15 @@ describe('create status mesh', () => {
expect(mesh.geometry.attributes.status.array[6]).toBeCloseTo(EXPECTED_WIDTH, 8);
expect(mesh.geometry.attributes.status.array[7]).toBeLessThan(HEIGHT);

// default status bar color
expect(mesh.geometry.attributes.color.array[0]).toBe(DEFAULT_STATUS_BAR_COLOR[0]);
expect(mesh.geometry.attributes.color.array[1]).toBe(DEFAULT_STATUS_BAR_COLOR[1]);
expect(mesh.geometry.attributes.color.array[2]).toBe(DEFAULT_STATUS_BAR_COLOR[2]);
// default status bar color 1
expect(mesh.geometry.attributes.color.array[0]).toBe(DEFAULT_STATUS_BAR_COLOR_1[0]);
expect(mesh.geometry.attributes.color.array[1]).toBe(DEFAULT_STATUS_BAR_COLOR_1[1]);
expect(mesh.geometry.attributes.color.array[2]).toBe(DEFAULT_STATUS_BAR_COLOR_1[2]);

// default status bar color
expect(mesh.geometry.attributes.color.array[3]).toBe(DEFAULT_STATUS_BAR_COLOR[0]);
expect(mesh.geometry.attributes.color.array[4]).toBe(DEFAULT_STATUS_BAR_COLOR[1]);
expect(mesh.geometry.attributes.color.array[5]).toBe(DEFAULT_STATUS_BAR_COLOR[2]);
// default status bar color 2
expect(mesh.geometry.attributes.color.array[3]).toBe(DEFAULT_STATUS_BAR_COLOR_2[0]);
expect(mesh.geometry.attributes.color.array[4]).toBe(DEFAULT_STATUS_BAR_COLOR_2[1]);
expect(mesh.geometry.attributes.color.array[5]).toBe(DEFAULT_STATUS_BAR_COLOR_2[2]);
});

it('constructs buffers correctly for multiple data streams', () => {
Expand Down Expand Up @@ -206,25 +232,25 @@ describe('create status mesh', () => {
expect(mesh.geometry.attributes.status.array[14]).toBeCloseTo(EXPECTED_WIDTH, 8);
expect(mesh.geometry.attributes.status.array[15]).toBeLessThan(height);

// Data stream 1 status 1 is default status bar color
expect(mesh.geometry.attributes.color.array[0]).toBe(DEFAULT_STATUS_BAR_COLOR[0]);
expect(mesh.geometry.attributes.color.array[1]).toBe(DEFAULT_STATUS_BAR_COLOR[1]);
expect(mesh.geometry.attributes.color.array[2]).toBe(DEFAULT_STATUS_BAR_COLOR[2]);

// Data stream 1 status 1 is default status bar color
expect(mesh.geometry.attributes.color.array[3]).toBe(DEFAULT_STATUS_BAR_COLOR[0]);
expect(mesh.geometry.attributes.color.array[4]).toBe(DEFAULT_STATUS_BAR_COLOR[1]);
expect(mesh.geometry.attributes.color.array[5]).toBe(DEFAULT_STATUS_BAR_COLOR[2]);

// Data stream 2 status 1 is default status bar color
expect(mesh.geometry.attributes.color.array[6]).toBe(DEFAULT_STATUS_BAR_COLOR[0]);
expect(mesh.geometry.attributes.color.array[7]).toBe(DEFAULT_STATUS_BAR_COLOR[1]);
expect(mesh.geometry.attributes.color.array[8]).toBe(DEFAULT_STATUS_BAR_COLOR[2]);

// Data stream 2 status 2 is default status bar color
expect(mesh.geometry.attributes.color.array[9]).toBe(DEFAULT_STATUS_BAR_COLOR[0]);
expect(mesh.geometry.attributes.color.array[10]).toBe(DEFAULT_STATUS_BAR_COLOR[1]);
expect(mesh.geometry.attributes.color.array[11]).toBe(DEFAULT_STATUS_BAR_COLOR[2]);
// Data stream 1 status 1 is default status bar color 1
expect(mesh.geometry.attributes.color.array[0]).toBe(DEFAULT_STATUS_BAR_COLOR_1[0]);
expect(mesh.geometry.attributes.color.array[1]).toBe(DEFAULT_STATUS_BAR_COLOR_1[1]);
expect(mesh.geometry.attributes.color.array[2]).toBe(DEFAULT_STATUS_BAR_COLOR_1[2]);

// Data stream 1 status 1 is default status bar color 2
expect(mesh.geometry.attributes.color.array[3]).toBe(DEFAULT_STATUS_BAR_COLOR_2[0]);
expect(mesh.geometry.attributes.color.array[4]).toBe(DEFAULT_STATUS_BAR_COLOR_2[1]);
expect(mesh.geometry.attributes.color.array[5]).toBe(DEFAULT_STATUS_BAR_COLOR_2[2]);

// Data stream 2 status 1 is default status bar color 1
expect(mesh.geometry.attributes.color.array[6]).toBe(DEFAULT_STATUS_BAR_COLOR_1[0]);
expect(mesh.geometry.attributes.color.array[7]).toBe(DEFAULT_STATUS_BAR_COLOR_1[1]);
expect(mesh.geometry.attributes.color.array[8]).toBe(DEFAULT_STATUS_BAR_COLOR_1[2]);

// Data stream 2 status 2 is default status bar color 2
expect(mesh.geometry.attributes.color.array[9]).toBe(DEFAULT_STATUS_BAR_COLOR_2[0]);
expect(mesh.geometry.attributes.color.array[10]).toBe(DEFAULT_STATUS_BAR_COLOR_2[1]);
expect(mesh.geometry.attributes.color.array[11]).toBe(DEFAULT_STATUS_BAR_COLOR_2[2]);
});

it('constructs buffer correctly for two data streams', () => {
Expand Down Expand Up @@ -310,15 +336,15 @@ describe('update status mesh', () => {
expect(mesh.geometry.attributes.status.array[6]).toBeCloseTo(EXPECTED_WIDTH, 8);
expect(mesh.geometry.attributes.status.array[7]).toBeLessThan(HEIGHT);

// default status bar color
expect(mesh.geometry.attributes.color.array[0]).toBe(DEFAULT_STATUS_BAR_COLOR[0]);
expect(mesh.geometry.attributes.color.array[1]).toBe(DEFAULT_STATUS_BAR_COLOR[1]);
expect(mesh.geometry.attributes.color.array[2]).toBe(DEFAULT_STATUS_BAR_COLOR[2]);
// default status bar color 1
expect(mesh.geometry.attributes.color.array[0]).toBe(DEFAULT_STATUS_BAR_COLOR_1[0]);
expect(mesh.geometry.attributes.color.array[1]).toBe(DEFAULT_STATUS_BAR_COLOR_1[1]);
expect(mesh.geometry.attributes.color.array[2]).toBe(DEFAULT_STATUS_BAR_COLOR_1[2]);

// default status bar color
expect(mesh.geometry.attributes.color.array[3]).toBe(DEFAULT_STATUS_BAR_COLOR[0]);
expect(mesh.geometry.attributes.color.array[4]).toBe(DEFAULT_STATUS_BAR_COLOR[1]);
expect(mesh.geometry.attributes.color.array[5]).toBe(DEFAULT_STATUS_BAR_COLOR[2]);
// default status bar color 2
expect(mesh.geometry.attributes.color.array[3]).toBe(DEFAULT_STATUS_BAR_COLOR_2[0]);
expect(mesh.geometry.attributes.color.array[4]).toBe(DEFAULT_STATUS_BAR_COLOR_2[1]);
expect(mesh.geometry.attributes.color.array[5]).toBe(DEFAULT_STATUS_BAR_COLOR_2[2]);
});

it('updates a non-empty status mesh to an empty one', () => {
Expand Down Expand Up @@ -532,10 +558,10 @@ describe('threshold correctly effects the color buffer', () => {
});
expect(mesh.count).toEqual(1);

// default status bar color
expect(mesh.geometry.attributes.color.array[0]).toBe(DEFAULT_STATUS_BAR_COLOR[0]);
expect(mesh.geometry.attributes.color.array[1]).toBe(DEFAULT_STATUS_BAR_COLOR[1]);
expect(mesh.geometry.attributes.color.array[2]).toBe(DEFAULT_STATUS_BAR_COLOR[2]);
// default status bar color 1
expect(mesh.geometry.attributes.color.array[0]).toBe(DEFAULT_STATUS_BAR_COLOR_1[0]);
expect(mesh.geometry.attributes.color.array[1]).toBe(DEFAULT_STATUS_BAR_COLOR_1[1]);
expect(mesh.geometry.attributes.color.array[2]).toBe(DEFAULT_STATUS_BAR_COLOR_1[2]);
});

it('initializes color buffer with the correct threshold color for a single status', () => {
Expand Down Expand Up @@ -617,10 +643,10 @@ describe('threshold correctly effects the color buffer', () => {
});
expect(mesh.count).toEqual(2);

// Default status color
expect(mesh.geometry.attributes.color.array[0]).toBe(DEFAULT_STATUS_BAR_COLOR[0]);
expect(mesh.geometry.attributes.color.array[1]).toBe(DEFAULT_STATUS_BAR_COLOR[1]);
expect(mesh.geometry.attributes.color.array[2]).toBe(DEFAULT_STATUS_BAR_COLOR[2]);
// Default status color 1
expect(mesh.geometry.attributes.color.array[0]).toBe(DEFAULT_STATUS_BAR_COLOR_1[0]);
expect(mesh.geometry.attributes.color.array[1]).toBe(DEFAULT_STATUS_BAR_COLOR_1[1]);
expect(mesh.geometry.attributes.color.array[2]).toBe(DEFAULT_STATUS_BAR_COLOR_1[2]);

// Red
expect(mesh.geometry.attributes.color.array[3]).toBe(255);
Expand Down Expand Up @@ -678,10 +704,10 @@ describe('threshold correctly effects the color buffer', () => {
});
expect(mesh.count).toEqual(1);

// default status bar color
expect(mesh.geometry.attributes.color.array[0]).toBe(DEFAULT_STATUS_BAR_COLOR[0]);
expect(mesh.geometry.attributes.color.array[1]).toBe(DEFAULT_STATUS_BAR_COLOR[1]);
expect(mesh.geometry.attributes.color.array[2]).toBe(DEFAULT_STATUS_BAR_COLOR[2]);
// default status bar color 1
expect(mesh.geometry.attributes.color.array[0]).toBe(DEFAULT_STATUS_BAR_COLOR_1[0]);
expect(mesh.geometry.attributes.color.array[1]).toBe(DEFAULT_STATUS_BAR_COLOR_1[1]);
expect(mesh.geometry.attributes.color.array[2]).toBe(DEFAULT_STATUS_BAR_COLOR_1[2]);
});

it('updates the color buffer when point breached threshold, despite the data stream color change', () => {
Expand Down Expand Up @@ -769,10 +795,10 @@ describe('threshold correctly effects the color buffer', () => {
});
expect(mesh.count).toEqual(1);

// default status bar color
expect(mesh.geometry.attributes.color.array[0]).toBe(DEFAULT_STATUS_BAR_COLOR[0]);
expect(mesh.geometry.attributes.color.array[1]).toBe(DEFAULT_STATUS_BAR_COLOR[1]);
expect(mesh.geometry.attributes.color.array[2]).toBe(DEFAULT_STATUS_BAR_COLOR[2]);
// default status bar color 1
expect(mesh.geometry.attributes.color.array[0]).toBe(DEFAULT_STATUS_BAR_COLOR_1[0]);
expect(mesh.geometry.attributes.color.array[1]).toBe(DEFAULT_STATUS_BAR_COLOR_1[1]);
expect(mesh.geometry.attributes.color.array[2]).toBe(DEFAULT_STATUS_BAR_COLOR_1[2]);

updateStatusMesh({
chartSize: CHART_SIZE,
Expand Down Expand Up @@ -848,10 +874,10 @@ describe('threshold correctly effects the color buffer', () => {
});
expect(mesh.count).toEqual(1);

// default status bar color
expect(mesh.geometry.attributes.color.array[0]).toBe(DEFAULT_STATUS_BAR_COLOR[0]);
expect(mesh.geometry.attributes.color.array[1]).toBe(DEFAULT_STATUS_BAR_COLOR[1]);
expect(mesh.geometry.attributes.color.array[2]).toBe(DEFAULT_STATUS_BAR_COLOR[2]);
// default status bar color 1
expect(mesh.geometry.attributes.color.array[0]).toBe(DEFAULT_STATUS_BAR_COLOR_1[0]);
expect(mesh.geometry.attributes.color.array[1]).toBe(DEFAULT_STATUS_BAR_COLOR_1[1]);
expect(mesh.geometry.attributes.color.array[2]).toBe(DEFAULT_STATUS_BAR_COLOR_1[2]);
});

it('updates the color buffer with the correct threshold color for multiple streams with different colors', () => {
Expand Down Expand Up @@ -885,15 +911,15 @@ describe('threshold correctly effects the color buffer', () => {
});
expect(mesh.count).toEqual(2);

// default status bar color
expect(mesh.geometry.attributes.color.array[0]).toBe(DEFAULT_STATUS_BAR_COLOR[0]);
expect(mesh.geometry.attributes.color.array[1]).toBe(DEFAULT_STATUS_BAR_COLOR[1]);
expect(mesh.geometry.attributes.color.array[2]).toBe(DEFAULT_STATUS_BAR_COLOR[2]);
// default status bar color 1
expect(mesh.geometry.attributes.color.array[0]).toBe(DEFAULT_STATUS_BAR_COLOR_1[0]);
expect(mesh.geometry.attributes.color.array[1]).toBe(DEFAULT_STATUS_BAR_COLOR_1[1]);
expect(mesh.geometry.attributes.color.array[2]).toBe(DEFAULT_STATUS_BAR_COLOR_1[2]);

// default status bar color
expect(mesh.geometry.attributes.color.array[3]).toBe(DEFAULT_STATUS_BAR_COLOR[0]);
expect(mesh.geometry.attributes.color.array[4]).toBe(DEFAULT_STATUS_BAR_COLOR[1]);
expect(mesh.geometry.attributes.color.array[5]).toBe(DEFAULT_STATUS_BAR_COLOR[2]);
// default status bar color 1
expect(mesh.geometry.attributes.color.array[3]).toBe(DEFAULT_STATUS_BAR_COLOR_1[0]);
expect(mesh.geometry.attributes.color.array[4]).toBe(DEFAULT_STATUS_BAR_COLOR_1[1]);
expect(mesh.geometry.attributes.color.array[5]).toBe(DEFAULT_STATUS_BAR_COLOR_1[2]);

const thresholds2: Threshold[] = [
{
Expand Down Expand Up @@ -928,10 +954,10 @@ describe('threshold correctly effects the color buffer', () => {
expect(mesh.geometry.attributes.color.array[1]).toBe(0);
expect(mesh.geometry.attributes.color.array[2]).toBe(0);

// default status bar color
expect(mesh.geometry.attributes.color.array[3]).toBe(DEFAULT_STATUS_BAR_COLOR[0]);
expect(mesh.geometry.attributes.color.array[4]).toBe(DEFAULT_STATUS_BAR_COLOR[1]);
expect(mesh.geometry.attributes.color.array[5]).toBe(DEFAULT_STATUS_BAR_COLOR[2]);
// default status bar color 1
expect(mesh.geometry.attributes.color.array[3]).toBe(DEFAULT_STATUS_BAR_COLOR_1[0]);
expect(mesh.geometry.attributes.color.array[4]).toBe(DEFAULT_STATUS_BAR_COLOR_1[1]);
expect(mesh.geometry.attributes.color.array[5]).toBe(DEFAULT_STATUS_BAR_COLOR_1[2]);
});

it('updates the color buffer with the upper threshold when a positive point breached two threshold', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import statusFrag from './status.frag';
import { WriteableBufferAttribute, WriteableInstancedBufferAttribute } from '../../sc-webgl-context/types';
import { numDataPoints, vertices } from '../sc-webgl-base-chart/utils';
import { getCSSColorByString } from '../common/getCSSColorByString';
import { STATUS_MARGIN_TOP_PX, DEFAULT_STATUS_BAR_COLOR, HEIGHT } from './constants';
import { STATUS_MARGIN_TOP_PX, DEFAULT_STATUS_BAR_COLOR_1, DEFAULT_STATUS_BAR_COLOR_2, HEIGHT } from './constants';
import { getBreachedThreshold } from '../common/annotations/utils';
import { getDistanceFromDuration } from '../common/getDistanceFromDuration';
import { Threshold, ThresholdOptions } from '../common/types';
Expand Down Expand Up @@ -121,20 +121,34 @@ const updateMesh = ({
const vizHeight = rowHeight - margin;

streamVertexSets.forEach((streamVertexSet, setIndex) => {
let prevY: Primitive | undefined;
let currentDefaultGrayColor = DEFAULT_STATUS_BAR_COLOR_1;
streamVertexSet.forEach((currVertex, v) => {
const nextVertex = streamVertexSet[v + 1];
const [nextX = undefined] = nextVertex || [];
const [currX, currY] = currVertex;

if (prevY != null && prevY !== currY) {
// Swap grays if data value has changed since last point. Don't want to change color of the data point has the same value as the previous data point.
currentDefaultGrayColor =
currentDefaultGrayColor === DEFAULT_STATUS_BAR_COLOR_1
? DEFAULT_STATUS_BAR_COLOR_2
: DEFAULT_STATUS_BAR_COLOR_1;
}

/**
* Color Buffer Construction
*/

const breachedThreshold = getBreachedThreshold(currY, thresholds);

if (breachedThreshold == null || !thresholdOptions.showColor) {
const [r, g, b] = DEFAULT_STATUS_BAR_COLOR;
// Set status color to default gray (r, g, b)
const [r, g, b] = currentDefaultGrayColor;
// The status-timeline alternates between two grays as a default color.
// This provides visual contrast when the data value changes, without
// requiring a user to specify every threshold in advance.
// In certain use cases of the status-timeline, the values visualized cannot be known ahead of time,
// but users still need to be able to visually differentiate when they change.
color.array[colorIndex] = r;
color.array[colorIndex + 1] = g;
color.array[colorIndex + 2] = b;
Expand Down Expand Up @@ -169,6 +183,7 @@ const updateMesh = ({

// Increment Indexes by the associated stride of the buffer
statusIndex += NUM_STATUS_COMPONENTS;
prevY = currY;
});
});
status.needsUpdate = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class testingGrounds {
<div>
<h3>
hello, welcome to the{' '}
<a href="https://synchrocharts.com/" rel="noreferrer" target="_blank">
<a href="https://synchrocharts.com/" rel="noopener noreferrer" target="_blank">
synchro-charts
</a>{' '}
testing grounds!
Expand Down

0 comments on commit 94f6181

Please sign in to comment.