Commit f57ed509fc480e94a4fa4dc649032f742317f278
Committed by
GitHub
Merge pull request #2507 from vvlladd28/feature/digital-gauge/ticks
[2.5] Add support ticks to digital gauge
Showing
2 changed files
with
263 additions
and
70 deletions
... | ... | @@ -51,6 +51,10 @@ let defaultDigitalGaugeOptions = Object.assign({}, canvasGauges.GenericOptions, |
51 | 51 | |
52 | 52 | neonGlowBrightness: 0, |
53 | 53 | |
54 | + colorTicks: 'gray', | |
55 | + tickWidth: 4, | |
56 | + ticks: [], | |
57 | + | |
54 | 58 | isMobile: false |
55 | 59 | |
56 | 60 | }); |
... | ... | @@ -133,6 +137,13 @@ export default class CanvasDigitalGauge extends canvasGauges.BaseGauge { |
133 | 137 | } |
134 | 138 | } |
135 | 139 | |
140 | + options.ticksValue = []; | |
141 | + for(let i = 0; i < options.ticks.length; i++){ | |
142 | + if(options.ticks[i] !== null){ | |
143 | + options.ticksValue.push(CanvasDigitalGauge.normalizeValue(options.ticks[i], options.minValue, options.maxValue)) | |
144 | + } | |
145 | + } | |
146 | + | |
136 | 147 | if (options.neonGlowBrightness) { |
137 | 148 | options.neonColorTitle = tinycolor(options.colorTitle).brighten(options.neonGlowBrightness).toHexString(); |
138 | 149 | options.neonColorLabel = tinycolor(options.colorLabel).brighten(options.neonGlowBrightness).toHexString(); |
... | ... | @@ -729,6 +740,48 @@ function drawBarGlow(context, startX, startY, endX, endY, color, strokeWidth, is |
729 | 740 | context.stroke(); |
730 | 741 | } |
731 | 742 | |
743 | +function drawTickArc(context, tickValues, Cx, Cy, Ri, Rm, Ro, startAngle, endAngle, color, tickWidth) { | |
744 | + if(!tickValues.length) { | |
745 | + return; | |
746 | + } | |
747 | + | |
748 | + const strokeWidth = Ro - Ri; | |
749 | + context.beginPath(); | |
750 | + context.lineWidth = tickWidth; | |
751 | + context.strokeStyle = color; | |
752 | + for (let i = 0; i < tickValues.length; i++) { | |
753 | + var angle = startAngle + tickValues[i] * endAngle; | |
754 | + var x1 = Cx + (Ri + strokeWidth) * Math.cos(angle); | |
755 | + var y1 = Cy + (Ri + strokeWidth) * Math.sin(angle); | |
756 | + var x2 = Cx + Ri * Math.cos(angle); | |
757 | + var y2 = Cy + Ri * Math.sin(angle); | |
758 | + context.moveTo(x1, y1); | |
759 | + context.lineTo(x2, y2); | |
760 | + } | |
761 | + context.stroke(); | |
762 | +} | |
763 | + | |
764 | +function drawTickBar(context, tickValues, startX, startY, distanceBar, strokeWidth, isVertical, color, tickWidth) { | |
765 | + if(!tickValues.length) { | |
766 | + return; | |
767 | + } | |
768 | + | |
769 | + context.beginPath(); | |
770 | + context.lineWidth = tickWidth; | |
771 | + context.strokeStyle = color; | |
772 | + for (let i = 0; i < tickValues.length; i++) { | |
773 | + let tickValue = tickValues[i] * distanceBar; | |
774 | + if (isVertical) { | |
775 | + context.moveTo(startX - strokeWidth / 2, startY + tickValue - distanceBar); | |
776 | + context.lineTo(startX + strokeWidth / 2, startY + tickValue - distanceBar); | |
777 | + } else { | |
778 | + context.moveTo(startX + tickValue, startY); | |
779 | + context.lineTo(startX + tickValue, startY + strokeWidth); | |
780 | + } | |
781 | + } | |
782 | + context.stroke(); | |
783 | +} | |
784 | + | |
732 | 785 | function drawProgress(context, options, progress) { |
733 | 786 | var neonColor; |
734 | 787 | if (options.neonGlowBrightness) { |
... | ... | @@ -759,6 +812,7 @@ function drawProgress(context, options, progress) { |
759 | 812 | if (options.neonGlowBrightness && !options.isMobile) { |
760 | 813 | drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, true, options.donutStartAngle, options.donutEndAngle); |
761 | 814 | } |
815 | + drawTickArc(context, options.ticksValue, Cx, Cy, Ri, Rm, Ro, options.donutStartAngle, options.donutEndAngle - options.donutStartAngle, options.colorTicks, options.tickWidth); | |
762 | 816 | } else if (options.gaugeType === 'arc') { |
763 | 817 | if (options.neonGlowBrightness) { |
764 | 818 | context.strokeStyle = neonColor; |
... | ... | @@ -769,6 +823,7 @@ function drawProgress(context, options, progress) { |
769 | 823 | if (options.neonGlowBrightness && !options.isMobile) { |
770 | 824 | drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, false); |
771 | 825 | } |
826 | + drawTickArc(context, options.ticksValue, Cx, Cy, Ri, Rm, Ro, Math.PI, Math.PI, options.colorTicks, options.tickWidth); | |
772 | 827 | } else if (options.gaugeType === 'horizontalBar') { |
773 | 828 | if (options.neonGlowBrightness) { |
774 | 829 | context.strokeStyle = neonColor; |
... | ... | @@ -781,6 +836,7 @@ function drawProgress(context, options, progress) { |
781 | 836 | drawBarGlow(context, barLeft, barTop + strokeWidth/2, barLeft + (barRight-barLeft)*progress, barTop + strokeWidth/2, |
782 | 837 | neonColor, strokeWidth, false); |
783 | 838 | } |
839 | + drawTickBar(context, options.ticksValue, barLeft, barTop, barRight - barLeft, strokeWidth, false, options.colorTicks, options.tickWidth); | |
784 | 840 | } else if (options.gaugeType === 'verticalBar') { |
785 | 841 | if (options.neonGlowBrightness) { |
786 | 842 | context.strokeStyle = neonColor; |
... | ... | @@ -793,6 +849,7 @@ function drawProgress(context, options, progress) { |
793 | 849 | drawBarGlow(context, baseX + width/2, barBottom, baseX + width/2, barBottom - (barBottom-barTop)*progress, |
794 | 850 | neonColor, strokeWidth, true); |
795 | 851 | } |
852 | + drawTickBar(context, options.ticksValue, baseX + width / 2, barTop, barTop - barBottom, strokeWidth, true, options.colorTicks, options.tickWidth); | |
796 | 853 | } |
797 | 854 | } |
798 | 855 | ... | ... |
... | ... | @@ -62,6 +62,12 @@ export default class TbCanvasDigitalGauge { |
62 | 62 | this.localSettings.fixedLevelColors = settings.fixedLevelColors || []; |
63 | 63 | } |
64 | 64 | |
65 | + this.localSettings.showTicks = settings.showTicks || false; | |
66 | + this.localSettings.ticks = []; | |
67 | + this.localSettings.ticksValue = settings.ticksValue || []; | |
68 | + this.localSettings.tickWidth = settings.tickWidth || 4; | |
69 | + this.localSettings.colorTicks = settings.colorTicks || '#666'; | |
70 | + | |
65 | 71 | this.localSettings.decimals = angular.isDefined(dataKey.decimals) ? dataKey.decimals : |
66 | 72 | ((angular.isDefined(settings.decimals) && settings.decimals !== null) |
67 | 73 | ? settings.decimals : ctx.decimals); |
... | ... | @@ -145,6 +151,10 @@ export default class TbCanvasDigitalGauge { |
145 | 151 | gaugeColor: this.localSettings.gaugeColor, |
146 | 152 | levelColors: this.localSettings.levelColors, |
147 | 153 | |
154 | + colorTicks: this.localSettings.colorTicks, | |
155 | + tickWidth: this.localSettings.tickWidth, | |
156 | + ticks: this.localSettings.ticks, | |
157 | + | |
148 | 158 | title: this.localSettings.title, |
149 | 159 | |
150 | 160 | fontTitleSize: this.localSettings.titleFont.size, |
... | ... | @@ -204,9 +214,81 @@ export default class TbCanvasDigitalGauge { |
204 | 214 | if (this.localSettings.useFixedLevelColor) { |
205 | 215 | if (this.localSettings.fixedLevelColors && this.localSettings.fixedLevelColors.length > 0) { |
206 | 216 | this.localSettings.levelColors = this.settingLevelColorsSubscribe(this.localSettings.fixedLevelColors); |
207 | - this.updateLevelColors(this.localSettings.levelColors); | |
208 | 217 | } |
209 | 218 | } |
219 | + if (this.localSettings.showTicks) { | |
220 | + if (this.localSettings.ticksValue && this.localSettings.ticksValue.length) { | |
221 | + this.localSettings.ticks = this.settingTicksSubscribe(this.localSettings.ticksValue); | |
222 | + } | |
223 | + } | |
224 | + this.updateSetting(); | |
225 | + } | |
226 | + | |
227 | + static generateDatasorce(ctx, datasources, entityAlias, attribute, settings){ | |
228 | + let entityAliasId = ctx.aliasController.getEntityAliasId(entityAlias); | |
229 | + if (!entityAliasId) { | |
230 | + throw new Error('Not valid entity aliase name ' + entityAlias); | |
231 | + } | |
232 | + | |
233 | + let datasource = datasources.filter((datasource) => { | |
234 | + return datasource.entityAliasId === entityAliasId; | |
235 | + })[0]; | |
236 | + | |
237 | + let dataKey = { | |
238 | + type: ctx.$scope.$injector.get('types').dataKeyType.attribute, | |
239 | + name: attribute, | |
240 | + label: attribute, | |
241 | + settings: [settings], | |
242 | + _hash: Math.random() | |
243 | + }; | |
244 | + | |
245 | + if (datasource) { | |
246 | + let findDataKey = datasource.dataKeys.filter((dataKey) => { | |
247 | + return dataKey.name === attribute; | |
248 | + })[0]; | |
249 | + | |
250 | + if (findDataKey) { | |
251 | + findDataKey.settings.push(settings); | |
252 | + } else { | |
253 | + datasource.dataKeys.push(dataKey) | |
254 | + } | |
255 | + } else { | |
256 | + datasource = { | |
257 | + type: ctx.$scope.$injector.get('types').datasourceType.entity, | |
258 | + name: entityAlias, | |
259 | + aliasName: entityAlias, | |
260 | + entityAliasId: entityAliasId, | |
261 | + dataKeys: [dataKey] | |
262 | + }; | |
263 | + datasources.push(datasource); | |
264 | + } | |
265 | + | |
266 | + return datasources; | |
267 | + } | |
268 | + | |
269 | + settingTicksSubscribe(options) { | |
270 | + let ticksDatasource = []; | |
271 | + let predefineTicks = []; | |
272 | + | |
273 | + for (let i = 0; i < options.length; i++) { | |
274 | + let tick = options[i]; | |
275 | + if (tick.valueSource === 'predefinedValue' && isFinite(tick.value)) { | |
276 | + predefineTicks.push(tick.value) | |
277 | + } else if (tick.entityAlias && tick.attribute) { | |
278 | + try { | |
279 | + ticksDatasource = TbCanvasDigitalGauge.generateDatasorce(this.ctx, ticksDatasource, tick.entityAlias, tick.attribute, predefineTicks.length); | |
280 | + } catch (e) { | |
281 | + continue; | |
282 | + } | |
283 | + predefineTicks.push(null); | |
284 | + } | |
285 | + } | |
286 | + | |
287 | + this.subscribeAttributes(ticksDatasource, 'ticks').then((subscription) => { | |
288 | + this.ticksSourcesSubscription = subscription; | |
289 | + }); | |
290 | + | |
291 | + return predefineTicks; | |
210 | 292 | } |
211 | 293 | |
212 | 294 | settingLevelColorsSubscribe(options) { |
... | ... | @@ -220,50 +302,14 @@ export default class TbCanvasDigitalGauge { |
220 | 302 | color: color |
221 | 303 | }) |
222 | 304 | } else if (levelSetting.entityAlias && levelSetting.attribute) { |
223 | - let entityAliasId = this.ctx.aliasController.getEntityAliasId(levelSetting.entityAlias); | |
224 | - if (!entityAliasId) { | |
225 | - return; | |
226 | - } | |
227 | - | |
228 | - let datasource = levelColorsDatasource.filter((datasource) => { | |
229 | - return datasource.entityAliasId === entityAliasId; | |
230 | - })[0]; | |
231 | - | |
232 | - let dataKey = { | |
233 | - type: this.ctx.$scope.$injector.get('types').dataKeyType.attribute, | |
234 | - name: levelSetting.attribute, | |
235 | - label: levelSetting.attribute, | |
236 | - settings: [{ | |
305 | + try { | |
306 | + levelColorsDatasource = TbCanvasDigitalGauge.generateDatasorce(this.ctx, levelColorsDatasource, levelSetting.entityAlias, levelSetting.attribute, { | |
237 | 307 | color: color, |
238 | 308 | index: predefineLevelColors.length |
239 | - }], | |
240 | - _hash: Math.random() | |
241 | - }; | |
242 | - | |
243 | - if (datasource) { | |
244 | - let findDataKey = datasource.dataKeys.filter((dataKey) => { | |
245 | - return dataKey.name === levelSetting.attribute; | |
246 | - })[0]; | |
247 | - | |
248 | - if (findDataKey) { | |
249 | - findDataKey.settings.push({ | |
250 | - color: color, | |
251 | - index: predefineLevelColors.length | |
252 | - }); | |
253 | - } else { | |
254 | - datasource.dataKeys.push(dataKey) | |
255 | - } | |
256 | - } else { | |
257 | - datasource = { | |
258 | - type: this.ctx.$scope.$injector.get('types').datasourceType.entity, | |
259 | - name: levelSetting.entityAlias, | |
260 | - aliasName: levelSetting.entityAlias, | |
261 | - entityAliasId: entityAliasId, | |
262 | - dataKeys: [dataKey] | |
263 | - }; | |
264 | - levelColorsDatasource.push(datasource); | |
309 | + }); | |
310 | + } catch (e) { | |
311 | + return; | |
265 | 312 | } |
266 | - | |
267 | 313 | predefineLevelColors.push(null); |
268 | 314 | } |
269 | 315 | } |
... | ... | @@ -278,49 +324,63 @@ export default class TbCanvasDigitalGauge { |
278 | 324 | } |
279 | 325 | } |
280 | 326 | |
281 | - this.subscribeLevelColorsAttributes(levelColorsDatasource); | |
327 | + this.subscribeAttributes(levelColorsDatasource, 'levelColors').then((subscription) => { | |
328 | + this.levelColorSourcesSubscription = subscription; | |
329 | + }); | |
282 | 330 | |
283 | 331 | return predefineLevelColors; |
284 | 332 | } |
285 | 333 | |
286 | - updateLevelColors(levelColors) { | |
287 | - this.gauge.options.levelColors = levelColors; | |
288 | - this.gauge.options = CanvasDigitalGauge.configure(this.gauge.options); | |
289 | - this.gauge.update(); | |
290 | - } | |
334 | + subscribeAttributes(datasources, typeAttributes) { | |
335 | + if (!datasources.length) { | |
336 | + return this.ctx.$scope.$injector.get('$q').when(null); | |
337 | + } | |
291 | 338 | |
292 | - subscribeLevelColorsAttributes(datasources) { | |
293 | - let TbCanvasDigitalGauge = this; | |
294 | 339 | let levelColorsSourcesSubscriptionOptions = { |
295 | 340 | datasources: datasources, |
296 | 341 | useDashboardTimewindow: false, |
297 | 342 | type: this.ctx.$scope.$injector.get('types').widgetType.latest.value, |
298 | 343 | callbacks: { |
299 | 344 | onDataUpdated: (subscription) => { |
300 | - for (let i = 0; i < subscription.data.length; i++) { | |
301 | - let keyData = subscription.data[i]; | |
302 | - if (keyData && keyData.data && keyData.data[0]) { | |
303 | - let attrValue = keyData.data[0][1]; | |
304 | - if (isFinite(attrValue)) { | |
305 | - for (let i = 0; i < keyData.dataKey.settings.length; i++) { | |
306 | - let setting = keyData.dataKey.settings[i]; | |
307 | - this.localSettings.levelColors[setting.index] = { | |
308 | - value: attrValue, | |
309 | - color: setting.color | |
310 | - }; | |
311 | - } | |
312 | - } | |
313 | - } | |
314 | - } | |
315 | - this.updateLevelColors(this.localSettings.levelColors); | |
345 | + this.updateAttribute(subscription.data, typeAttributes); | |
316 | 346 | } |
317 | 347 | } |
318 | 348 | }; |
319 | - this.ctx.subscriptionApi.createSubscription(levelColorsSourcesSubscriptionOptions, true).then( | |
320 | - (subscription) => { | |
321 | - TbCanvasDigitalGauge.levelColorSourcesSubscription = subscription; | |
349 | + | |
350 | + return this.ctx.subscriptionApi.createSubscription(levelColorsSourcesSubscriptionOptions, true); | |
351 | + } | |
352 | + | |
353 | + updateAttribute(data, typeAttributes) { | |
354 | + for (let i = 0; i < data.length; i++) { | |
355 | + let keyData = data[i]; | |
356 | + if (keyData && keyData.data && keyData.data[0]) { | |
357 | + let attrValue = keyData.data[0][1]; | |
358 | + if (isFinite(attrValue)) { | |
359 | + for (let i = 0; i < keyData.dataKey.settings.length; i++) { | |
360 | + let setting = keyData.dataKey.settings[i]; | |
361 | + switch (typeAttributes) { | |
362 | + case 'levelColors': | |
363 | + this.localSettings.levelColors[setting.index] = { | |
364 | + value: attrValue, | |
365 | + color: setting.color | |
366 | + }; | |
367 | + break; | |
368 | + case 'ticks': | |
369 | + this.localSettings.ticks[setting] = attrValue; | |
370 | + break; | |
371 | + } | |
372 | + } | |
373 | + } | |
322 | 374 | } |
323 | - ); | |
375 | + } | |
376 | + this.updateSetting(); | |
377 | + } | |
378 | + | |
379 | + updateSetting() { | |
380 | + this.gauge.options.ticks = this.localSettings.ticks; | |
381 | + this.gauge.options.levelColors = this.localSettings.levelColors; | |
382 | + this.gauge.options = CanvasDigitalGauge.configure(this.gauge.options); | |
383 | + this.gauge.update(); | |
324 | 384 | } |
325 | 385 | |
326 | 386 | update() { |
... | ... | @@ -526,6 +586,48 @@ export default class TbCanvasDigitalGauge { |
526 | 586 | } |
527 | 587 | } |
528 | 588 | }, |
589 | + "showTicks": { | |
590 | + "title": "Show ticks", | |
591 | + "type": "boolean", | |
592 | + "default": false | |
593 | + }, | |
594 | + "tickWidth": { | |
595 | + "title": "Width ticks", | |
596 | + "type": "number", | |
597 | + "default": 4 | |
598 | + }, | |
599 | + "colorTicks": { | |
600 | + "title": "Color ticks", | |
601 | + "type": "string", | |
602 | + "default": "#666" | |
603 | + }, | |
604 | + "ticksValue": { | |
605 | + "title": "The ticks predefined value", | |
606 | + "type": "array", | |
607 | + "items": { | |
608 | + "title": "tickValue", | |
609 | + "type": "object", | |
610 | + "properties": { | |
611 | + "valueSource": { | |
612 | + "title": "Value source", | |
613 | + "type": "string", | |
614 | + "default": "predefinedValue" | |
615 | + }, | |
616 | + "entityAlias": { | |
617 | + "title": "Source entity alias", | |
618 | + "type": "string" | |
619 | + }, | |
620 | + "attribute": { | |
621 | + "title": "Source entity attribute", | |
622 | + "type": "string" | |
623 | + }, | |
624 | + "value": { | |
625 | + "title": "Value (if predefined value is selected)", | |
626 | + "type": "number" | |
627 | + } | |
628 | + } | |
629 | + } | |
630 | + }, | |
529 | 631 | "animation": { |
530 | 632 | "title": "Enable animation", |
531 | 633 | "type": "boolean", |
... | ... | @@ -771,6 +873,40 @@ export default class TbCanvasDigitalGauge { |
771 | 873 | } |
772 | 874 | ] |
773 | 875 | }, |
876 | + "showTicks", | |
877 | + { | |
878 | + "key": "tickWidth", | |
879 | + "condition": "model.showTicks === true" | |
880 | + }, | |
881 | + { | |
882 | + "key": "colorTicks", | |
883 | + "condition": "model.showTicks === true", | |
884 | + "type": "color" | |
885 | + }, | |
886 | + { | |
887 | + "key": "ticksValue", | |
888 | + "condition": "model.showTicks === true", | |
889 | + "items": [ | |
890 | + { | |
891 | + "key": "ticksValue[].valueSource", | |
892 | + "type": "rc-select", | |
893 | + "multiple": false, | |
894 | + "items": [ | |
895 | + { | |
896 | + "value": "predefinedValue", | |
897 | + "label": "Predefined value (Default)" | |
898 | + }, | |
899 | + { | |
900 | + "value": "entityAttribute", | |
901 | + "label": "Value taken from entity attribute" | |
902 | + } | |
903 | + ] | |
904 | + }, | |
905 | + "ticksValue[].value", | |
906 | + "ticksValue[].entityAlias", | |
907 | + "ticksValue[].attribute" | |
908 | + ] | |
909 | + }, | |
774 | 910 | "animation", |
775 | 911 | "animationDuration", |
776 | 912 | { | ... | ... |