From 249130734398ca117e9f84053d875a4b26e8b04d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Thu, 21 Dec 2023 12:43:42 +0100 Subject: [PATCH 1/8] occlusion initializer --- docs/.vitepress/config.ts | 1 + docs/data/cancer.data.ts | 9 + docs/data/cancer.ts | 4 + docs/public/data/cancer.csv | 97 + docs/transforms/occlusion.md | 133 + src/index.d.ts | 1 + src/index.js | 1 + src/transforms/occlusion.d.ts | 59 + src/transforms/occlusion.js | 85 + test/output/occlusionStocks.html | 163 + test/output/occlusionXPaths.svg | 5330 ++++++++++++++++++++++++++++++ test/output/occlusionYPaths.svg | 82 + test/plots/index.ts | 1 + test/plots/occlusion.ts | 105 + 14 files changed, 6071 insertions(+) create mode 100644 docs/data/cancer.data.ts create mode 100644 docs/data/cancer.ts create mode 100644 docs/public/data/cancer.csv create mode 100644 docs/transforms/occlusion.md create mode 100644 src/transforms/occlusion.d.ts create mode 100644 src/transforms/occlusion.js create mode 100644 test/output/occlusionStocks.html create mode 100644 test/output/occlusionXPaths.svg create mode 100644 test/output/occlusionYPaths.svg create mode 100644 test/plots/occlusion.ts diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 5aaf69e194..6758e83336 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -121,6 +121,7 @@ export default defineConfig({ {text: "Interval", link: "/transforms/interval"}, {text: "Map", link: "/transforms/map"}, {text: "Normalize", link: "/transforms/normalize"}, + {text: "Occlusion", link: "/transforms/occlusion"}, {text: "Select", link: "/transforms/select"}, {text: "Shift", link: "/transforms/shift"}, {text: "Sort", link: "/transforms/sort"}, diff --git a/docs/data/cancer.data.ts b/docs/data/cancer.data.ts new file mode 100644 index 0000000000..71e78f81a8 --- /dev/null +++ b/docs/data/cancer.data.ts @@ -0,0 +1,9 @@ +import fs from "node:fs"; +import {csvParse} from "d3"; + +export default { + watch: ["../public/data/cancer.csv"], + load([file]) { + return csvParse(fs.readFileSync(file, "utf-8")); + } +}; diff --git a/docs/data/cancer.ts b/docs/data/cancer.ts new file mode 100644 index 0000000000..c8c2ec7d8b --- /dev/null +++ b/docs/data/cancer.ts @@ -0,0 +1,4 @@ +import {data} from "./cancer.data"; +import {autoType} from "d3"; + +export default data.map(({...d}) => autoType(d)); diff --git a/docs/public/data/cancer.csv b/docs/public/data/cancer.csv new file mode 100644 index 0000000000..71cb9b1516 --- /dev/null +++ b/docs/public/data/cancer.csv @@ -0,0 +1,97 @@ +name,year,survival +Prostate,5 Year,99 +Thyroid,5 Year,96 +Testis,5 Year,95 +Melanomas,5 Year,89 +Breast,5 Year,86 +Hodgkin’s disease,5 Year,85 +"Corpus uteri, uterus",5 Year,84 +"Urinary, bladder",5 Year,82 +"Cervix, uteri",5 Year,71 +Larynx,5 Year,69 +Rectum,5 Year,63 +"Kidney, renal pelvis",5 Year,62 +Colon,5 Year,62 +Non-Hodgkin’s,5 Year,58 +"Oral cavity, pharynx",5 Year,57 +Ovary,5 Year,55 +Leukemia,5 Year,43 +"Brain, nervous system",5 Year,32 +Multiple myeloma,5 Year,30 +Stomach,5 Year,24 +Lung and bronchus,5 Year,15 +Esophagus,5 Year,14 +"Liver, bile duct",5 Year,8 +Pancreas,5 Year,4 +Prostate,10 Year,95 +Thyroid,10 Year,96 +Testis,10 Year,94 +Melanomas,10 Year,87 +Breast,10 Year,78 +Hodgkin’s disease,10 Year,80 +"Corpus uteri, uterus",10 Year,83 +"Urinary, bladder",10 Year,76 +"Cervix, uteri",10 Year,64 +Larynx,10 Year,57 +Rectum,10 Year,55 +"Kidney, renal pelvis",10 Year,54 +Colon,10 Year,55 +Non-Hodgkin’s,10 Year,46 +"Oral cavity, pharynx",10 Year,44 +Ovary,10 Year,49 +Leukemia,10 Year,32 +"Brain, nervous system",10 Year,29 +Multiple myeloma,10 Year,13 +Stomach,10 Year,19 +Lung and bronchus,10 Year,11 +Esophagus,10 Year,8 +"Liver, bile duct",10 Year,6 +Pancreas,10 Year,3 +Prostate,15 Year,87 +Thyroid,15 Year,94 +Testis,15 Year,91 +Melanomas,15 Year,84 +Breast,15 Year,71 +Hodgkin’s disease,15 Year,74 +"Corpus uteri, uterus",15 Year,81 +"Urinary, bladder",15 Year,70 +"Cervix, uteri",15 Year,63 +Larynx,15 Year,46 +Rectum,15 Year,52 +"Kidney, renal pelvis",15 Year,50 +Colon,15 Year,54 +Non-Hodgkin’s,15 Year,38 +"Oral cavity, pharynx",15 Year,38 +Ovary,15 Year,50 +Leukemia,15 Year,30 +"Brain, nervous system",15 Year,28 +Multiple myeloma,15 Year,7 +Stomach,15 Year,19 +Lung and bronchus,15 Year,8 +Esophagus,15 Year,8 +"Liver, bile duct",15 Year,6 +Pancreas,15 Year,3 +Prostate,20 Year,81 +Thyroid,20 Year,95 +Testis,20 Year,88 +Melanomas,20 Year,83 +Breast,20 Year,65 +Hodgkin’s disease,20 Year,67 +"Corpus uteri, uterus",20 Year,79 +"Urinary, bladder",20 Year,68 +"Cervix, uteri",20 Year,60 +Larynx,20 Year,38 +Rectum,20 Year,49 +"Kidney, renal pelvis",20 Year,47 +Colon,20 Year,52 +Non-Hodgkin’s,20 Year,34 +"Oral cavity, pharynx",20 Year,33 +Ovary,20 Year,50 +Leukemia,20 Year,26 +"Brain, nervous system",20 Year,26 +Multiple myeloma,20 Year,5 +Stomach,20 Year,15 +Lung and bronchus,20 Year,6 +Esophagus,20 Year,5 +"Liver, bile duct",20 Year,8 +Pancreas,20 Year,3 \ No newline at end of file diff --git a/docs/transforms/occlusion.md b/docs/transforms/occlusion.md new file mode 100644 index 0000000000..af57fb6583 --- /dev/null +++ b/docs/transforms/occlusion.md @@ -0,0 +1,133 @@ + + +# Occlusion transform + +Given a position dimension (either **x** or **y**), the **occlusion** transform rearranges the values along that dimension in such a way that the distance between nodes is greater than or equal to the minimum distance, and their visual order preserved. The [occlusionX transform](#occlusionX) rearranges the **x** (horizontal) position of each series of nodes sharing a common **y** (vertical) position; likewise the [occlusionY transform](#occlusionY) rearranges nodes vertically. + +The occlusion transform is commonly used to prevent superposition of labels on line charts. The example below, per [Edward Tufte](https://www.edwardtufte.com/bboard/q-and-a-fetch-msg?msg_id=0003nk), represents estimates of survival rates per type of cancer after 5, 10, 15 and 20 years. Each data point is labelled with its actual value (rounded to the unit). Labels in the last column indicate the type. + +:::plot +```js +Plot.plot({ + width: 400, + height: 600, + marginRight: 60, + marginBottom: 20, + x: { + axis: "top", + domain: ["5 Year", "10 Year", "15 Year", "20 Year"], + label: null, + padding: 0 + }, + y: { axis: null, insetTop: 20 }, + marks: [ + Plot.line(cancer, {x: "year", y: "survival", z: "name", strokeWidth: 1}), + Plot.text(cancer, Plot.occlusionY({ + text: "survival", + x: "year", + y: "survival", + textAnchor: "end", + dx: 5, + fontVariant: "tabular-nums", + stroke: "var(--plot-background)", + strokeWidth: 7, + fill: "currentColor", + tip: true + })), + Plot.text(cancer, Plot.occlusionY({ + filter: d => d.year === "20 Year", + text: "name", + textAnchor: "start", + frameAnchor: "right", + dx: 10, + y: "survival" + })) + ], + caption: "Estimates of survival rate (%), per type of cancer" +}) +``` + +Without this transform, some of these labels would otherwise be masking each other. Note that when several labels share an identical position and text contents, only the first one is retained—and the others are filtered out (for example, check the value 62 in the first column). + +The **minDistance** option is a constant indicating the minimum distance between nodes, in pixels. It defaults to 11, about the height of a line of text with the default font size. (If zero, the transform is not applied.) + +The chart below shows how the positions are transformed as we repeatedly inject nodes into a collection, at a random vertical position, and apply the occlusionY transform at each step (horizontal axis). Adjust the range slider below to see how the positions change with the mininmum distance option: + +

+ +

+ +:::plot +```js +Plot.plot({ + y: {axis: null, inset: 25}, + color: {type: "categorical"}, + marks: [ + Plot.line(points, Plot.occlusionY(minDistance, { + x: "step", + stroke: "node", + y: "y", + curve: "basis", + strokeWidth: 1 + })), + Plot.dot(points, Plot.occlusionY(minDistance, { + x: "step", + fill: "node", + r: (d) => d.step === d.node, + y: "y" + })), + ] +}) +``` +::: + +The occlusion transform differs from the [dodge transform](./dodge.md) in that it only adjusts the nodes’ existing positions. + +The occlusion transform can be used with any mark that supports **x** and **y** position. + +## Occlusion options + +The occlusion transforms accept the following option: + +* **minDistance** — the number of pixels separating the nodes’ positions + +## occlusionY(*occlusionOptions*, *options*) {#occlusionY} + +```js +Plot.occlusionY(minDistance, {x: "date", y: "value"}) +``` + +Given marks arranged along the *y* axis, the occlusionY transform adjusts their vertical positions in such a way that two nodes are separated by at least *minDistance* pixels, avoiding overlapping. The order of the nodes is preserved. The *x* position channel, if present, is used to determine series on which the transform is applied, and left unchanged. + +## occlusionX(*occlusionOptions*, *options*) {#occlusionX} + +```js +Plot.occlusionX({x: "value"}) +``` + +Equivalent to Plot.occlusionY, but arranging the marks horizontally by returning an updated *x* position channel that avoids overlapping. The *y* position channel, if present, is used to determine series and left unchanged. diff --git a/src/index.d.ts b/src/index.d.ts index dcaa949da8..31df02f11a 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -52,6 +52,7 @@ export * from "./transforms/group.js"; export * from "./transforms/hexbin.js"; export * from "./transforms/map.js"; export * from "./transforms/normalize.js"; +export * from "./transforms/occlusion.js"; export * from "./transforms/select.js"; export * from "./transforms/shift.js"; export * from "./transforms/stack.js"; diff --git a/src/index.js b/src/index.js index 9fde7ce2d5..fea75f0330 100644 --- a/src/index.js +++ b/src/index.js @@ -39,6 +39,7 @@ export {find, group, groupX, groupY, groupZ} from "./transforms/group.js"; export {hexbin} from "./transforms/hexbin.js"; export {normalize, normalizeX, normalizeY} from "./transforms/normalize.js"; export {map, mapX, mapY} from "./transforms/map.js"; +export {occlusionX, occlusionY} from "./transforms/occlusion.js"; export {shiftX} from "./transforms/shift.js"; export {window, windowX, windowY} from "./transforms/window.js"; export {select, selectFirst, selectLast, selectMaxX, selectMaxY, selectMinX, selectMinY} from "./transforms/select.js"; diff --git a/src/transforms/occlusion.d.ts b/src/transforms/occlusion.d.ts new file mode 100644 index 0000000000..8e6a96b398 --- /dev/null +++ b/src/transforms/occlusion.d.ts @@ -0,0 +1,59 @@ +import type {ChannelValueSpec} from "../channel.js"; +import type {Initialized} from "./basic.js"; + +/** Options for the occlusion transform. */ +export interface OcclusionOptions { + /** + * A constant in pixels describing the minimum distance between two nodes. + * Defaults to 11. + */ + minDistance?: number; +} + +/** Options for the occlusionX transform. */ +export interface OcclusionXOptions extends OcclusionOptions { + /** + * The vertical position. Nodes sharing the same vertical position will be + * rearranged horizontally together. + */ + y?: ChannelValueSpec; +} + +/** Options for the occlusionY transform. */ +export interface OcclusionYOptions extends OcclusionOptions { + /** + * The horizontal position. Nodes sharing the same horizontal position will be + * rearranged vertically together. + */ + x?: ChannelValueSpec; +} + +/** + * Given an **x** position channel, rearranges the values in such a way that the + * horizontal distance between nodes is greater than or equal to the minimum + * distance, and their visual order preserved. Nodes that share the same + * position and text are fused together. + * + * If *occlusionOptions* is a number, it is shorthand for the occlusion + * **minDistance**. + */ +export function occlusionX(options?: T & OcclusionXOptions): Initialized; +export function occlusionX( + occlusionOptions?: OcclusionXOptions | OcclusionXOptions["minDistance"], + options?: T +): Initialized; + +/** + * Given a **y** position channel, rearranges the values in such a way that the + * vertical distance between nodes is greater than or equal to the minimum + * distance, and their visual order preserved. Nodes that share the same + * position and text are fused together. + * + * If *occlusionOptions* is a number, it is shorthand for the occlusion + * **minDistance**. + */ +export function occlusionY(options?: T & OcclusionYOptions): Initialized; +export function occlusionY( + dodgeOptions?: OcclusionYOptions | OcclusionYOptions["minDistance"], + options?: T +): Initialized; diff --git a/src/transforms/occlusion.js b/src/transforms/occlusion.js new file mode 100644 index 0000000000..d270593f03 --- /dev/null +++ b/src/transforms/occlusion.js @@ -0,0 +1,85 @@ +import {bisector, group} from "d3"; +import {valueof} from "../options.js"; +import {initializer} from "./basic.js"; + +export function occlusionX(occlusionOptions = {}, options = {}) { + if (arguments.length === 1) [occlusionOptions, options] = mergeOptions(occlusionOptions); + const {minDistance = 11} = maybeDistance(occlusionOptions); + return occlusion("x", "y", minDistance, options); +} + +export function occlusionY(occlusionOptions = {}, options = {}) { + if (arguments.length === 1) [occlusionOptions, options] = mergeOptions(occlusionOptions); + const {minDistance = 11} = maybeDistance(occlusionOptions); + return occlusion("y", "x", minDistance, options); +} + +function maybeDistance(minDistance) { + return typeof minDistance === "number" ? {minDistance} : minDistance; +} +function mergeOptions({minDistance, ...options}) { + return [{minDistance}, options]; +} + +function occlusion(k, h, minDistance, options) { + const sk = k[0]; // e.g., the scale for x1 is x + if (typeof minDistance !== "number" || !(minDistance >= 0)) throw new Error(`unsupported minDistance ${minDistance}`); + if (minDistance === 0) return options; + return initializer(options, function (data, facets, {[k]: channel, text}, {[sk]: s}) { + const {value: K, scale} = channel ?? {}; + if (K === undefined) throw new Error(`missing channel ${k}`); + const T = text?.value; + const H = valueof(data, options[h]); + const bisect = bisector((d) => d.lo).left; + + for (const facet of facets) { + for (const index of H ? group(facet, (i) => H[i]).values() : [facet]) { + const I = []; + const unique = new Set(); + const groups = []; + for (const i of index) { + if (scale === sk) K[i] = s(K[i]); + + // Remove empty and duplicate labels. + if (T) { + if (T[i] == null || unique.has(`${K[i]} ${T[i]}`)) { + K[i] = NaN; + continue; + } + unique.add(`${K[i]} ${T[i]}`); + } + + I.push(i); + } + + for (const i of index) { + let j = bisect(groups, K[i]); + groups.splice(j, 0, {lo: K[i], hi: K[i], items: [i]}); + + // Merge overlapping groups. + while ( + groups[j + 1]?.lo < groups[j].hi + minDistance || + (groups[j - 1]?.hi > groups[j].lo - minDistance && (--j, true)) + ) { + const items = groups[j].items.concat(groups[j + 1].items); + const mid = (Math.min(groups[j].lo, groups[j + 1].lo) + Math.max(groups[j].hi, groups[j + 1].hi)) / 2; + const w = (minDistance * (items.length - 1)) / 2; + groups.splice(j, 2, {lo: mid - w, hi: mid + w, items}); + } + } + + // Reposition elements within each group. + for (const {lo, hi, items} of groups) { + if (items.length > 1) { + const dist = (hi - lo) / (items.length - 1); + items.sort((i, j) => K[i] - K[j]); + let p = lo; + for (const i of items) (K[i] = p), (p += dist); + } + } + } + } + + return {data, facets, channels: {[k]: {value: K}}}; + }); +} diff --git a/test/output/occlusionStocks.html b/test/output/occlusionStocks.html new file mode 100644 index 0000000000..99c5d1aa32 --- /dev/null +++ b/test/output/occlusionStocks.html @@ -0,0 +1,163 @@ +
+
+ + + AAPL + + AMZN + + GOOG + + IBM +
+ + + + + + + + + + 2014 + 2015 + 2016 + 2017 + 2018 + + + + + + + + + + + + 60 + 70 + 79 + 77 + 94 + 99 + 109 + 124 + 127 + 110 + 105 + 110 + 96 + 113 + 116 + 144 + 144 + 154 + 172 + 167 + 284 + 321 + 398 + 343 + 332 + 317 + 309 + 370 + 437 + 521 + 637 + 599 + 726 + 837 + 754 + 892 + 954 + 959 + 1,189 + 1,372 + 438 + 441 + 553 + 564 + 579 + 565 + 522 + 540 + 522 + 611 + 742 + 750 + 699 + 773 + 786 + 839 + 899 + 953 + 1,065 + 1,006 + 192 + 186 + 186 + 195 + 186 + 187 + 162 + 159 + 164 + 144 + 136 + 153 + 152 + 158 + 167 + 175 + 156 + 147 + 154 + 150 + + + AAPL + AMZN + GOOG + IBM + + +
\ No newline at end of file diff --git a/test/output/occlusionXPaths.svg b/test/output/occlusionXPaths.svg new file mode 100644 index 0000000000..1f64192c09 --- /dev/null +++ b/test/output/occlusionXPaths.svg @@ -0,0 +1,5330 @@ + + + + + + + + + + + + + + + + + 0 + 10 + 20 + 30 + 40 + 50 + 60 + 70 + 80 + 90 + 100 + + + ↑ y + + + + + + + + + + + + + + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + + + x → + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/output/occlusionYPaths.svg b/test/output/occlusionYPaths.svg new file mode 100644 index 0000000000..a534d6ad8c --- /dev/null +++ b/test/output/occlusionYPaths.svg @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/plots/index.ts b/test/plots/index.ts index 0a3ee3eb0d..0bf85be097 100644 --- a/test/plots/index.ts +++ b/test/plots/index.ts @@ -176,6 +176,7 @@ export * from "./movies-rating-by-genre.js"; export * from "./multiplication-table.js"; export * from "./music-revenue.js"; export * from "./npm-versions.js"; +export * from "./occlusion.js"; export * from "./opacity.js"; export * from "./ordinal-bar.js"; export * from "./pairs.js"; diff --git a/test/plots/occlusion.ts b/test/plots/occlusion.ts new file mode 100644 index 0000000000..38a7575c94 --- /dev/null +++ b/test/plots/occlusion.ts @@ -0,0 +1,105 @@ +import * as Plot from "@observablehq/plot"; +import * as d3 from "d3"; + +export async function occlusionXPaths() { + const random = d3.randomNormal.source(d3.randomLcg(42))(5, 2); + const data = []; + const points = []; + for (let i = 0; i < 101; ++i) { + data.push(random()); + points.push(...data.map((d, e) => ({x: d, y: i, e}))); + } + return Plot.plot({ + x: {domain: [0, 10]}, + marks: [ + Plot.line(points, Plot.occlusionX(6, {x: "x", z: "e", y: "y", strokeOpacity: 0.3, strokeWidth: 0.5})), + Plot.dot( + points, + Plot.occlusionX( + {minDistance: 6}, + {x: "x", r: 2, fill: "currentColor", fillOpacity: (d) => (d.y === d.e ? 1 : 0), y: "y"} + ) + ) + ] + }); +} + +export async function occlusionYPaths() { + const random = d3.randomLcg(42); + const data = []; + const points = []; + let i; + for (i = 0; i < 31; ++i) { + data.push(random()); + points.push(...data.map((d, e) => ({x: i, y: d, e}))); + } + points.push(...data.map((d, e) => ({x: i, y: d, e}))); + return Plot.plot({ + axis: null, + y: {inset: 25}, + color: {scheme: "Observable10"}, + marks: [ + Plot.line(points, Plot.occlusionY({x: "x", stroke: "e", y: "y", curve: "basis", strokeWidth: 1})), + Plot.dot(points, Plot.occlusionY({x: "x", fill: "e", r: (d) => d.x === d.e, y: "y"})) + ] + }); +} + +async function loadSymbol(name) { + const Symbol = name.toUpperCase(); + return d3.csv(`data/${name}.csv`, (d) => ({Symbol, ...d3.autoType(d)})); +} + +export async function occlusionStocks() { + const stocks = (await Promise.all(["aapl", "amzn", "goog", "ibm"].map(loadSymbol))).flat(); + return Plot.plot({ + insetTop: 4, + insetRight: 15, + y: {axis: null}, + color: {legend: true}, + marks: [ + Plot.ruleY([0]), + Plot.lineY(stocks, {x: "Date", y: "Close", stroke: "Symbol"}), + Plot.text( + stocks, + Plot.occlusionY( + Plot.binX( + { + x: "first", + y: "first", + text: "first", + thresholds: "3 months" + }, + { + filter: ( + (k) => (d) => + d.Date > k + )(new Date("2013-07-01")), + x: "Date", + y: "Close", + text: (d) => Math.round(d.Close), + fill: "black", + stroke: "white", + z: "Symbol" + } + ) + ) + ), + Plot.text( + stocks, + Plot.occlusionY( + Plot.selectMaxX({ + dx: 4, + textAnchor: "start", + x: "Date", + y: "Close", + text: "Symbol", + fill: "black", + stroke: "white", + z: "Symbol" + }) + ) + ) + ] + }); +} From 81ab511a57dd51c1f5ad49738b6f5590b42fb161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Fri, 22 Dec 2023 19:11:58 +0100 Subject: [PATCH 2/8] typo --- docs/transforms/occlusion.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/transforms/occlusion.md b/docs/transforms/occlusion.md index af57fb6583..10a07d5661 100644 --- a/docs/transforms/occlusion.md +++ b/docs/transforms/occlusion.md @@ -68,11 +68,11 @@ Plot.plot({ }) ``` -Without this transform, some of these labels would otherwise be masking each other. Note that when several labels share an identical position and text contents, only the first one is retained—and the others are filtered out (for example, check the value 62 in the first column). +Without this transform, some of these labels would otherwise be masking each other. Note that when several labels share an identical position and text contents, only the first one is retained—and the others are filtered out (for example, value 62 in the first column). The **minDistance** option is a constant indicating the minimum distance between nodes, in pixels. It defaults to 11, about the height of a line of text with the default font size. (If zero, the transform is not applied.) -The chart below shows how the positions are transformed as we repeatedly inject nodes into a collection, at a random vertical position, and apply the occlusionY transform at each step (horizontal axis). Adjust the range slider below to see how the positions change with the mininmum distance option: +The chart below shows how the positions are transformed as we repeatedly inject nodes into a collection at a random vertical position, and apply the occlusionY transform at each step (horizontal axis). Adjust the range slider below to see how the positions change with the minimum distance option:

+ + + + + + + + + 5 Year + 10 Year + 15 Year + 20 Year + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 99 + 96 + 96 + 95 + 95 + 95 + 89 + 86 + 85 + 84 + 84 + 82 + 71 + 71 + 69 + 63 + 63 + 62 + 58 + 57 + 57 + 55 + 55 + 43 + 32 + 32 + 30 + 30 + 24 + 15 + 15 + 14 + 8 + 8 + 8 + 8 + 4 + 94 + 94 + 87 + 87 + 78 + 80 + 83 + 83 + 76 + 64 + 54 + 54 + 46 + 46 + 44 + 49 + 49 + 29 + 13 + 19 + 19 + 11 + 6 + 6 + 6 + 3 + 3 + 3 + 91 + 74 + 81 + 81 + 70 + 52 + 52 + 50 + 50 + 38 + 38 + 28 + 7 + 88 + 65 + 67 + 79 + 68 + 60 + 47 + 34 + 33 + 26 + 5 + + + Prostate + Thyroid + Testis + Melanomas + Breast + Hodgkin’s disease + Corpus uteri, uterus + Urinary, bladder + Cervix, uteri + Larynx + Rectum + Kidney, renal pelvis + Colon + Non-Hodgkin’s + Oral cavity, pharynx + Ovary + Leukemia + Brain, nervous system + Multiple myeloma + Stomach + Lung and bronchus + Esophagus + Liver, bile duct + Pancreas + + + +
Estimates of survival rate (%), per type of cancer
+
\ No newline at end of file From 96ab53808c6db7e5786bd61f460ac17546081eed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Thu, 5 Jun 2025 23:21:48 +0200 Subject: [PATCH 7/8] update tests --- test/output/occlusionCancer.html | 206 +- test/output/occlusionStocks.html | 190 +- test/output/occlusionXPaths.svg | 10318 ++++++++++++++--------------- test/output/occlusionYPaths.svg | 62 +- 4 files changed, 5388 insertions(+), 5388 deletions(-) diff --git a/test/output/occlusionCancer.html b/test/output/occlusionCancer.html index c526a8ad9e..37f2cf7493 100644 --- a/test/output/occlusionCancer.html +++ b/test/output/occlusionCancer.html @@ -13,7 +13,7 @@ white-space: pre; } - + 99 - 96 - 96 - 95 - 95 - 95 - 89 - 86 - 85 - 84 - 84 - 82 - 71 - 71 + 96 + 96 + 95 + 95 + 95 + 89 + 86 + 85 + 84 + 84 + 82 + 71 + 71 69 - 63 + 63 63 - 62 - 58 - 57 - 57 - 55 - 55 - 43 - 32 - 32 - 30 - 30 - 24 - 15 + 62 + 58 + 57 + 57 + 55 + 55 + 43 + 32 + 32 + 30 + 30 + 24 + 15 15 - 14 - 8 - 8 - 8 - 8 - 4 - 94 - 94 + 14 + 8 + 8 + 8 + 8 + 4 + 94 + 94 87 87 - 78 - 80 - 83 - 83 - 76 - 64 - 54 - 54 - 46 - 46 - 44 - 49 - 49 - 29 - 13 - 19 - 19 - 11 - 6 - 6 - 6 + 78 + 80 + 83 + 83 + 76 + 64 + 54 + 54 + 46 + 46 + 44 + 49 + 49 + 29 + 13 + 19 + 19 + 11 + 6 + 6 + 6 3 - 3 - 3 - 91 - 74 + 3 + 3 + 91 + 74 81 81 - 70 - 52 - 52 - 50 - 50 - 38 - 38 - 28 - 7 - 88 - 65 - 67 - 79 - 68 - 60 - 47 - 34 - 33 - 26 - 5 + 70 + 52 + 52 + 50 + 50 + 38 + 38 + 28 + 7 + 88 + 65 + 67 + 79 + 68 + 60 + 47 + 34 + 33 + 26 + 5 Prostate - Thyroid - Testis - Melanomas - Breast - Hodgkin’s disease - Corpus uteri, uterus - Urinary, bladder - Cervix, uteri - Larynx - Rectum - Kidney, renal pelvis - Colon - Non-Hodgkin’s - Oral cavity, pharynx - Ovary - Leukemia - Brain, nervous system - Multiple myeloma + Thyroid + Testis + Melanomas + Breast + Hodgkin’s disease + Corpus uteri, uterus + Urinary, bladder + Cervix, uteri + Larynx + Rectum + Kidney, renal pelvis + Colon + Non-Hodgkin’s + Oral cavity, pharynx + Ovary + Leukemia + Brain, nervous system + Multiple myeloma Stomach - Lung and bronchus - Esophagus - Liver, bile duct - Pancreas + Lung and bronchus + Esophagus + Liver, bile duct + Pancreas diff --git a/test/output/occlusionStocks.html b/test/output/occlusionStocks.html index 99c5d1aa32..b15554a836 100644 --- a/test/output/occlusionStocks.html +++ b/test/output/occlusionStocks.html @@ -48,19 +48,19 @@ white-space: pre; } - - - - - - + - 2014 - 2015 - 2016 - 2017 - 2018 + 2014 + 2015 + 2016 + 2017 + 2018 @@ -72,92 +72,92 @@ - 60 - 70 - 79 - 77 - 94 - 99 - 109 - 124 - 127 - 110 - 105 - 110 - 96 - 113 - 116 - 144 - 144 - 154 - 172 - 167 - 284 - 321 - 398 - 343 - 332 - 317 - 309 - 370 - 437 - 521 - 637 - 599 - 726 - 837 - 754 - 892 - 954 - 959 - 1,189 - 1,372 - 438 - 441 - 553 - 564 - 579 - 565 - 522 - 540 - 522 - 611 - 742 - 750 - 699 - 773 - 786 - 839 - 899 - 953 - 1,065 - 1,006 - 192 - 186 - 186 - 195 - 186 - 187 - 162 - 159 - 164 - 144 - 136 - 153 - 152 - 158 - 167 - 175 - 156 - 147 - 154 - 150 + 60 + 70 + 79 + 77 + 94 + 99 + 109 + 124 + 127 + 110 + 105 + 110 + 96 + 113 + 116 + 144 + 144 + 154 + 172 + 167 + 284 + 321 + 398 + 343 + 332 + 317 + 309 + 370 + 437 + 521 + 637 + 599 + 726 + 837 + 754 + 892 + 954 + 959 + 1,189 + 1,372 + 438 + 441 + 553 + 564 + 579 + 565 + 522 + 540 + 522 + 611 + 742 + 750 + 699 + 773 + 786 + 839 + 899 + 953 + 1,065 + 1,006 + 192 + 186 + 186 + 195 + 186 + 187 + 162 + 159 + 164 + 144 + 136 + 153 + 152 + 158 + 167 + 175 + 156 + 147 + 154 + 150 - AAPL - AMZN - GOOG - IBM + AAPL + AMZN + GOOG + IBM \ No newline at end of file diff --git a/test/output/occlusionXPaths.svg b/test/output/occlusionXPaths.svg index 1f64192c09..41be1ab1ba 100644 --- a/test/output/occlusionXPaths.svg +++ b/test/output/occlusionXPaths.svg @@ -13,7 +13,7 @@ white-space: pre; } - + @@ -34,15 +34,15 @@ 40 50 60 - 70 - 80 - 90 + 70 + 80 + 90 100 ↑ y - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/output/occlusionYPaths.svg b/test/output/occlusionYPaths.svg index a534d6ad8c..03673f058f 100644 --- a/test/output/occlusionYPaths.svg +++ b/test/output/occlusionYPaths.svg @@ -47,36 +47,36 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From df5062ec85f38972ae0bb640870db08cfd51a511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Fri, 6 Jun 2025 15:41:14 +0200 Subject: [PATCH 8/8] =?UTF-8?q?occlusion=20=E2=86=92=20repel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/.vitepress/config.ts | 2 +- docs/transforms/{occlusion.md => repel.md} | 38 +++++++++---------- src/index.d.ts | 2 +- src/index.js | 2 +- src/transforms/{occlusion.d.ts => repel.d.ts} | 30 ++++++--------- src/transforms/{occlusion.js => repel.js} | 18 ++++----- ...{occlusionCancer.html => repelCancer.html} | 0 ...{occlusionStocks.html => repelStocks.html} | 0 .../{occlusionXPaths.svg => repelXPaths.svg} | 0 .../{occlusionYPaths.svg => repelYPaths.svg} | 0 test/plots/index.ts | 2 +- test/plots/{occlusion.ts => repel.ts} | 24 ++++++------ 12 files changed, 56 insertions(+), 62 deletions(-) rename docs/transforms/{occlusion.md => repel.md} (53%) rename src/transforms/{occlusion.d.ts => repel.d.ts} (56%) rename src/transforms/{occlusion.js => repel.js} (78%) rename test/output/{occlusionCancer.html => repelCancer.html} (100%) rename test/output/{occlusionStocks.html => repelStocks.html} (100%) rename test/output/{occlusionXPaths.svg => repelXPaths.svg} (100%) rename test/output/{occlusionYPaths.svg => repelYPaths.svg} (100%) rename test/plots/{occlusion.ts => repel.ts} (85%) diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 92bc05d061..cdf1e3ecde 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -133,7 +133,7 @@ export default defineConfig({ {text: "Interval", link: "/transforms/interval"}, {text: "Map", link: "/transforms/map"}, {text: "Normalize", link: "/transforms/normalize"}, - {text: "Occlusion", link: "/transforms/occlusion"}, + {text: "Repel", link: "/transforms/repel"}, {text: "Select", link: "/transforms/select"}, {text: "Shift", link: "/transforms/shift"}, {text: "Sort", link: "/transforms/sort"}, diff --git a/docs/transforms/occlusion.md b/docs/transforms/repel.md similarity index 53% rename from docs/transforms/occlusion.md rename to docs/transforms/repel.md index ead7c42f93..08fde5ef6a 100644 --- a/docs/transforms/occlusion.md +++ b/docs/transforms/repel.md @@ -7,7 +7,7 @@ import cancer from "../data/cancer.ts"; const minDistance = ref(11); -const points = (() => { +const points = (() => { const random = d3.randomLcg(42); const data = []; const points = []; @@ -21,11 +21,11 @@ const points = (() => { })(); -# Occlusion transform +# Repel transform -Given a position dimension (either **x** or **y**), the **occlusion** transform rearranges the values along that dimension in such a way that the distance between nodes is greater than or equal to the minimum distance, and their visual order preserved. The [occlusionX transform](#occlusionX) rearranges the **x** (horizontal) position of each series of nodes sharing a common **y** (vertical) position; likewise the [occlusionY transform](#occlusionY) rearranges nodes vertically. +Given a position dimension (either **x** or **y**), the **repel** transform rearranges the values along that dimension in such a way that the distance between nodes is greater than or equal to the minimum distance, and their visual order preserved. The [repelX transform](#repelX) rearranges the **x** (horizontal) position of each series of nodes sharing a common **y** (vertical) position; likewise the [repelY transform](#repelY) rearranges nodes vertically. -The occlusion transform is commonly used to prevent superposition of labels on line charts. The example below, per [Edward Tufte](https://www.edwardtufte.com/bboard/q-and-a-fetch-msg?msg_id=0003nk), represents estimates of survival rates per type of cancer after 5, 10, 15 and 20 years. Each data point is labelled with its actual value (rounded to the unit). Labels in the last column indicate the type. +The repel transform is commonly used to prevent superposition of labels on line charts. The example below, per [Edward Tufte](https://www.edwardtufte.com/bboard/q-and-a-fetch-msg?msg_id=0003nk), represents estimates of survival rates per type of cancer after 5, 10, 15 and 20 years. Each data point is labelled with its actual value (rounded to the unit). Labels in the last column indicate the type. :::plot ```js @@ -43,7 +43,7 @@ Plot.plot({ y: { axis: null, insetTop: 20 }, marks: [ Plot.line(cancer, {x: "year", y: "survival", z: "name", strokeWidth: 1}), - Plot.text(cancer, Plot.occlusionY( + Plot.text(cancer, Plot.repelY( Plot.group({ text:"first" }, { @@ -58,7 +58,7 @@ Plot.plot({ fill: "currentColor" }) )), - Plot.text(cancer, Plot.occlusionY({ + Plot.text(cancer, Plot.repelY({ filter: d => d.year === "20 Year", text: "name", textAnchor: "start", @@ -75,7 +75,7 @@ Without this transform, some of these labels would otherwise be masking each oth The **minDistance** option is a constant indicating the minimum distance between nodes, in pixels. It defaults to 11, about the height of a line of text with the default font size. (If zero, the transform is not applied.) -The chart below shows how the positions are transformed as we repeatedly inject nodes into a collection at a random vertical position, and apply the occlusionY transform at each step (horizontal axis). Adjust the range slider below to see how the positions change with the minimum distance option: +The chart below shows how the positions are transformed as we repeatedly inject nodes into a collection at a random vertical position, and apply the repelY transform at each step (horizontal axis). Adjust the range slider below to see how the positions change with the minimum distance option: