diff --git a/example/src/index.ts b/example/src/index.ts index b699f33..2499510 100644 --- a/example/src/index.ts +++ b/example/src/index.ts @@ -14,6 +14,7 @@ import { TestDataProvider } from "./test-data-provider"; import { TimeGraphChartGrid } from "timeline-chart/lib/layer/time-graph-chart-grid"; import { TimeGraphVerticalScrollbar } from "timeline-chart/lib/layer/time-graph-vertical-scrollbar"; import { TimeGraphChartArrows } from "timeline-chart/lib/layer/time-graph-chart-arrows"; +import { TimeGraphRangeEventsLayer } from "timeline-chart/lib/layer/time-graph-range-events-layer"; const styleConfig = { mainWidth: 1000, @@ -42,45 +43,7 @@ unitController.numberTranslator = (theNumber: number) => { return milli + ':' + micro + ':' + nano; }; -const rowHeight = 16; -const totalHeight = timeGraph.rows.length * rowHeight; -const rowController = new TimeGraphRowController(rowHeight, totalHeight); - -const axisHTMLContainer = document.createElement('div'); -axisHTMLContainer.id = 'main_axis'; -container.appendChild(axisHTMLContainer); - -const axisCanvas = document.createElement('canvas'); -const timeGraphAxisContainer = new TimeGraphContainer({ - height: 30, - width: styleConfig.mainWidth, - id: timeGraph.id + '_axis', - backgroundColor: 0xffffff -}, unitController, axisCanvas); -axisHTMLContainer.appendChild(timeGraphAxisContainer.canvas); - -const timeAxisLayer = new TimeGraphAxis('timeGraphAxis', { color: styleConfig.naviBackgroundColor }); -timeGraphAxisContainer.addLayer(timeAxisLayer); - -const chartHTMLContainer = document.createElement('div'); -chartHTMLContainer.id = 'main_chart'; -container.appendChild(chartHTMLContainer); - -const chartCanvas = document.createElement('canvas'); -chartCanvas.tabIndex = 1; - -const timeGraphChartContainer = new TimeGraphContainer({ - id: timeGraph.id + '_chart', - height: styleConfig.mainHeight, - width: styleConfig.mainWidth, - backgroundColor: styleConfig.chartBackgroundColor -}, unitController, chartCanvas); -chartHTMLContainer.appendChild(timeGraphChartContainer.canvas); - -const timeGraphChartGridLayer = new TimeGraphChartGrid('timeGraphGrid', rowHeight); -timeGraphChartContainer.addLayer(timeGraphChartGridLayer); - -const timeGraphChart = new TimeGraphChart('timeGraphChart', { +const providers = { dataProvider: (range: TimelineChart.TimeGraphRange, resolution: number) => { const length = range.end - range.start; const overlap = ((length * 20) - length) / 2; @@ -131,8 +94,53 @@ const timeGraphChart = new TimeGraphChart('timeGraphChart', { lineColor: row.data && row.data.hasStates ? 0xdddddd : 0xaa4444, lineThickness: row.data && row.data.hasStates ? 1 : 3 } + }, + rowAnnotationStyleProvider: (annotation: TimelineChart.TimeGraphAnnotation) => { + return { + color: 0xFF0000, size: 7, symbol: 'none', verticalAlign: 'middle', opacity: 0.2 + } } -}, rowController); +} + +const rowHeight = 16; +const totalHeight = timeGraph.rows.length * rowHeight; +const rowController = new TimeGraphRowController(rowHeight, totalHeight); + +const axisHTMLContainer = document.createElement('div'); +axisHTMLContainer.id = 'main_axis'; +container.appendChild(axisHTMLContainer); + +const axisCanvas = document.createElement('canvas'); +const timeGraphAxisContainer = new TimeGraphContainer({ + height: 30, + width: styleConfig.mainWidth, + id: timeGraph.id + '_axis', + backgroundColor: 0xffffff +}, unitController, axisCanvas); +axisHTMLContainer.appendChild(timeGraphAxisContainer.canvas); + +const timeAxisLayer = new TimeGraphAxis('timeGraphAxis', { color: styleConfig.naviBackgroundColor }); +timeGraphAxisContainer.addLayer(timeAxisLayer); + +const chartHTMLContainer = document.createElement('div'); +chartHTMLContainer.id = 'main_chart'; +container.appendChild(chartHTMLContainer); + +const chartCanvas = document.createElement('canvas'); +chartCanvas.tabIndex = 1; + +const timeGraphChartContainer = new TimeGraphContainer({ + id: timeGraph.id + '_chart', + height: styleConfig.mainHeight, + width: styleConfig.mainWidth, + backgroundColor: styleConfig.chartBackgroundColor +}, unitController, chartCanvas); +chartHTMLContainer.appendChild(timeGraphChartContainer.canvas); + +const timeGraphChartGridLayer = new TimeGraphChartGrid('timeGraphGrid', rowHeight); +timeGraphChartContainer.addLayer(timeGraphChartGridLayer); + +const timeGraphChart = new TimeGraphChart('timeGraphChart', providers, rowController); timeGraphChartContainer.addLayer(timeGraphChart); timeGraphChart.registerStateMouseInteractions({ @@ -154,6 +162,9 @@ const timeGraphSelectionRange = new TimeGraphChartSelectionRange('chart-selectio timeGraphChartContainer.addLayer(timeGraphSelectionRange); const timeGraphChartCursors = new TimeGraphChartCursors('chart-cursors', timeGraphChart, rowController, { color: styleConfig.cursorColor }); timeGraphChartContainer.addLayer(timeGraphChartCursors); +const timeGraphChartRangeEvents = new TimeGraphRangeEventsLayer('timeGraphChartRangeEvents', providers); +timeGraphChartContainer.addLayer(timeGraphChartRangeEvents); +timeGraphChartRangeEvents.addRangeEvents(timeGraph.rangeEvents); const cursorReset = document.getElementById('cursor-reset'); if (cursorReset) { diff --git a/example/src/test-data-provider.ts b/example/src/test-data-provider.ts index 089b859..05c6ac5 100644 --- a/example/src/test-data-provider.ts +++ b/example/src/test-data-provider.ts @@ -185,6 +185,24 @@ export class TestDataProvider { const rows: TimelineChart.TimeGraphRowModel[] = []; const range = opts.range || { start: 0, end: this.totalLength }; const resolution = opts.resolution || this.totalLength / this.canvasDisplayWidth; + const commonRow = timeGraphStates.model.rows.find(row => row.entryId === -1); + const _rangeEvents = commonRow?.annotations; + const rangeEvents: TimelineChart.TimeGraphAnnotation[] = []; + const startTime = 1332170682440133097; + _rangeEvents?.forEach((annotation: any, annotationIndex: number) => { + const start = annotation.range.start - startTime; + if (range.start < start && range.end > start) { + rangeEvents.push({ + id: 'mark_' + -1 + '_' + annotationIndex, + range: { + start: annotation.range.start - this.absoluteStart, + end: annotation.range.end - this.absoluteStart + }, + label: '', + }); + } + }); + timeGraphEntries.model.entries.forEach((entry: any, rowIndex: number): void => { const states: TimelineChart.TimeGraphState[] = []; const annotations: TimelineChart.TimeGraphAnnotation[] = []; @@ -266,6 +284,7 @@ export class TestDataProvider { id: "", arrows, rows, + rangeEvents, totalLength: this.totalLength }; } diff --git a/example/src/test-states.ts b/example/src/test-states.ts index 1ba186e..8a2864d 100644 --- a/example/src/test-states.ts +++ b/example/src/test-states.ts @@ -8868,6 +8868,18 @@ export const timeGraphStates = { "value": -2147483648 } ] + }, + { + "entryId": -1, + "annotations": [ + { + "range": { + "start": 1332170682486039800, + "end": 1332170682488039800 + }, + } + ], + "states": [] } ] }, diff --git a/timeline-chart/src/components/time-graph-annotation.ts b/timeline-chart/src/components/time-graph-annotation.ts index 28e5c61..de9aaca 100644 --- a/timeline-chart/src/components/time-graph-annotation.ts +++ b/timeline-chart/src/components/time-graph-annotation.ts @@ -11,6 +11,7 @@ export interface TimeGraphAnnotationStyle extends TimeGraphComponentOptions { size?: number color?: number verticalAlign?: string + opacity?:number } /* diff --git a/timeline-chart/src/components/time-graph-rectangle.ts b/timeline-chart/src/components/time-graph-rectangle.ts index 9e2a6ca..63f11e2 100644 --- a/timeline-chart/src/components/time-graph-rectangle.ts +++ b/timeline-chart/src/components/time-graph-rectangle.ts @@ -1,16 +1,24 @@ import { TimeGraphComponent, TimeGraphStyledRect } from "./time-graph-component"; export class TimeGraphRectangle extends TimeGraphComponent { - constructor(protected opts: TimeGraphStyledRect){ + protected _options: TimeGraphStyledRect; + + constructor(protected opts: TimeGraphStyledRect) { super('rect'); this._options = opts; } - get rectOptions(): TimeGraphStyledRect { - return this._options as TimeGraphStyledRect; + update(opts?: TimeGraphStyledRect): void { + + if (opts) { + this._options.width = opts.width; + this._options.height = opts.height; + this._options.position = opts.position; + } + super.update(); } render(): void { - this.rect(this._options as TimeGraphStyledRect); + this.rect(this._options); } } \ No newline at end of file diff --git a/timeline-chart/src/layer/time-graph-chart-selection-range.ts b/timeline-chart/src/layer/time-graph-chart-selection-range.ts index 2445ca2..2f7dd66 100644 --- a/timeline-chart/src/layer/time-graph-chart-selection-range.ts +++ b/timeline-chart/src/layer/time-graph-chart-selection-range.ts @@ -17,9 +17,16 @@ export class TimeGraphChartSelectionRange extends TimeGraphLayer { protected updateScaleAndPosition() { if (this.unitController.selectionRange && this.selectionRange) { - this.selectionRange.rectOptions.position.x = this.getPixels(this.unitController.selectionRange.start - this.unitController.viewRange.start); - this.selectionRange.rectOptions.width = this.getPixels(this.unitController.selectionRange.end - this.unitController.selectionRange.start) - this.selectionRange.update(); + const firstCursorPosition = this.getPixels(this.unitController.selectionRange.start - this.unitController.viewRange.start); + const width = this.getPixels(this.unitController.selectionRange.end - this.unitController.selectionRange.start) + this.selectionRange.update({ + position: { + x: firstCursorPosition, + y: 0 + }, + height: this.stateController.canvasDisplayHeight, + width + }); } } @@ -58,8 +65,6 @@ export class TimeGraphChartSelectionRange extends TimeGraphLayer { this.addChild(this.selectionRange); } else { this.selectionRange.update({ - color: this.color, - opacity: 0.2, position: { x: firstCursorPosition, y: 0 diff --git a/timeline-chart/src/layer/time-graph-layer.ts b/timeline-chart/src/layer/time-graph-layer.ts index 68a6891..640d953 100644 --- a/timeline-chart/src/layer/time-graph-layer.ts +++ b/timeline-chart/src/layer/time-graph-layer.ts @@ -21,7 +21,7 @@ export abstract class TimeGraphLayer { if (!this.canvas) { throw ("Layers must be added to a container before components can be added."); } - child.render(); + child.update(); if (parent) { parent.addChild(child); } else { diff --git a/timeline-chart/src/layer/time-graph-range-events-layer.ts b/timeline-chart/src/layer/time-graph-range-events-layer.ts new file mode 100644 index 0000000..ae249cc --- /dev/null +++ b/timeline-chart/src/layer/time-graph-range-events-layer.ts @@ -0,0 +1,87 @@ +import { TimeGraphRectangle } from "../components/time-graph-rectangle"; +import { TimelineChart } from "../time-graph-model"; +import { TimeGraphChartProviders } from "./time-graph-chart"; +import { TimeGraphLayer } from "./time-graph-layer"; + +export class TimeGraphRangeEventsLayer extends TimeGraphLayer { + protected rangeEvents: Map; + protected providers: TimeGraphChartProviders; + + private _viewRangeUpdateHandler: { (): void; (viewRange: TimelineChart.TimeGraphRange): void; (viewRange: TimelineChart.TimeGraphRange): void; }; + private _updateHandler: { (): void; (selectionRange: TimelineChart.TimeGraphRange): void; (selectionRange: TimelineChart.TimeGraphRange): void; }; + + constructor(id: string, providers: TimeGraphChartProviders) { + super(id); + this.providers = providers; + } + + protected afterAddToContainer() { + this._updateHandler = (): void => this.update(); + this.unitController.onViewRangeChanged(this._updateHandler); + } + + protected addRangeEvent(rangeEvent: TimelineChart.TimeGraphAnnotation) { + const start = this.getPixels(rangeEvent.range.start - this.unitController.viewRange.start); + const end = this.getPixels(rangeEvent.range.end - this.unitController.viewRange.start); + const width = end - start; + const elementStyle = this.providers.rowAnnotationStyleProvider ? this.providers.rowAnnotationStyleProvider(rangeEvent) : undefined; + const rangeEventComponent = new TimeGraphRectangle({ + color: (elementStyle && elementStyle.color) || 0xFF0000, + opacity: (elementStyle && elementStyle.opacity) || 0.2, + position: { + x: start, + y: 0 + }, + height: this.stateController.canvasDisplayHeight, + width + }); + this.rangeEvents.set(rangeEvent, rangeEventComponent); + this.addChild(rangeEventComponent); + } + + addRangeEvents(rangeEvents: TimelineChart.TimeGraphAnnotation[]): void { + if (!this.stateController) { + throw ('Add the TimeGraphRangeEventsLayer to a container before adding range events.'); + } + if (this.rangeEvents) { + this.removeChildren(); + } + this.rangeEvents = new Map(); + rangeEvents.forEach(range => { + this.addRangeEvent(range); + }) + } + + update(): void { + if (this.rangeEvents) { + for (const range of this.rangeEvents.keys()) { + this.updateRangeEvent(range); + } + } + } + + protected updateRangeEvent(rangeEvent: TimelineChart.TimeGraphAnnotation) { + const rangeEventComponent = this.rangeEvents.get(rangeEvent); + const start = this.getPixels(rangeEvent.range.start - this.unitController.viewRange.start); + const end = this.getPixels(rangeEvent.range.end - this.unitController.viewRange.start); + const width = end - start; + if (rangeEventComponent) { + rangeEventComponent.update({ + position: { + x: start, + y: 0 + }, + height: this.stateController.canvasDisplayHeight, + width + }); + } + } + + destroy(): void { + if (this.unitController) { + this.unitController.removeViewRangeChangedHandler(this._viewRangeUpdateHandler); + this.unitController.removeSelectionRangeChangedHandler(this._updateHandler); + } + super.destroy(); + } +} \ No newline at end of file diff --git a/timeline-chart/src/time-graph-container.ts b/timeline-chart/src/time-graph-container.ts index 906b6a0..d44bcc3 100644 --- a/timeline-chart/src/time-graph-container.ts +++ b/timeline-chart/src/time-graph-container.ts @@ -91,9 +91,13 @@ export class TimeGraphContainer { this.application.renderer.resize(newWidth, newHeight); this.stateController.updateDisplayWidth(); this.stateController.updateDisplayHeight(); - this.background.displayObject.width = newWidth; - this.background.displayObject.height = newHeight; - this.background.update(); + this.background.update({ + position: { + x: 0, y: 0 + }, + height: newHeight, + width: newWidth + }); this.layers.forEach(l => l.update()); } diff --git a/timeline-chart/src/time-graph-model.ts b/timeline-chart/src/time-graph-model.ts index d1b127c..9f14e8f 100644 --- a/timeline-chart/src/time-graph-model.ts +++ b/timeline-chart/src/time-graph-model.ts @@ -8,6 +8,7 @@ export namespace TimelineChart { id: string totalLength: number rows: TimeGraphRowModel[] + rangeEvents: TimeGraphAnnotation[] arrows: TimeGraphArrow[] readonly data?: { [key: string]: any } }