Skip to content

Commit f0a0635

Browse files
committed
Merge branch 'main' into demo
2 parents 6b71d84 + 845dfcb commit f0a0635

29 files changed

Lines changed: 601 additions & 388 deletions

index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<meta name="viewport" content="width=device-width, initial-scale=1.0">
77
<title>VVPlot</title>
88
<link rel="stylesheet" href="web-docs/style/style.css">
9+
<link rel="stylesheet" href="web-docs/style/article.css">
910
</head>
1011

1112
<body>

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "vvplot",
3-
"version": "0.3.0",
3+
"version": "0.4.0",
44
"license": "MIT",
55
"files": [
66
"dist"

src/core/CoreLegend.vue

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11
<script setup>
22
import { computed } from 'vue'
3-
import { interaction } from '#base/js/utils'
3+
import { interaction, GEnumLevel } from '#base/js/utils'
44
import CoreGuide from './guide/CoreGuide.vue'
55
const { scales, theme } = defineProps({
66
theme: { type: Object, default: () => ({}) },
77
scales: Map,
88
})
99
const guides = computed(() => {
10-
let guide_scales = new Map(
11-
Array.from(scales).filter(([fn]) => fn.legend !== false)
12-
)
10+
let guide_scales = new Map(Array.from(scales).filter(([fn]) => fn.legend !== false))
1311
let scale_fns = Array.from(guide_scales.keys())
1412
let keys = scale_fns.map(s => s.key)
15-
let j = 1
16-
for (let i in keys) {
17-
while (keys.includes("_guide_" + j)) j++
18-
if (keys[i] == null) keys[i] = "_guide_" + j
13+
let n = 1
14+
for (let i = 0; i < keys.length; i++) {
15+
while (keys.includes("_guide_" + n)) n++
16+
keys[i] ??= keys.find((_, j) => keys[j] != null && GEnumLevel.isEqual(scale_fns[j].level, scale_fns[i].level)) ?? "_guide_" + n
1917
}
2018
let types = scale_fns.map(s => getGuideType(s))
2119
let groups = interaction(keys, types)

src/core/CorePlot.vue

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -300,19 +300,24 @@ function getPadding({ min: $min, max: $max } = {}, { min: mmin = 0, max: mmax =
300300
301301
function getCoord(event) {
302302
let rect = svgRef.value.getBoundingClientRect()
303-
let l = Math.trunc(event.clientX) - (rect.left + panel.left + innerRect.left),
304-
t = Math.trunc(event.clientY) - (rect.top + panel.top + innerRect.top),
305-
r = rect.left + panel.left + innerRect.right - Math.trunc(event.clientX),
306-
b = rect.top + panel.top + innerRect.bottom - Math.trunc(event.clientY)
303+
let scaleX = rect.width / svgRef.value.clientWidth,
304+
scaleY = rect.height / svgRef.value.clientHeight
305+
let offsetX = event.clientX - rect.left, offsetY = event.clientY - rect.top
306+
let l = offsetX / scaleX - (panel.left + innerRect.left),
307+
t = offsetY / scaleY - (panel.top + innerRect.top),
308+
r = (panel.left + innerRect.right) - offsetX / scaleX,
309+
b = (panel.top + innerRect.bottom) - offsetY / scaleY
307310
let { x, y } = pos2coord({ h: l, v: t })
308311
return { l, t, r, b, x, y }
309312
}
310313
function isInPlot(event) {
311314
let rect = svgRef.value.getBoundingClientRect()
312-
return event.clientX > rect.left + panel.l &&
313-
event.clientX < rect.right - panel.r &&
314-
event.clientY > rect.top + panel.t &&
315-
event.clientY < rect.bottom - panel.b
315+
let scaleX = rect.width / svgRef.value.clientWidth,
316+
scaleY = rect.height / svgRef.value.clientHeight
317+
return event.clientX - rect.left > panel.l * scaleX &&
318+
rect.right - event.clientX > panel.r * scaleX &&
319+
event.clientY - rect.top > panel.t * scaleY &&
320+
rect.bottom - event.clientY > panel.b * scaleY
316321
}
317322
318323
function dispatchPointerEvent(e) {
@@ -449,6 +454,9 @@ function svgPointerdown(e) {
449454
}
450455
let act = props.action.find(a => a.action == "move" && ["buttons", "ctrlKey", "shiftKey", "altKey", "metaKey"].every(k => a[k] == e[k]))
451456
if (act) {
457+
let rect = svgRef.value.getBoundingClientRect()
458+
let scaleX = rect.width / svgRef.value.clientWidth,
459+
scaleY = rect.height / svgRef.value.clientHeight
452460
e.target.setPointerCapture(e.pointerId)
453461
svg.style.userSelect = 'none'
454462
moveTimer = clearTimeout(moveTimer)
@@ -461,13 +469,13 @@ function svgPointerdown(e) {
461469
let [h, v] = flip ? [y, x] : [x, y]
462470
if (h) {
463471
movementX += ev.movementX
464-
let dh = oob_squish_any(-movementX, { min: boundary.hmin - hmin0, max: boundary.hmax - hmax0 })
472+
let dh = oob_squish_any(-movementX / scaleX, { min: boundary.hmin - hmin0, max: boundary.hmax - hmax0 })
465473
h1 = hmin0 + dh
466474
h2 = hmax0 + dh
467475
}
468476
if (v) {
469477
movementY += ev.movementY
470-
let dv = oob_squish_any(-movementY, { min: boundary.vmin - vmin0, max: boundary.vmax - vmax0 })
478+
let dv = oob_squish_any(-movementY / scaleY, { min: boundary.vmin - vmin0, max: boundary.vmax - vmax0 })
471479
v1 = vmin0 + dv
472480
v2 = vmax0 + dv
473481
}

src/core/axis/CoreAxisH.vue

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,10 @@ const tickTexts = computed(() => {
119119
const iRef = useTemplateRef("i")
120120
function getPosition(event) {
121121
let rect = iRef.value.getBoundingClientRect()
122-
let l = Math.trunc(event.clientX) - rect.left - layout.left,
123-
r = rect.left + layout.left + layout.width - Math.trunc(event.clientX)
122+
let scaleX = rect.width / iRef.value.getBBox().width
123+
let offsetX = event.clientX - rect.left
124+
let l = offsetX / scaleX - layout.left,
125+
r = layout.left + layout.width - offsetX / scaleX
124126
let result = { l, r }
125127
let { x, y } = pos2coord({ h: l })
126128
if (x) result.x = x
@@ -159,11 +161,13 @@ function axisMovePointerdown(e) {
159161
moveTimer = clearTimeout(moveTimer)
160162
let hmin0 = 0, hmax0 = layout.width
161163
let boundary = coord2pos(act)
164+
let rect = iRef.value.getBoundingClientRect()
165+
let scaleX = rect.width / iRef.value.getBBox().width
162166
e.target.setPointerCapture(e.pointerId)
163167
e.target.onpointermove = (ev) => {
164168
movementX += ev.movementX
165169
if (!pointerMoved) return
166-
let dh = oob_squish_any(-movementX, { min: boundary.hmin - hmin0, max: boundary.hmax - hmax0 })
170+
let dh = oob_squish_any(-movementX / scaleX, { min: boundary.hmin - hmin0, max: boundary.hmax - hmax0 })
167171
let { xmin, xmax, ymin, ymax } = pos2coord({ hmin: hmin0 + dh, hmax: hmax0 + dh })
168172
Object.assign(rangePreview, { xmin, xmax, ymin, ymax })
169173
}
@@ -191,12 +195,14 @@ function axisRescaleLeftPointerdown(e) {
191195
min: coord2pos(act).hmin,
192196
max: coord2pos({ hmin: coord0.hmax - mrh, hmax: coord0.hmin + mrh }).hmin,
193197
}
198+
let rect = iRef.value.getBoundingClientRect()
199+
let scaleX = rect.width / iRef.value.getBBox().width
194200
let movementX = 0
195201
e.target.setPointerCapture(e.pointerId)
196202
e.target.onpointermove = (ev) => {
197203
movementX += ev.movementX
198204
let { xmin, xmax, ymin, ymax } = pos2coord({
199-
hmin: oob_squish_any(hmax - (hmax - hmin) * position.r / Math.max(position.r - movementX, 1), boundary),
205+
hmin: oob_squish_any(hmax - (hmax - hmin) * position.r / Math.max(position.r - movementX / scaleX, 1), boundary),
200206
hmax,
201207
})
202208
Object.assign(rangePreview, { xmin, xmax, ymin, ymax })
@@ -220,6 +226,8 @@ function axisRescaleRightPointerdown(e) {
220226
min: coord2pos({ hmin: coord0.hmax - mrh, hmax: coord0.hmin + mrh }).hmax,
221227
max: coord2pos(act).hmax,
222228
}
229+
let rect = iRef.value.getBoundingClientRect()
230+
let scaleX = rect.width / iRef.value.getBBox().width
223231
let movementX = 0
224232
e.target.setPointerCapture(e.pointerId)
225233
e.target.onpointermove = (ev) => {

src/core/axis/CoreAxisV.vue

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,10 @@ const tickTexts = computed(() => {
119119
const iRef = useTemplateRef("i")
120120
function getPosition(event) {
121121
let rect = iRef.value.getBoundingClientRect()
122-
let t = Math.trunc(event.clientY) - rect.top - layout.top,
123-
b = rect.top + layout.top + layout.height - Math.trunc(event.clientY)
122+
let scaleY = rect.height / iRef.value.getBBox().height
123+
let offsetY = event.clientY - rect.top
124+
let t = offsetY / scaleY - layout.top,
125+
b = layout.top + layout.height - offsetY / scaleY
124126
let result = { t, b }
125127
let { x, y } = pos2coord({ v: t })
126128
if (x) result.x = x
@@ -159,11 +161,13 @@ function axisMovePointerdown(e) {
159161
moveTimer = clearTimeout(moveTimer)
160162
let vmin0 = 0, vmax0 = layout.height
161163
let boundary = coord2pos(act)
164+
let rect = iRef.value.getBoundingClientRect()
165+
let scaleY = rect.height / iRef.value.getBBox().height
162166
e.target.setPointerCapture(e.pointerId)
163167
e.target.onpointermove = (ev) => {
164168
movementY += ev.movementY
165169
if (!pointerMoved) return
166-
let dv = oob_squish_any(-movementY, { min: boundary.vmin - vmin0, max: boundary.vmax - vmax0 })
170+
let dv = oob_squish_any(-movementY / scaleY, { min: boundary.vmin - vmin0, max: boundary.vmax - vmax0 })
167171
let { xmin, xmax, ymin, ymax } = pos2coord({ vmin: vmin0 + dv, vmax: vmax0 + dv })
168172
Object.assign(rangePreview, { xmin, xmax, ymin, ymax })
169173
}
@@ -191,12 +195,14 @@ function axisRescaleTopPointerdown(e) {
191195
min: coord2pos(act).vmin,
192196
max: coord2pos({ vmin: coord0.vmax - mrv, vmax: coord0.vmin + mrv }).vmin,
193197
}
198+
let rect = iRef.value.getBoundingClientRect()
199+
let scaleY = rect.height / iRef.value.getBBox().height
194200
let movementY = 0
195201
e.target.setPointerCapture(e.pointerId)
196202
e.target.onpointermove = (ev) => {
197203
movementY += ev.movementY
198204
let { xmin, xmax, ymin, ymax } = pos2coord({
199-
vmin: oob_squish_any(vmax - (vmax - vmin) * position.b / Math.max(position.b - movementY, 1), boundary),
205+
vmin: oob_squish_any(vmax - (vmax - vmin) * position.b / Math.max(position.b - movementY / scaleY, 1), boundary),
200206
vmax,
201207
})
202208
Object.assign(rangePreview, { xmin, xmax, ymin, ymax })
@@ -220,13 +226,15 @@ function axisRescaleBottomPointerdown(e) {
220226
min: coord2pos({ vmin: coord0.vmax - mrv, vmax: coord0.vmin + mrv }).vmax,
221227
max: coord2pos(act).vmax,
222228
}
229+
let rect = iRef.value.getBoundingClientRect()
230+
let scaleY = rect.height / iRef.value.getBBox().height
223231
let movementY = 0
224232
e.target.setPointerCapture(e.pointerId)
225233
e.target.onpointermove = (ev) => {
226234
movementY += ev.movementY
227235
let { xmin, xmax, ymin, ymax } = pos2coord({
228236
vmin,
229-
vmax: oob_squish_any(vmin + (vmax - vmin) * position.t / Math.max(position.t + movementY, 1), boundary),
237+
vmax: oob_squish_any(vmin + (vmax - vmin) * position.t / Math.max(position.t + movementY / scaleY, 1), boundary),
230238
})
231239
Object.assign(rangePreview, { xmin, xmax, ymin, ymax })
232240
}

src/core/element/CorePoint.vue

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,11 @@ const binds = computed(() => {
3131
'stroke-dasharray': parseLinetype(linetype).join(" ") || null,
3232
}
3333
if (d != null) {
34-
let transform = `translate(${x + translateX},${y + translateY})`
35-
if (size != 1) transform += `scale(${size})`
36-
if (angle) transform += `rotate(${angle})`
37-
Object.assign(result, { d, transform })
34+
let transform = [], tslX = x + translateX, tslY = y + translateY
35+
if (tslX !== 0 || tslY !== 0) transform.push(`translate(${tslX},${tslY})`)
36+
if (size != 1) transform.push(`scale(${size})`)
37+
if (angle) transform.push(`rotate(${angle})`)
38+
Object.assign(result, { d, transform: transform.join(' ') || null })
3839
} else {
3940
let transform = (translateX || translateY) ? `translate(${translateX}, ${translateY})` : null
4041
Object.assign(result, { cx: x, cy: y, r: size / 2, transform })

src/core/guide/CoreGuide.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const guide = {
1414
gradientbar: CoreGuideGradientbar,
1515
}
1616
const title = computed(() => {
17-
return scales.map(([s]) => s.title).find(v => v != null)
17+
return scales.reduce((v, [s]) => v ?? s.title, null) ?? scales.reduce((v, [s]) => v ?? s._title, null)
1818
})
1919
const breaks = computed(() => {
2020
let scale_funcs = scales.map(([s]) => s)
@@ -33,10 +33,10 @@ const binds = computed(() => {
3333
let result = { scales: scales.map(([s]) => s) }
3434
if (type == "legendkey") {
3535
result.appearances = {
36-
text: obj_merge(...scales.flatMap(([s, a]) => [a.text, a.markdown])),
36+
tile: obj_merge(...scales.flatMap(([s, a]) => [a.rect, a.tile, a.polygon, a.ellipse])),
3737
line: obj_merge(...scales.flatMap(([s, a]) => [a.line, a.linerange, a.curve])),
3838
point: obj_merge(...scales.flatMap(([s, a]) => [a.point])),
39-
tile: obj_merge(...scales.flatMap(([s, a]) => [a.rect, a.tile, a.polygon, a.ellipse])),
39+
text: obj_merge(...scales.flatMap(([s, a]) => [a.text, a.markdown])),
4040
}
4141
} else if (type == "gradientbar") {
4242
let max = scales.map(([s]) => s.limits?.max).filter(v => v != null).reduce((a, b) => Math.min(a, b), Infinity)

src/core/guide/CoreGuideLegendkey.vue

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,24 @@ const guide_breaks = computed(() => {
1616
}))
1717
let binds = categorize(keys.map(v => v.bind))
1818
let group = Map.groupBy(keys.map(v => v.label), (v, i) => binds.categories[binds[i]])
19-
return Array.from(group).map(([bind, labels]) => ({
20-
bind, label: labels.join('; ')
21-
}))
19+
20+
return Array.from(group).map(([$bind, labels]) => {
21+
let bind = { tile: {}, line: {}, point: {}, text: {} }
22+
for (let geom in bind) {
23+
for (let prop of Object.keys(element[geom].props)) {
24+
if ($bind[prop]) bind[geom][prop] = $bind[prop]
25+
}
26+
}
27+
return { bind, label: labels.join('; ') }
28+
})
2229
})
2330
const geoms = computed(() => {
2431
let { text, line, point, tile } = appearances
2532
let result = {}
26-
if (text != null) result.text = { size: 4, text: 'a', ...text }
33+
if (tile != null) result.tile = { width: 10, height: 10, fill: 'transparent', ...tile }
2734
if (line != null) result.line = { x1: -5, x2: 5, color: 'black', ...line }
2835
if (point != null) result.point = { size: 6, ...point }
29-
if (tile != null) result.tile = { width: 10, height: 10, fill: 'transparent', ...tile }
36+
if (text != null) result.text = { size: 4, text: 'a', ...text }
3037
return result
3138
})
3239
const width = ref(0)
@@ -42,7 +49,7 @@ watch(guide_breaks, async () => {
4249
<svg :height="guide_breaks.length * 20" :width="width" ref="svg">
4350
<g v-for="v, i in guide_breaks" :transform="`translate(0, ${i * 20})`">
4451
<g :transform="`translate(10, 10)`">
45-
<component :is="element[geom]" v-for="bind, geom in geoms" v-bind="{ ...bind, ...v.bind }" />
52+
<component :is="element[geom]" v-for="bind, geom in geoms" v-bind="{ ...bind, ...v.bind[geom] }" />
4653
</g>
4754
<text x="20" y="10" alignment-baseline="central">{{ v.label }}</text>
4855
</g>

src/core/layer/canvas/CoreCanvasBoxplot.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ const layerCanvas = computed(() => {
9797
canvas.addEventListener(evt, function (e) {
9898
if (e._vhandled) return
9999
const rect = canvas.getBoundingClientRect()
100-
const x = e.clientX - rect.left
101-
const y = e.clientY - rect.top
100+
const x = (e.clientX - rect.left) * canvas.width / rect.width
101+
const y = (e.clientY - rect.top) * canvas.height / rect.height
102102
for (const [path, data] of path_data) {
103103
if (ctx.isPointInPath(path, x, y)) {
104104
emit(evt, e, getCoord(e), data)

0 commit comments

Comments
 (0)