Skip to content

Commit 761302a

Browse files
committed
VueUiScatter added marginal bars config option
1 parent fc457e3 commit 761302a

File tree

6 files changed

+132
-10
lines changed

6 files changed

+132
-10
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "vue-data-ui",
33
"private": false,
4-
"version": "2.1.26",
4+
"version": "2.1.27",
55
"type": "module",
66
"description": "A user-empowering data visualization Vue 3 components library for eloquent data storytelling",
77
"keywords": [

src/App.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,7 +1061,7 @@ const weeks = computed(() => {
10611061
10621062
const scat1 = computed(() => {
10631063
const arr = [];
1064-
for (let i = 0; i < 200; i += 1) {
1064+
for (let i = 0; i < 100; i += 1) {
10651065
arr.push({
10661066
x: Math.random() * i + 12,
10671067
y: (Math.random() * i) / 20,
@@ -4442,7 +4442,7 @@ const pillConfig = ref({
44424442
</template>
44434443
</Box>
44444444

4445-
<Box @copy="copyConfig(PROD_CONFIG.vue_ui_scatter)">
4445+
<Box open @copy="copyConfig(PROD_CONFIG.vue_ui_scatter)">
44464446
<template #title>
44474447
<BaseIcon name="chartScatter" />
44484448
VueUiScatter

src/components/vue-ui-scatter.vue

Lines changed: 107 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,21 @@ const svg = computed(() => {
9494
}
9595
});
9696
97+
const marginalSize = computed(() => {
98+
if(!scatterConfig.value.style.layout.marginalBars.show) {
99+
return 0
100+
}
101+
return scatterConfig.value.style.layout.marginalBars.size + scatterConfig.value.style.layout.marginalBars.offset
102+
})
103+
97104
const drawingArea = computed(() => {
98105
return {
99-
top: scatterConfig.value.style.layout.padding.top,
100-
right: svg.value.width - scatterConfig.value.style.layout.padding.right,
106+
top: scatterConfig.value.style.layout.padding.top + marginalSize.value,
107+
right: svg.value.width - scatterConfig.value.style.layout.padding.right - marginalSize.value,
101108
bottom: svg.value.height - scatterConfig.value.style.layout.padding.bottom,
102109
left: scatterConfig.value.style.layout.padding.left,
103-
height: svg.value.height - scatterConfig.value.style.layout.padding.top - scatterConfig.value.style.layout.padding.bottom,
104-
width: svg.value.width - scatterConfig.value.style.layout.padding.left - scatterConfig.value.style.layout.padding.right
110+
height: svg.value.height - scatterConfig.value.style.layout.padding.top - scatterConfig.value.style.layout.padding.bottom - marginalSize.value,
111+
width: svg.value.width - scatterConfig.value.style.layout.padding.left - scatterConfig.value.style.layout.padding.right - marginalSize.value
105112
}
106113
});
107114
@@ -256,6 +263,61 @@ function getData() {
256263
return drawableDataset.value;
257264
}
258265
266+
function aggregateCoordinates(arr, scale) {
267+
const flattened = arr.flatMap(a => {
268+
return a.plots.map((p) => {
269+
return {
270+
x: p.x,
271+
y: p.y
272+
}
273+
})
274+
});
275+
276+
let xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity;
277+
278+
flattened.forEach(({x, y}) => {
279+
xMin = Math.min(xMin, x);
280+
xMax = Math.max(xMax, x);
281+
yMin = Math.min(yMin, y);
282+
yMax = Math.max(yMax, y);
283+
});
284+
285+
const totalX = xMax - xMin;
286+
const totalY = yMax - yMin;
287+
const chunkSizeX = totalX / scale;
288+
const chunkSizeY = totalY / scale;
289+
const xCounts = Array(scale).fill(0);
290+
const yCounts = Array(scale).fill(0);
291+
292+
flattened.forEach(({ x, y }) => {
293+
const xIndex = Math.floor((x - xMin) / chunkSizeX);
294+
const yIndex = Math.floor((y - yMin) / chunkSizeY);
295+
if(!xCounts[xIndex]) {
296+
xCounts[xIndex] = 0;
297+
}
298+
if(!yCounts[yIndex]) {
299+
yCounts[yIndex] = 0;
300+
}
301+
xCounts[xIndex] += 1;
302+
yCounts[yIndex] += 1;
303+
});
304+
305+
const avgX = [];
306+
const avgY = [];
307+
for (let i = 0; i < scale; i += 1) {
308+
avgX.push(xMin + (i + 0.5) * chunkSizeX);
309+
avgY.push(yMin + (i + 0.5) * chunkSizeY);
310+
}
311+
const maxX = Math.max(...xCounts);
312+
const maxY = Math.max(...yCounts);
313+
return { x: xCounts, y: yCounts, avgX, avgY, maxX, maxY };
314+
}
315+
316+
const scale = ref(20);
317+
const marginalBars = computed(() => {
318+
return aggregateCoordinates(mutableDataset.value, scale.value)
319+
})
320+
259321
const selectedPlotId = ref(undefined);
260322
const selectedPlot = ref(null);
261323
const dataTooltipSlot = ref(null);
@@ -590,6 +652,46 @@ defineExpose({
590652
</g>
591653
</g>
592654
655+
<!-- MARGINAL BARS -->
656+
<g v-if="scatterConfig.style.layout.marginalBars.show">
657+
<defs>
658+
<linearGradient :id="`marginal_x_${uid}`" x1="0%" y1="0%" x2="0%" y2="100%">
659+
<stop offset="0%" :stop-color="scatterConfig.style.layout.marginalBars.fill"/>
660+
<stop offset="100%" :stop-color="scatterConfig.style.backgroundColor"/>
661+
</linearGradient>
662+
<linearGradient :id="`marginal_y_${uid}`" x1="0%" x2="100%" y1="0%" y2="0%">
663+
<stop offset="0%" :stop-color="scatterConfig.style.backgroundColor"/>
664+
<stop offset="100%" :stop-color="scatterConfig.style.layout.marginalBars.fill"/>
665+
</linearGradient>
666+
</defs>
667+
<g v-for="(x, i) in marginalBars.x">
668+
<rect
669+
v-if="x && marginalBars.avgX[i]"
670+
:x="marginalBars.avgX[i] - (drawingArea.width / scale / 2)"
671+
:y="drawingArea.top - scatterConfig.style.layout.marginalBars.offset - x / marginalBars.maxX * scatterConfig.style.layout.marginalBars.size"
672+
:width="drawingArea.width / scale"
673+
:height="x / marginalBars.maxX * scatterConfig.style.layout.marginalBars.size"
674+
:fill="scatterConfig.style.layout.marginalBars.useGradient ? `url(#marginal_x_${uid})` : scatterConfig.style.layout.marginalBars.fill"
675+
:style="`opacity:${scatterConfig.style.layout.marginalBars.opacity}`"
676+
:stroke="scatterConfig.style.backgroundColor"
677+
:rx="scatterConfig.style.layout.marginalBars.borderRadius"
678+
/>
679+
</g>
680+
<g v-for="(y, i) in marginalBars.y">
681+
<rect
682+
v-if="y && marginalBars.avgY[i]"
683+
:x="drawingArea.right + scatterConfig.style.layout.marginalBars.offset"
684+
:y="marginalBars.avgY[i] - (drawingArea.height / scale / 2)"
685+
:height="drawingArea.height / scale"
686+
:width="y / marginalBars.maxY * scatterConfig.style.layout.marginalBars.size"
687+
:fill="scatterConfig.style.layout.marginalBars.useGradient ? `url(#marginal_y_${uid})` : scatterConfig.style.layout.marginalBars.fill"
688+
:style="`opacity:${scatterConfig.style.layout.marginalBars.opacity}`"
689+
:stroke="scatterConfig.style.backgroundColor"
690+
:rx="scatterConfig.style.layout.marginalBars.borderRadius"
691+
/>
692+
</g>
693+
</g>
694+
593695
<!-- AXIS LABELS -->
594696
<g v-if="scatterConfig.style.layout.dataLabels.xAxis.show">
595697
<text
@@ -652,7 +754,7 @@ defineExpose({
652754
:font-weight="scatterConfig.style.layout.dataLabels.yAxis.bold ? 'bold' : 'normal'"
653755
:fill="scatterConfig.style.layout.dataLabels.yAxis.color"
654756
:x="drawingArea.left + drawingArea.width / 2"
655-
:y="drawingArea.top - 8 - scatterConfig.style.layout.dataLabels.yAxis.fontSize"
757+
:y="drawingArea.bottom + 8 + scatterConfig.style.layout.dataLabels.yAxis.fontSize"
656758
>
657759
{{ scatterConfig.style.layout.dataLabels.yAxis.name }}
658760
</text>

src/default_configs.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1916,6 +1916,16 @@
19161916
"stroke":"#e1e5e8",
19171917
"strokeWidth":1
19181918
},
1919+
"marginalBars": {
1920+
"show": false,
1921+
"size": 40,
1922+
"opacity": 0.6,
1923+
"fill": "#2D353C",
1924+
"strokeWidth": 1,
1925+
"offset": 20,
1926+
"borderRadius": 2,
1927+
"useGradient": true
1928+
},
19191929
"plots": {
19201930
"radius": 2,
19211931
"stroke": "#FFFFFF",

types/vue-data-ui.d.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1997,6 +1997,16 @@ declare module 'vue-data-ui' {
19971997
fillOpacity?: number;
19981998
};
19991999
};
2000+
marginalBars?: {
2001+
show?: boolean;
2002+
size?: number;
2003+
opacity?: number;
2004+
fill?: string;
2005+
strokeWidth?: number;
2006+
offset?: number;
2007+
borderRadius?: number;
2008+
useGradient?: boolean;
2009+
};
20002010
correlation?: {
20012011
show?: boolean;
20022012
strokeDasharray?: number;

0 commit comments

Comments
 (0)