mirror of
				https://github.com/ClusterCockpit/cc-backend
				synced 2025-10-26 14:25:06 +01:00 
			
		
		
		
	Fix small oversight. remove wip plot component
This commit is contained in:
		| @@ -4,7 +4,7 @@ Originally created by Michael Keller (https://github.com/mhkeller/svelte-double- | ||||
| Changes: remove dependency, text inputs, configurable value ranges, on:change event | ||||
| --> | ||||
| <!--  | ||||
| 	@component | ||||
| 	@component Helper component to display range selections via min and max double-sliders | ||||
|  | ||||
| 	Properties: | ||||
| 	- min:          Number | ||||
|   | ||||
| @@ -1,627 +0,0 @@ | ||||
| <script> | ||||
|     import Quadtree from '@timohausmann/quadtree-js' // https://github.com/timohausmann/quadtree-js | ||||
|     import uPlot from 'uplot' | ||||
|     import { formatNumber } from '../units.js' | ||||
|     import { onMount, onDestroy } from 'svelte' | ||||
|     import { Card } from 'sveltestrap' | ||||
|  | ||||
|     let plotWrapper = null | ||||
|     let uplot = null | ||||
|     let timeoutId = null | ||||
|  | ||||
|     function randInt(min, max) { | ||||
|         return Math.floor(Math.random() * (max - min + 1)) + min; | ||||
|     } | ||||
|  | ||||
|     function filledArr(len, val) { | ||||
|         let arr = Array(len); | ||||
|  | ||||
|         if (typeof val == "function") { | ||||
|             for (let i = 0; i < len; ++i) | ||||
|                 arr[i] = val(i); | ||||
|         } | ||||
|         else { | ||||
|             for (let i = 0; i < len; ++i) | ||||
|                 arr[i] = val; | ||||
|         } | ||||
|  | ||||
|         return arr; | ||||
|     } | ||||
|  | ||||
|     let points = 10000; | ||||
|     let series = 5; | ||||
|  | ||||
|     console.time("prep"); | ||||
|  | ||||
|     let data = filledArr(series, v => [ | ||||
|         filledArr(points, i => randInt(0,500)), | ||||
|         filledArr(points, i => randInt(0,500)), | ||||
|     ]); | ||||
|  | ||||
|     data[0] = null; | ||||
|  | ||||
|     console.timeEnd("prep"); | ||||
|  | ||||
|     console.log(data); | ||||
|  | ||||
|     const drawPoints = (u, seriesIdx, idx0, idx1) => { | ||||
|         const size = 5 * devicePixelRatio; | ||||
|  | ||||
|         uPlot.orient(u, seriesIdx, (series, dataX, dataY, scaleX, scaleY, valToPosX, valToPosY, xOff, yOff, xDim, yDim, moveTo, lineTo, rect, arc) => { | ||||
|             let d = u.data[seriesIdx]; | ||||
|  | ||||
|             u.ctx.fillStyle = series.stroke(); | ||||
|  | ||||
|             let deg360 = 2 * Math.PI; | ||||
|  | ||||
|             console.time("points"); | ||||
|  | ||||
|         //	let cir = new Path2D(); | ||||
|         //	cir.moveTo(0, 0); | ||||
|         //	arc(cir, 0, 0, 3, 0, deg360); | ||||
|  | ||||
|             // Create transformation matrix that moves 200 points to the right | ||||
|         //	let m = document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGMatrix(); | ||||
|         //	m.a = 1;   m.b = 0; | ||||
|         //	m.c = 0;   m.d = 1; | ||||
|         //	m.e = 200; m.f = 0; | ||||
|  | ||||
|  | ||||
|             let p = new Path2D(); | ||||
|  | ||||
|             for (let i = 0; i < d[0].length; i++) { | ||||
|                 let xVal = d[0][i]; | ||||
|                 let yVal = d[1][i]; | ||||
|  | ||||
|                 if (xVal >= scaleX.min && xVal <= scaleX.max && yVal >= scaleY.min && yVal <= scaleY.max) { | ||||
|                     let cx = valToPosX(xVal, scaleX, xDim, xOff); | ||||
|                     let cy = valToPosY(yVal, scaleY, yDim, yOff); | ||||
|  | ||||
|                     p.moveTo(cx + size/2, cy); | ||||
|                 //	arc(p, cx, cy, 3, 0, deg360); | ||||
|                     arc(p, cx, cy, size/2, 0, deg360); | ||||
|  | ||||
|                 //	m.e = cx; | ||||
|                 //	m.f = cy; | ||||
|                 //	p.addPath(cir, m); | ||||
|  | ||||
|                 //	qt.add({x: cx - 1.5, y: cy - 1.5, w: 3, h: 3, sidx: seriesIdx, didx: i}); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             console.timeEnd("points"); | ||||
|  | ||||
|             u.ctx.fill(p); | ||||
|         }); | ||||
|  | ||||
|         return null; | ||||
|     }; | ||||
|  | ||||
|     const drawPoints2 = (u, seriesIdx, idx0, idx1) => { | ||||
|         uPlot.orient(u, seriesIdx, (series, dataX, dataY, scaleX, scaleY, valToPosX, valToPosY, xOff, yOff, xDim, yDim, moveTo, lineTo, rect, arc) => { | ||||
|             let d = u.data[seriesIdx]; | ||||
|  | ||||
|             u.ctx.fillStyle = series.fill(); | ||||
|  | ||||
|             let deg360 = 2 * Math.PI; | ||||
|  | ||||
|             console.time("points"); | ||||
|  | ||||
|         //	let cir = new Path2D(); | ||||
|         //	cir.moveTo(0, 0); | ||||
|         //	arc(cir, 0, 0, 3, 0, deg360); | ||||
|  | ||||
|             // Create transformation matrix that moves 200 points to the right | ||||
|         //	let m = document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGMatrix(); | ||||
|         //	m.a = 1;   m.b = 0; | ||||
|         //	m.c = 0;   m.d = 1; | ||||
|         //	m.e = 200; m.f = 0; | ||||
|  | ||||
|  | ||||
|             let p = new Path2D(); | ||||
|  | ||||
|             let strokeWidth = 1; | ||||
|  | ||||
|             for (let i = 0; i < d[0].length; i++) { | ||||
|                 let cx = valToPosX(d[0][i], scaleX, xDim, xOff); | ||||
|                 let cy = valToPosY(d[1][i], scaleY, yDim, yOff); | ||||
|  | ||||
|                 let size = d[2][i] * devicePixelRatio; | ||||
|  | ||||
|                 p.moveTo(cx + size, cy); | ||||
|                 arc(p, cx, cy, size, 0, deg360); | ||||
|                 qt.add({ | ||||
|                     x: cx - size - strokeWidth/2 - u.bbox.left, | ||||
|                     y: cy - size - strokeWidth/2 - u.bbox.top, | ||||
|                     w: size * 2 + strokeWidth, | ||||
|                     h: size * 2 + strokeWidth, | ||||
|                     sidx: seriesIdx, | ||||
|                     didx: i | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             console.timeEnd("points"); | ||||
|  | ||||
|             u.ctx.fill(p); | ||||
|  | ||||
|             u.ctx.lineWidth = strokeWidth; | ||||
|             u.ctx.strokeStyle = series.stroke(); | ||||
|             u.ctx.stroke(p); | ||||
|         }); | ||||
|  | ||||
|         return null; | ||||
|     }; | ||||
|  | ||||
|     // shape? | ||||
|     const makeDrawPoints3 = (opts) => { | ||||
|         let {/*size,*/ disp, each = () => {}} = opts; | ||||
|  | ||||
|         return (u, seriesIdx, idx0, idx1) => { | ||||
|             uPlot.orient(u, seriesIdx, (series, dataX, dataY, scaleX, scaleY, valToPosX, valToPosY, xOff, yOff, xDim, yDim, moveTo, lineTo, rect, arc) => { | ||||
|                 let d = u.data[seriesIdx]; | ||||
|  | ||||
|                 let strokeWidth = 1; | ||||
|  | ||||
|                 u.ctx.save(); | ||||
|  | ||||
|                 u.ctx.rect(u.bbox.left, u.bbox.top, u.bbox.width, u.bbox.height); | ||||
|                 u.ctx.clip(); | ||||
|  | ||||
|                 u.ctx.fillStyle = series.fill(); | ||||
|                 u.ctx.strokeStyle = series.stroke(); | ||||
|                 u.ctx.lineWidth = strokeWidth; | ||||
|  | ||||
|                 let deg360 = 2 * Math.PI; | ||||
|  | ||||
|                 console.time("points"); | ||||
|  | ||||
|             //	let cir = new Path2D(); | ||||
|             //	cir.moveTo(0, 0); | ||||
|             //	arc(cir, 0, 0, 3, 0, deg360); | ||||
|  | ||||
|                 // Create transformation matrix that moves 200 points to the right | ||||
|             //	let m = document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGMatrix(); | ||||
|             //	m.a = 1;   m.b = 0; | ||||
|             //	m.c = 0;   m.d = 1; | ||||
|             //	m.e = 200; m.f = 0; | ||||
|  | ||||
|                 // compute bubble dims | ||||
|                 let sizes = disp.size.values(u, seriesIdx, idx0, idx1); | ||||
|  | ||||
|                 // todo: this depends on direction & orientation | ||||
|                 // todo: calc once per redraw, not per path | ||||
|                 let filtLft = u.posToVal(-maxSize / 2, scaleX.key); | ||||
|                 let filtRgt = u.posToVal(u.bbox.width / devicePixelRatio + maxSize / 2, scaleX.key); | ||||
|                 let filtBtm = u.posToVal(u.bbox.height / devicePixelRatio + maxSize / 2, scaleY.key); | ||||
|                 let filtTop = u.posToVal(-maxSize / 2, scaleY.key); | ||||
|  | ||||
|                 for (let i = 0; i < d[0].length; i++) { | ||||
|                     let xVal = d[0][i]; | ||||
|                     let yVal = d[1][i]; | ||||
|                     let size = sizes[i] * devicePixelRatio; | ||||
|  | ||||
|                     if (xVal >= filtLft && xVal <= filtRgt && yVal >= filtBtm && yVal <= filtTop) { | ||||
|                         let cx = valToPosX(xVal, scaleX, xDim, xOff); | ||||
|                         let cy = valToPosY(yVal, scaleY, yDim, yOff); | ||||
|  | ||||
|                         u.ctx.moveTo(cx + size/2, cy); | ||||
|                         u.ctx.beginPath(); | ||||
|                         u.ctx.arc(cx, cy, size/2, 0, deg360); | ||||
|                         u.ctx.fill(); | ||||
|                         u.ctx.stroke(); | ||||
|  | ||||
|                         each(u, seriesIdx, i, | ||||
|                             cx - size/2 - strokeWidth/2, | ||||
|                             cy - size/2 - strokeWidth/2, | ||||
|                             size + strokeWidth, | ||||
|                             size + strokeWidth | ||||
|                         ); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 console.timeEnd("points"); | ||||
|  | ||||
|                 u.ctx.restore(); | ||||
|             }); | ||||
|  | ||||
|             return null; | ||||
|         }; | ||||
|     }; | ||||
|  | ||||
|     // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/isPointInPath | ||||
|     let qt; | ||||
|  | ||||
|     let pxRatio; | ||||
|  | ||||
|     function setPxRatio() { | ||||
|         pxRatio = devicePixelRatio; | ||||
|     } | ||||
|  | ||||
|     function guardedRange(u, min, max) { | ||||
|         if (max == min) { | ||||
|             if (min == null) { | ||||
|                 min = 0; | ||||
|                 max = 100; | ||||
|             } | ||||
|             else { | ||||
|                 let delta = Math.abs(max) || 100; | ||||
|                 max += delta; | ||||
|                 min -= delta; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return [min, max]; | ||||
|     } | ||||
|  | ||||
|     setPxRatio(); | ||||
|  | ||||
|     window.addEventListener('dppxchange', setPxRatio); | ||||
|  | ||||
|     const opts = { | ||||
|         title: "Scatter Plot", | ||||
|         mode: 2, | ||||
|         width: 1920, | ||||
|         height: 600, | ||||
|         legend: { | ||||
|             live: false, | ||||
|         }, | ||||
|         hooks: { | ||||
|             drawClear: [ | ||||
|                 u => { | ||||
|                 //	qt = qt || new Quadtree(0, 0, u.bbox.width, u.bbox.height); | ||||
|  | ||||
|                 //	qt.clear(); | ||||
|  | ||||
|                     // force-clear the path cache to cause drawBars() to rebuild new quadtree | ||||
|                     u.series.forEach((s, i) => { | ||||
|                         if (i > 0) | ||||
|                             s._paths = null; | ||||
|                     }); | ||||
|                 }, | ||||
|             ], | ||||
|         }, | ||||
|         scales: { | ||||
|             x: { | ||||
|                 time: false, | ||||
|             //	auto: false, | ||||
|             //	range: [0, 500], | ||||
|                 // remove any scale padding, use raw data limits | ||||
|                 range: guardedRange, | ||||
|             }, | ||||
|             y: { | ||||
|             //	auto: false, | ||||
|             //	range: [0, 500], | ||||
|                 // remove any scale padding, use raw data limits | ||||
|                 range: guardedRange, | ||||
|             }, | ||||
|         }, | ||||
|         series: [ | ||||
|             { | ||||
|                 /* | ||||
|                 stroke: "red", | ||||
|                 fill: "rgba(255,0,0,0.1)", | ||||
|                 paths: (u, seriesIdx, idx0, idx1) => { | ||||
|                     uPlot.orient(u, seriesIdx, (series, dataX, dataY, scaleX, scaleY, valToPosX, valToPosY, xOff, yOff, xDim, yDim) => { | ||||
|                         let d = u.data[seriesIdx]; | ||||
|  | ||||
|                         console.log(d); | ||||
|                     }); | ||||
|  | ||||
|                     return null; | ||||
|                 }, | ||||
|                 */ | ||||
|             }, | ||||
|             { | ||||
|                 stroke: "red", | ||||
|                 fill: "rgba(255,0,0,0.1)", | ||||
|                 paths: drawPoints, | ||||
|             }, | ||||
|             { | ||||
|                 stroke: "green", | ||||
|                 fill: "rgba(0,255,0,0.1)", | ||||
|                 paths: drawPoints, | ||||
|             }, | ||||
|             { | ||||
|                 stroke: "blue", | ||||
|                 fill: "rgba(0,0,255,0.1)", | ||||
|                 paths: drawPoints, | ||||
|             }, | ||||
|             { | ||||
|                 stroke: "magenta", | ||||
|                 fill: "rgba(0,0,255,0.1)", | ||||
|                 paths: drawPoints, | ||||
|             }, | ||||
|         ], | ||||
|     }; | ||||
|  | ||||
|     let u = new uPlot(opts, data, document.body); | ||||
|  | ||||
|  | ||||
|  | ||||
|     points = 50; | ||||
|  | ||||
|     // size range in pixels (diameter) | ||||
|     let minSize = 6; | ||||
|     let maxSize = 60; | ||||
|  | ||||
|     let maxArea = Math.PI * (maxSize / 2) ** 2; | ||||
|     let minArea = Math.PI * (minSize / 2) ** 2; | ||||
|  | ||||
|     // quadratic scaling (px area) | ||||
|     function getSize(value, minValue, maxValue) { | ||||
|         let pct = value / maxValue; | ||||
|         // clamp to min area | ||||
|         //let area = Math.max(maxArea * pct, minArea); | ||||
|         let area = maxArea * pct; | ||||
|         return Math.sqrt(area / Math.PI) * 2; | ||||
|     } | ||||
|  | ||||
|     function getSizeMinMax(u) { | ||||
|         let minValue = Infinity; | ||||
|         let maxValue = -Infinity; | ||||
|  | ||||
|         for (let i = 1; i < u.series.length; i++) { | ||||
|             let sizeData = u.data[i][2]; | ||||
|  | ||||
|             for (let j = 0; j < sizeData.length; j++) { | ||||
|                 minValue = Math.min(minValue, sizeData[j]); | ||||
|                 maxValue = Math.max(maxValue, sizeData[j]); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return [minValue, maxValue]; | ||||
|     } | ||||
|  | ||||
|     let drawPoints3 = makeDrawPoints3({ | ||||
|         disp: { | ||||
|             size: { | ||||
|                 unit: 3, // raw CSS pixels | ||||
|             //	discr: true, | ||||
|                 values: (u, seriesIdx, idx0, idx1) => { | ||||
|                     // TODO: only run once per setData() call | ||||
|                     let [minValue, maxValue] = getSizeMinMax(u); | ||||
|                     return u.data[seriesIdx][2].map(v => getSize(v, minValue, maxValue)); | ||||
|                 }, | ||||
|             }, | ||||
|         }, | ||||
|         each: (u, seriesIdx, dataIdx, lft, top, wid, hgt) => { | ||||
|             // we get back raw canvas coords (included axes & padding). translate to the plotting area origin | ||||
|             lft -= u.bbox.left; | ||||
|             top -= u.bbox.top; | ||||
|             qt.add({x: lft, y: top, w: wid, h: hgt, sidx: seriesIdx, didx: dataIdx}); | ||||
|         }, | ||||
|     }); | ||||
|  | ||||
|     let data2 = filledArr(series, v => [ | ||||
|         filledArr(points, i => randInt(0,500)), | ||||
|         filledArr(points, i => randInt(0,500)), | ||||
|         filledArr(points, i => randInt(1,10000)),  // bubble size, population | ||||
|         filledArr(points, i => (Math.random() + 1).toString(36).substring(7)), // label / country name | ||||
|     ]); | ||||
|  | ||||
|     data2[0] = null; | ||||
|     data2[1][1] = data2[1][1].map(v => v == 0 ? v : v * -1); | ||||
|  | ||||
|     /* | ||||
|     const legendValue = (u, val, seriesIdx, idx) => { | ||||
|         let [x, y, size] = u.data[seriesIdx]; | ||||
|         return `${x[idx]}, ${y[idx]}, ${size[idx]}`; | ||||
|     }; | ||||
|     */ | ||||
|  | ||||
|     const legendValues = (u, seriesIdx, dataIdx) => { | ||||
|         // when data null, it's initial schema probe (also u.status == 0) | ||||
|         if (u.data == null || dataIdx == null || hRect == null || hRect.sidx != seriesIdx) { | ||||
|             return { | ||||
|                 "Country":    '<NUM COUNTRIES>', | ||||
|                 "Population": '<TOTAL POP>', | ||||
|                 "GDP":        '<TOTAL GDP>', | ||||
|                 "Income":     '<AVG INCOME>' | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         let [x, y, size, label] = u.data[seriesIdx]; | ||||
|  | ||||
|         return { | ||||
|             "Country":    label[hRect.didx], | ||||
|             "Population": size[hRect.didx], | ||||
|             "GDP":        '$' + x[hRect.didx], | ||||
|             "Income":     '$' + y[hRect.didx], | ||||
|         }; | ||||
|     }; | ||||
|  | ||||
|     // hovered | ||||
|     let hRect; | ||||
|  | ||||
|     const opts2 = { | ||||
|         title: "Bubble Plot", | ||||
|         mode: 2, | ||||
|         width: 1920, | ||||
|         height: 600, | ||||
|         legend: { | ||||
|         //	live: false, | ||||
|         }, | ||||
|         cursor: { | ||||
|             dataIdx: (u, seriesIdx) => { | ||||
|                 if (seriesIdx == 1) { | ||||
|                     hRect = null; | ||||
|  | ||||
|                     let dist = Infinity; | ||||
|                     let cx = u.cursor.left * pxRatio; | ||||
|                     let cy = u.cursor.top * pxRatio; | ||||
|  | ||||
|                     // !!! https://github.com/timohausmann/quadtree-js/blob/master/docs/simple.html | ||||
|                     // Rewrite mouseOver based on this example | ||||
|  | ||||
|                     // let overlaps =  qt.retrieve({x:cx, y:cy, width:1, height:1}) | ||||
|                      | ||||
|                     // for(var i=0;i<overlaps.length;i=i+1) { | ||||
|                     //     overlaps[i].check = true; | ||||
|                     // } | ||||
|  | ||||
|                     // OLD! | ||||
|                     qt.get(cx, cy, 1, 1, o => { | ||||
|                         if (pointWithin(cx, cy, o.x, o.y, o.x + o.w, o.y + o.h)) { | ||||
|                             let ocx = o.x + o.w / 2; | ||||
|                             let ocy = o.y + o.h / 2; | ||||
|  | ||||
|                             let dx = ocx - cx; | ||||
|                             let dy = ocy - cy; | ||||
|  | ||||
|                             let d = Math.sqrt(dx ** 2 + dy ** 2); | ||||
|  | ||||
|                             // test against radius for actual hover | ||||
|                             if (d <= o.w / 2) { | ||||
|                                 // only hover bbox with closest distance | ||||
|                                 if (d <= dist) { | ||||
|                                     dist = d; | ||||
|                                     hRect = o; | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     }); | ||||
|                 } | ||||
|  | ||||
|                 return hRect && seriesIdx == hRect.sidx ? hRect.didx : null; | ||||
|             }, | ||||
|             points: { | ||||
|                 size: (u, seriesIdx) => { | ||||
|                     return hRect && seriesIdx == hRect.sidx ? hRect.w / devicePixelRatio : 0; | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         hooks: { | ||||
|             drawClear: [ | ||||
|                 u => { | ||||
|                     qt = qt || new Quadtree(0, 0, u.bbox.width, u.bbox.height); | ||||
|  | ||||
|                     qt.clear(); | ||||
|  | ||||
|                     // force-clear the path cache to cause drawBars() to rebuild new quadtree | ||||
|                     u.series.forEach((s, i) => { | ||||
|                         if (i > 0) | ||||
|                             s._paths = null; | ||||
|                     }); | ||||
|                 }, | ||||
|             ], | ||||
|         }, | ||||
|         axes: [ | ||||
|             { | ||||
|                 label: "GDP", | ||||
|             }, | ||||
|             { | ||||
|                 label: "Income 1", | ||||
|             }, | ||||
|             { | ||||
|                 side: 1, | ||||
|                 scale: 'y2', | ||||
|                 stroke: "red", | ||||
|                 label: "Income 2", | ||||
|                 grid: { | ||||
|                     show: false, | ||||
|                 }, | ||||
|             }, | ||||
|         ], | ||||
|         scales: { | ||||
|             x: { | ||||
|                 time: false, | ||||
|             //	auto: false, | ||||
|             //	range: [0, 500], | ||||
|  | ||||
|                 // remove any scale padding, use raw data limits | ||||
|                 range: guardedRange, | ||||
|             }, | ||||
|             y: { | ||||
|             //	auto: false, | ||||
|             //	range: [0, 500], | ||||
|  | ||||
|                 // remove any scale padding, use raw data limits | ||||
|                 range: guardedRange, | ||||
|             }, | ||||
|             y2: { | ||||
|                 dir: 1, | ||||
|                 ori: 1, | ||||
|                 range: guardedRange, | ||||
|             } | ||||
|         }, | ||||
|         series: [ | ||||
|             null, | ||||
|             { | ||||
|                 facets: [ | ||||
|                     { | ||||
|                         scale: 'x', | ||||
|                         auto: true, | ||||
|                     }, | ||||
|                     { | ||||
|                         scale: 'y2', | ||||
|                         auto: true, | ||||
|                     } | ||||
|                 ], | ||||
|                 label: "Region A", | ||||
|                 stroke: "red", | ||||
|                 fill: "rgba(255,0,0,0.3)", | ||||
|                 paths: drawPoints3, | ||||
|                 values: legendValues, | ||||
|             }, | ||||
|             { | ||||
|                 facets: [ | ||||
|                     { | ||||
|                         scale: 'x', | ||||
|                         auto: true, | ||||
|                     }, | ||||
|                     { | ||||
|                         scale: 'y', | ||||
|                         auto: true, | ||||
|                     } | ||||
|                 ], | ||||
|                 label: "Region B", | ||||
|                 stroke: "green", | ||||
|                 fill: "rgba(0,255,0,0.3)", | ||||
|                 paths: drawPoints3, | ||||
|                 values: legendValues, | ||||
|             }, | ||||
|             { | ||||
|                 facets: [ | ||||
|                     { | ||||
|                         scale: 'x', | ||||
|                         auto: true, | ||||
|                     }, | ||||
|                     { | ||||
|                         scale: 'y', | ||||
|                         auto: true, | ||||
|                     } | ||||
|                 ], | ||||
|                 label: "Region C", | ||||
|                 stroke: "blue", | ||||
|                 fill: "rgba(0,0,255,0.3)", | ||||
|                 paths: drawPoints3, | ||||
|                 values: legendValues, | ||||
|             }, | ||||
|             { | ||||
|                 facets: [ | ||||
|                     { | ||||
|                         scale: 'x', | ||||
|                         auto: true, | ||||
|                     }, | ||||
|                     { | ||||
|                         scale: 'y', | ||||
|                         auto: true, | ||||
|                     } | ||||
|                 ], | ||||
|                 label: "Region E", | ||||
|                 stroke: "orange", | ||||
|                 fill: "rgba(255,128,0,0.3)", | ||||
|                 paths: drawPoints3, | ||||
|                 values: legendValues, | ||||
|             }, | ||||
|         ], | ||||
|     }; | ||||
|  | ||||
|     let u2 = new uPlot(opts2, data2, document.body); | ||||
| </script> | ||||
|  | ||||
| {#if data != null} | ||||
|     <div bind:this={plotWrapper}/> | ||||
| {:else} | ||||
|     <Card class="mx-4" body color="warning">Cannot render scatter: No data!</Card> | ||||
| {/if} | ||||
		Reference in New Issue
	
	Block a user