This is an automated email from the ASF dual-hosted git repository. plainheart pushed a commit to branch jitter-rng in repository https://gitbox.apache.org/repos/asf/echarts.git
commit 02edddaef7cebae1053f1ed1dc392395559964e2 Author: plainheart <[email protected]> AuthorDate: Wed Sep 16 17:15:13 2026 +0800 feat(scatter): add `jitterRng` option to configure a custom random number generator or a random seed for deterministic jitter layout --- src/chart/scatter/jitterLayout.ts | 7 +++++-- src/coord/axisCommonTypes.ts | 1 + src/util/jitter.ts | 20 ++++++++++++-------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/chart/scatter/jitterLayout.ts b/src/chart/scatter/jitterLayout.ts index a68ea6550..44d9b1024 100644 --- a/src/chart/scatter/jitterLayout.ts +++ b/src/chart/scatter/jitterLayout.ts @@ -26,6 +26,7 @@ import createRenderPlanner from '../helper/createRenderPlanner'; import { COORD_SYS_TYPE_CARTESIAN_2D } from '../../coord/cartesian/GridModel'; import { COORD_SYS_TYPE_SINGLE } from '../../coord/single/AxisModel'; import { validateUpstreamOutputRange } from '../../util/model'; +import type { AxisBaseModel } from '../../coord/AxisBaseModel'; export default function jitterLayout(): StageHandler { return { @@ -58,6 +59,8 @@ export default function jitterLayout(): StageHandler { return; } + const jitterRng = (baseAxis.model as AxisBaseModel).get('jitterRng'); + return { progress(params, data): void { const points = data.getLayout('points') as Float32Array; @@ -79,7 +82,7 @@ export default function jitterLayout(): StageHandler { if (jitterOnY) { // x is fixed, and y is floating - const jittered = fixJitter(baseAxis, layout[0], layout[1], size / 2); + const jittered = fixJitter(baseAxis, layout[0], layout[1], size / 2, jitterRng); if (hasPoints) { points[offset + 1] = jittered; } @@ -89,7 +92,7 @@ export default function jitterLayout(): StageHandler { } else if (jitterOnX) { // y is fixed, and x is floating - const jittered = fixJitter(baseAxis, layout[1], layout[0], size / 2); + const jittered = fixJitter(baseAxis, layout[1], layout[0], size / 2, jitterRng); if (hasPoints) { points[offset] = jittered; } diff --git a/src/coord/axisCommonTypes.ts b/src/coord/axisCommonTypes.ts index 058dea900..cd57a673d 100644 --- a/src/coord/axisCommonTypes.ts +++ b/src/coord/axisCommonTypes.ts @@ -128,6 +128,7 @@ export interface AxisBaseOptionCommon extends ComponentOption, jitter?: number; jitterOverlap?: boolean; jitterMargin?: number; + jitterRng?: () => number; breaks?: AxisBreakOption[]; breakArea?: { diff --git a/src/util/jitter.ts b/src/util/jitter.ts index c0e1e394e..352d3f971 100644 --- a/src/util/jitter.ts +++ b/src/util/jitter.ts @@ -61,7 +61,8 @@ export function fixJitter( fixedAxis: Axis2D | SingleAxis, fixedCoord: number, floatCoord: number, - radius: number + radius: number, + rng?: () => number ): number { if (fixedAxis instanceof Axis2D) { const scaleType = fixedAxis.scale.type; @@ -80,25 +81,27 @@ export function fixJitter( const bandWidth = isOrdinalScale(fixedAxis.scale) ? calcBandWidth(fixedAxis).w : null; + !rng && (rng = Math.random); if (jitterOverlap) { - return fixJitterIgnoreOverlaps(floatCoord, jitter, bandWidth, radius); + return fixJitterIgnoreOverlaps(floatCoord, jitter, bandWidth, radius, rng); } - return fixJitterAvoidOverlaps(fixedAxis, fixedCoord, floatCoord, radius, jitter, jitterMargin); + return fixJitterAvoidOverlaps(fixedAxis, fixedCoord, floatCoord, radius, jitter, jitterMargin, rng); } function fixJitterIgnoreOverlaps( floatCoord: number, jitter: number, bandWidth: number | null, - radius: number + radius: number, + rng: () => number ): number { // Don't clamp single axis if (bandWidth === null) { - return floatCoord + (Math.random() - 0.5) * jitter; + return floatCoord + (rng() - 0.5) * jitter; } const maxJitter = bandWidth - radius * 2; const actualJitter = Math.min(Math.max(0, jitter), maxJitter); - return floatCoord + (Math.random() - 0.5) * actualJitter; + return floatCoord + (rng() - 0.5) * actualJitter; } function fixJitterAvoidOverlaps( @@ -107,7 +110,8 @@ function fixJitterAvoidOverlaps( floatCoord: number, radius: number, jitter: number, - margin: number + margin: number, + rng: () => number ): number { const store = inner(fixedAxis); if (!store.items) { @@ -129,7 +133,7 @@ function fixJitterAvoidOverlaps( if (distance > jitter / 2 || (bandWidth && distance > bandWidth / 2 - radius)) { // If the new item is moved too far, then give up. // Fall back to random jitter. - return fixJitterIgnoreOverlaps(floatCoord, jitter, bandWidth, radius); + return fixJitterIgnoreOverlaps(floatCoord, jitter, bandWidth, radius, rng); } // Add new point to array --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
