@jbrowse/render-core 5.0.0-beta.11 → 5.0.0-beta.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/OverlayCanvas.d.ts +3 -2
- package/esm/OverlayCanvas.js +4 -2
- package/esm/RenderCanvas.d.ts +4 -3
- package/esm/RenderCanvas.js +4 -2
- package/esm/ScrollLockedOverlay.d.ts +2 -2
- package/esm/ScrollLockedOverlay.js +3 -2
- package/esm/blockClipUtils.d.ts +5 -5
- package/esm/blockClipUtils.js +4 -3
- package/esm/canvas2dUtils.d.ts +15 -21
- package/esm/canvas2dUtils.js +25 -55
- package/esm/colorRampLut.d.ts +2 -2
- package/esm/colorRampLut.js +2 -2
- package/esm/coverageBand.d.ts +0 -1
- package/esm/coverageBand.js +0 -5
- package/esm/coverageBandBuffers.d.ts +8 -5
- package/esm/createRenderingBackend.d.ts +1 -1
- package/esm/displayPhase.d.ts +5 -9
- package/esm/displayPhase.js +2 -2
- package/esm/hal/mockHal.d.ts +3 -4
- package/esm/hal/mockHal.js +5 -4
- package/esm/hal/types.d.ts +5 -5
- package/esm/hal/webgl2Hal.d.ts +4 -1
- package/esm/hal/webgl2Hal.js +18 -6
- package/esm/hal/webgpuHal.js +2 -2
- package/esm/linkFeet.d.ts +4 -0
- package/esm/linkFeet.js +7 -0
- package/esm/marks/barMark.d.ts +1 -1
- package/esm/marks/barMark.js +4 -2
- package/esm/marks/colorFill.d.ts +4 -4
- package/esm/marks/colorFill.js +4 -4
- package/esm/marks/connectorMark.d.ts +48 -0
- package/esm/marks/connectorMark.js +285 -0
- package/esm/marks/drawAgainstHit.d.ts +1 -1
- package/esm/marks/drawAgainstHit.js +4 -4
- package/esm/marks/glyphPaint.d.ts +5 -4
- package/esm/marks/glyphPaint.js +17 -7
- package/esm/marks/index.d.ts +8 -5
- package/esm/marks/index.js +6 -4
- package/esm/marks/lineMark.d.ts +47 -7
- package/esm/marks/lineMark.js +53 -39
- package/esm/marks/linkMark.d.ts +43 -6
- package/esm/marks/linkMark.js +118 -21
- package/esm/marks/markBackend.d.ts +1 -1
- package/esm/marks/markBackend.js +1 -1
- package/esm/marks/markPlan.d.ts +4 -3
- package/esm/marks/markPlan.js +5 -4
- package/esm/marks/markRamp.d.ts +11 -11
- package/esm/marks/markRamp.js +9 -9
- package/esm/marks/pointMark.d.ts +11 -5
- package/esm/marks/pointMark.js +34 -9
- package/esm/marks/rowLane.d.ts +4 -2
- package/esm/marks/rowLane.js +7 -3
- package/esm/marks/rowTable.d.ts +5 -5
- package/esm/marks/rowTable.js +4 -4
- package/esm/marks/ruleMark.js +1 -0
- package/esm/marks/spanMark.d.ts +8 -7
- package/esm/marks/spanMark.js +3 -3
- package/esm/marks/types.d.ts +8 -5
- package/esm/marks/types.js +8 -1
- package/esm/perRegionRenderingBackend.d.ts +1 -1
- package/esm/perRegionRenderingBackend.js +1 -1
- package/esm/regionDataMap.d.ts +2 -2
- package/esm/regionDataMap.js +2 -2
- package/esm/shaders/barMark.glsl.generated.d.ts +1 -1
- package/esm/shaders/barMark.glsl.generated.js +1 -1
- package/esm/shaders/barMark.wgsl.generated.d.ts +1 -1
- package/esm/shaders/barMark.wgsl.generated.js +1 -1
- package/esm/shaders/capsule.js.generated.d.ts +1 -1
- package/esm/shaders/capsule.js.generated.js +2 -2
- package/esm/shaders/connectorMark.consts.generated.d.ts +11 -0
- package/esm/shaders/connectorMark.consts.generated.js +13 -0
- package/esm/shaders/{coverageIndicator.generated.d.ts → connectorMark.generated.d.ts} +2 -1
- package/esm/shaders/connectorMark.generated.js +8 -0
- package/esm/shaders/connectorMark.glsl.generated.d.ts +2 -0
- package/esm/shaders/connectorMark.glsl.generated.js +4 -0
- package/esm/shaders/connectorMark.iface.generated.d.ts +88 -0
- package/esm/shaders/connectorMark.iface.generated.js +1194 -0
- package/esm/shaders/connectorMark.js.generated.d.ts +5 -0
- package/esm/shaders/connectorMark.js.generated.js +47 -0
- package/esm/shaders/connectorMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/connectorMark.wgsl.generated.js +3 -0
- package/esm/shaders/coverageInterbase.glsl.generated.d.ts +1 -1
- package/esm/shaders/coverageInterbase.glsl.generated.js +1 -1
- package/esm/shaders/coverageInterbase.wgsl.generated.d.ts +1 -1
- package/esm/shaders/coverageInterbase.wgsl.generated.js +1 -1
- package/esm/shaders/curveDistance.js.generated.d.ts +4 -0
- package/esm/shaders/curveDistance.js.generated.js +113 -0
- package/esm/shaders/insertionGlyph.generated.d.ts +6 -0
- package/esm/shaders/insertionGlyph.generated.js +8 -0
- package/esm/shaders/lineCenterMark.glsl.generated.d.ts +2 -2
- package/esm/shaders/lineCenterMark.glsl.generated.js +2 -2
- package/esm/shaders/lineCenterMark.iface.generated.d.ts +11 -9
- package/esm/shaders/lineCenterMark.iface.generated.js +20 -18
- package/esm/shaders/lineCenterMark.wgsl.generated.d.ts +1 -1
- package/esm/shaders/lineCenterMark.wgsl.generated.js +1 -1
- package/esm/shaders/lineCommon.js.generated.d.ts +1 -0
- package/esm/shaders/lineCommon.js.generated.js +20 -0
- package/esm/shaders/lineStepMark.glsl.generated.d.ts +2 -2
- package/esm/shaders/lineStepMark.glsl.generated.js +2 -2
- package/esm/shaders/lineStepMark.iface.generated.d.ts +11 -9
- package/esm/shaders/lineStepMark.iface.generated.js +20 -18
- package/esm/shaders/lineStepMark.wgsl.generated.d.ts +1 -1
- package/esm/shaders/lineStepMark.wgsl.generated.js +1 -1
- package/esm/shaders/linkMark.consts.generated.d.ts +1 -0
- package/esm/shaders/linkMark.consts.generated.js +1 -0
- package/esm/shaders/linkMark.glsl.generated.d.ts +2 -2
- package/esm/shaders/linkMark.glsl.generated.js +2 -2
- package/esm/shaders/linkMark.js.generated.d.ts +4 -2
- package/esm/shaders/linkMark.js.generated.js +33 -4
- package/esm/shaders/linkMark.wgsl.generated.d.ts +1 -1
- package/esm/shaders/linkMark.wgsl.generated.js +1 -1
- package/esm/shaders/pointMark.glsl.generated.d.ts +1 -1
- package/esm/shaders/pointMark.glsl.generated.js +1 -1
- package/esm/shaders/pointMark.iface.generated.d.ts +10 -8
- package/esm/shaders/pointMark.iface.generated.js +18 -16
- package/esm/shaders/pointMark.wgsl.generated.d.ts +1 -1
- package/esm/shaders/pointMark.wgsl.generated.js +1 -1
- package/esm/shaders/spanMark.glsl.generated.d.ts +1 -1
- package/esm/shaders/spanMark.glsl.generated.js +1 -1
- package/esm/shaders/spanMark.wgsl.generated.d.ts +1 -1
- package/esm/shaders/spanMark.wgsl.generated.js +1 -1
- package/esm/slangPass.d.ts +5 -3
- package/esm/slangPass.js +2 -2
- package/package.json +7 -4
- package/src/shaders/arcHull.test.ts +87 -0
- package/src/shaders/barMark.glsl.generated.ts +1 -1
- package/src/shaders/barMark.slang +2 -2
- package/src/shaders/barMark.wgsl.generated.ts +1 -1
- package/src/shaders/capsule.js.generated.ts +2 -2
- package/src/shaders/capsule.slang +69 -24
- package/src/shaders/clipStrip.slang +1 -1
- package/src/shaders/colorPack.slang +2 -2
- package/src/shaders/colorRampLut.slang +7 -7
- package/src/shaders/connectorMark.consts.generated.ts +24 -0
- package/src/shaders/connectorMark.generated.ts +12 -0
- package/src/shaders/connectorMark.glsl.generated.ts +6 -0
- package/src/shaders/connectorMark.iface.generated.ts +1270 -0
- package/src/shaders/connectorMark.js.generated.ts +52 -0
- package/src/shaders/connectorMark.slang +287 -0
- package/src/shaders/connectorMark.wgsl.generated.ts +4 -0
- package/src/shaders/coverageBand.slang +10 -12
- package/src/shaders/coverageInterbase.glsl.generated.ts +1 -1
- package/src/shaders/coverageInterbase.slang +24 -21
- package/src/shaders/coverageInterbase.wgsl.generated.ts +1 -1
- package/src/shaders/coverageSnp.slang +1 -1
- package/src/shaders/curveDistance.js.generated.ts +112 -0
- package/src/shaders/curveDistance.slang +104 -1
- package/src/shaders/farDome.test.ts +117 -0
- package/src/shaders/hpClampFloor.test.ts +0 -4
- package/src/shaders/insertionGlyph.generated.ts +14 -0
- package/src/shaders/insertionGlyph.slang +59 -0
- package/src/shaders/lineCenterMark.glsl.generated.ts +2 -2
- package/src/shaders/lineCenterMark.iface.generated.ts +21 -18
- package/src/shaders/lineCenterMark.slang +19 -23
- package/src/shaders/lineCenterMark.wgsl.generated.ts +1 -1
- package/src/shaders/lineCommon.js.generated.ts +20 -0
- package/src/shaders/lineCommon.slang +18 -8
- package/src/shaders/lineStepMark.glsl.generated.ts +2 -2
- package/src/shaders/lineStepMark.iface.generated.ts +21 -18
- package/src/shaders/lineStepMark.slang +10 -6
- package/src/shaders/lineStepMark.wgsl.generated.ts +1 -1
- package/src/shaders/linkMark.consts.generated.ts +2 -0
- package/src/shaders/linkMark.glsl.generated.ts +2 -2
- package/src/shaders/linkMark.js.generated.ts +30 -4
- package/src/shaders/linkMark.slang +67 -24
- package/src/shaders/linkMark.wgsl.generated.ts +1 -1
- package/src/shaders/markColor.slang +10 -10
- package/src/shaders/pointMark.glsl.generated.ts +1 -1
- package/src/shaders/pointMark.iface.generated.ts +19 -16
- package/src/shaders/pointMark.slang +12 -8
- package/src/shaders/pointMark.wgsl.generated.ts +1 -1
- package/src/shaders/rowRect.slang +3 -3
- package/src/shaders/rowTable.slang +12 -8
- package/src/shaders/scoreScale.slang +1 -1
- package/src/shaders/spanMark.glsl.generated.ts +1 -1
- package/src/shaders/spanMark.slang +5 -5
- package/src/shaders/spanMark.wgsl.generated.ts +1 -1
- package/src/shaders/viewRegion.slang +13 -0
- package/esm/shaders/coverageIndicator.generated.js +0 -7
- package/esm/shaders/coverageIndicator.glsl.generated.d.ts +0 -2
- package/esm/shaders/coverageIndicator.glsl.generated.js +0 -4
- package/esm/shaders/coverageIndicator.iface.generated.d.ts +0 -85
- package/esm/shaders/coverageIndicator.iface.generated.js +0 -114
- package/esm/shaders/coverageIndicator.wgsl.generated.d.ts +0 -1
- package/esm/shaders/coverageIndicator.wgsl.generated.js +0 -3
- package/src/shaders/coverageIndicator.generated.ts +0 -11
- package/src/shaders/coverageIndicator.glsl.generated.ts +0 -6
- package/src/shaders/coverageIndicator.iface.generated.ts +0 -172
- package/src/shaders/coverageIndicator.slang +0 -61
- package/src/shaders/coverageIndicator.wgsl.generated.ts +0 -4
package/esm/marks/index.d.ts
CHANGED
|
@@ -4,11 +4,13 @@ export { planMarks } from './markPlan.ts';
|
|
|
4
4
|
export { inkOfInstances, shiftInk } from './markInk.ts';
|
|
5
5
|
export { backToFront, nearestMarkHit, valueWindow } from './nearestMarkHit.ts';
|
|
6
6
|
export { barMark } from './barMark.ts';
|
|
7
|
-
export { pointMark } from './pointMark.ts';
|
|
7
|
+
export { pointInstance, pointMark } from './pointMark.ts';
|
|
8
8
|
export { ruleMark } from './ruleMark.ts';
|
|
9
9
|
export { spanMark } from './spanMark.ts';
|
|
10
|
-
export { lineCenterMark, lineMarkOf, lineStepMark } from './lineMark.ts';
|
|
11
|
-
export { linkFeet, linkMark, LINK_ELSEWHERE, LINK_NO_REGION, } from './linkMark.ts';
|
|
10
|
+
export { BandPen, lineCenterMark, lineMarkOf, lineStepMark, } from './lineMark.ts';
|
|
11
|
+
export { linkApex, linkLabelAnchor, linkFeet, linkInstance, linkMark, LINK_ELSEWHERE, LINK_NO_REGION, } from './linkMark.ts';
|
|
12
|
+
export { connectorEnds, connectorMark } from './connectorMark.ts';
|
|
13
|
+
export { CONNECTOR_ARROW, CONNECTOR_BOW, CONNECTOR_DASHED, CONNECTOR_LEADING_2, CONNECTOR_MAX_REACH_PX, CONNECTOR_MINUS_1, CONNECTOR_MINUS_2, CONNECTOR_STRAIGHT, } from '../shaders/connectorMark.consts.generated.ts';
|
|
12
14
|
export { ellipseDistance, ellipseNearest } from './ellipseDistance.ts';
|
|
13
15
|
export { appendGlyph, pointInsetPx } from './glyphPaint.ts';
|
|
14
16
|
export { recordPath } from './pathRecorder.ts';
|
|
@@ -16,7 +18,7 @@ export { abgrToCssRgba, makeAbgrFill } from './colorFill.ts';
|
|
|
16
18
|
export { RAMP_NOT_A_NUMBER_COLOR, RAMP_NO_VALUE_BITS, RAMP_NO_VALUE_COLOR, } from '../shaders/markColor.generated.ts';
|
|
17
19
|
export { instanceColor, isThreshold, keepRampValues, paintColors, rampUniforms, rampValueBits, thresholdBandOf, } from './markRamp.ts';
|
|
18
20
|
export { HIDDEN_ROW, NO_ROW_COLOR, RowKeys, buildRowTable } from './rowTable.ts';
|
|
19
|
-
export { keySlot, rowColor, rowSlot } from './rowLane.ts';
|
|
21
|
+
export { keySlot, rowColor, rowSlot, rowTableKeys, rowTableTextures, } from './rowLane.ts';
|
|
20
22
|
export { rowSpanIndex, spansInRow } from './rowSpanIndex.ts';
|
|
21
23
|
export type { MarkPlan } from './markPlan.ts';
|
|
22
24
|
export type { MarkInstance } from './markInk.ts';
|
|
@@ -27,7 +29,8 @@ export type { PointChannels, PointParams } from './pointMark.ts';
|
|
|
27
29
|
export type { RuleChannels, RuleParams } from './ruleMark.ts';
|
|
28
30
|
export type { SpanChannels, SpanParams } from './spanMark.ts';
|
|
29
31
|
export type { LineChannels, LineInterpolate, LineParams } from './lineMark.ts';
|
|
30
|
-
export type { LinkChannels, LinkParams, LinkRegion, LinkSizeScale, } from './linkMark.ts';
|
|
32
|
+
export type { LinkApex, LinkChannels, LinkParams, LinkRegion, LinkSizeScale, } from './linkMark.ts';
|
|
33
|
+
export type { ConnectorChannels, ConnectorParams } from './connectorMark.ts';
|
|
31
34
|
export type { RowTable } from './rowTable.ts';
|
|
32
35
|
export type { RowSpanIndex } from './rowSpanIndex.ts';
|
|
33
36
|
export type { ColorChannel } from './markRamp.ts';
|
package/esm/marks/index.js
CHANGED
|
@@ -4,11 +4,13 @@ export { planMarks } from './markPlan.js';
|
|
|
4
4
|
export { inkOfInstances, shiftInk } from './markInk.js';
|
|
5
5
|
export { backToFront, nearestMarkHit, valueWindow } from './nearestMarkHit.js';
|
|
6
6
|
export { barMark } from './barMark.js';
|
|
7
|
-
export { pointMark } from './pointMark.js';
|
|
7
|
+
export { pointInstance, pointMark } from './pointMark.js';
|
|
8
8
|
export { ruleMark } from './ruleMark.js';
|
|
9
9
|
export { spanMark } from './spanMark.js';
|
|
10
|
-
export { lineCenterMark, lineMarkOf, lineStepMark } from './lineMark.js';
|
|
11
|
-
export { linkFeet, linkMark, LINK_ELSEWHERE, LINK_NO_REGION, } from './linkMark.js';
|
|
10
|
+
export { BandPen, lineCenterMark, lineMarkOf, lineStepMark, } from './lineMark.js';
|
|
11
|
+
export { linkApex, linkLabelAnchor, linkFeet, linkInstance, linkMark, LINK_ELSEWHERE, LINK_NO_REGION, } from './linkMark.js';
|
|
12
|
+
export { connectorEnds, connectorMark } from './connectorMark.js';
|
|
13
|
+
export { CONNECTOR_ARROW, CONNECTOR_BOW, CONNECTOR_DASHED, CONNECTOR_LEADING_2, CONNECTOR_MAX_REACH_PX, CONNECTOR_MINUS_1, CONNECTOR_MINUS_2, CONNECTOR_STRAIGHT, } from '../shaders/connectorMark.consts.generated.js';
|
|
12
14
|
export { ellipseDistance, ellipseNearest } from './ellipseDistance.js';
|
|
13
15
|
export { appendGlyph, pointInsetPx } from './glyphPaint.js';
|
|
14
16
|
export { recordPath } from './pathRecorder.js';
|
|
@@ -16,5 +18,5 @@ export { abgrToCssRgba, makeAbgrFill } from './colorFill.js';
|
|
|
16
18
|
export { RAMP_NOT_A_NUMBER_COLOR, RAMP_NO_VALUE_BITS, RAMP_NO_VALUE_COLOR, } from '../shaders/markColor.generated.js';
|
|
17
19
|
export { instanceColor, isThreshold, keepRampValues, paintColors, rampUniforms, rampValueBits, thresholdBandOf, } from './markRamp.js';
|
|
18
20
|
export { HIDDEN_ROW, NO_ROW_COLOR, RowKeys, buildRowTable } from './rowTable.js';
|
|
19
|
-
export { keySlot, rowColor, rowSlot } from './rowLane.js';
|
|
21
|
+
export { keySlot, rowColor, rowSlot, rowTableKeys, rowTableTextures, } from './rowLane.js';
|
|
20
22
|
export { rowSpanIndex, spansInRow } from './rowSpanIndex.js';
|
package/esm/marks/lineMark.d.ts
CHANGED
|
@@ -1,25 +1,34 @@
|
|
|
1
1
|
import type { ColorChannel } from './markRamp.ts';
|
|
2
2
|
import type { RowChannel, RowParams } from './rowLane.ts';
|
|
3
|
-
import type { MarkColorScale, MarkShape } from './types.ts';
|
|
3
|
+
import type { MarkColorScale, MarkContext2D, MarkShape } from './types.ts';
|
|
4
4
|
import type { MarkValueScale } from './valueScale.ts';
|
|
5
5
|
/**
|
|
6
6
|
* The `line` shape's channels: a stroke through consecutive instances of one
|
|
7
|
-
* row
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* row and one `series`, in the order given, each instance a span from `x` to
|
|
8
|
+
* `x2` at `y` on the value scale. The caller hands each series' instances
|
|
9
|
+
* together; a change of row or series starts a new run, and absent, a row is
|
|
10
|
+
* one series. `gapBp` is the linear variant's break: a midpoint further than
|
|
11
|
+
* that from the previous one starts a new run. Both ride with the data, as
|
|
12
|
+
* the neighbour fields the packer derives from them do, so both backends
|
|
13
|
+
* break in the same place.
|
|
12
14
|
*/
|
|
13
15
|
export interface LineChannels extends ColorChannel, RowChannel {
|
|
14
16
|
x: Uint32Array;
|
|
15
17
|
x2: Uint32Array;
|
|
16
18
|
y: Float32Array;
|
|
19
|
+
series?: Uint32Array;
|
|
17
20
|
gapBp?: number;
|
|
18
21
|
count: number;
|
|
19
22
|
}
|
|
20
23
|
export interface LineParams extends RowParams, MarkValueScale {
|
|
21
|
-
/** The quantitative
|
|
24
|
+
/** The quantitative color scale, for a line whose color is a ramp or a threshold. */
|
|
22
25
|
colorScale?: MarkColorScale;
|
|
26
|
+
/**
|
|
27
|
+
* Whether the color field is the plotted `y`. Only then does a threshold's
|
|
28
|
+
* cut sit on the y scale, so the line changes color where it crosses one;
|
|
29
|
+
* otherwise each instance takes its own value's color.
|
|
30
|
+
*/
|
|
31
|
+
colorFromY?: boolean;
|
|
23
32
|
/** The value a step drops to across a gap. */
|
|
24
33
|
origin: number;
|
|
25
34
|
/** Stroke width in CSS px. */
|
|
@@ -29,6 +38,37 @@ export interface LineParams extends RowParams, MarkValueScale {
|
|
|
29
38
|
}
|
|
30
39
|
/** `step` holds each value across its span; `linear` joins the spans' midpoints. */
|
|
31
40
|
export type LineInterpolate = 'step' | 'linear';
|
|
41
|
+
/**
|
|
42
|
+
* What the two tracers hand a pen: the polyline's points, each with the y its
|
|
43
|
+
* color is read at where that differs from the y it is drawn at, and the
|
|
44
|
+
* instance the next points belong to.
|
|
45
|
+
*/
|
|
46
|
+
export interface LinePen {
|
|
47
|
+
instance(i: number): void;
|
|
48
|
+
moveTo(x: number, y: number, colorY?: number): void;
|
|
49
|
+
lineTo(x: number, y: number, colorY?: number): void;
|
|
50
|
+
/** Reads the next segment's color from `colorY` without moving the pen. */
|
|
51
|
+
recolor(colorY: number): void;
|
|
52
|
+
endRun(): void;
|
|
53
|
+
end(): void;
|
|
54
|
+
}
|
|
55
|
+
export declare class BandPen implements LinePen {
|
|
56
|
+
private readonly ctx;
|
|
57
|
+
private readonly top;
|
|
58
|
+
private readonly bottom;
|
|
59
|
+
private x;
|
|
60
|
+
private y;
|
|
61
|
+
private colorY;
|
|
62
|
+
private drawing;
|
|
63
|
+
private readonly path;
|
|
64
|
+
constructor(ctx: MarkContext2D, top: number, bottom: number);
|
|
65
|
+
instance(): void;
|
|
66
|
+
moveTo(x: number, y: number, colorY?: number): void;
|
|
67
|
+
recolor(colorY: number): void;
|
|
68
|
+
lineTo(x: number, y: number, colorY?: number): void;
|
|
69
|
+
endRun(): void;
|
|
70
|
+
end(): void;
|
|
71
|
+
}
|
|
32
72
|
/** The step variant: three square-capped quads an instance, flat filled. */
|
|
33
73
|
export declare const lineStepMark: MarkShape<LineChannels, LineParams>;
|
|
34
74
|
/** The linear variant: one round-capped capsule an instance, max blended. */
|
package/esm/marks/lineMark.js
CHANGED
|
@@ -2,6 +2,7 @@ import { bpRangeXTuple } from '../blockClipUtils.js';
|
|
|
2
2
|
import { CappedPath, getDpr, makeBpMapper } from '../canvas2dUtils.js';
|
|
3
3
|
import * as centerShader from '../shaders/lineCenterMark.generated.js';
|
|
4
4
|
import { GAP_Y, NO_PREV_X } from '../shaders/lineCommon.generated.js';
|
|
5
|
+
import { lineColorAlongY } from '../shaders/lineCommon.js.generated.js';
|
|
5
6
|
import * as stepShader from '../shaders/lineStepMark.generated.js';
|
|
6
7
|
import { valueToYPxScaled } from '../shaders/pointMark.js.generated.js';
|
|
7
8
|
import { rowBandTopPx } from '../shaders/rowTable.js.generated.js';
|
|
@@ -9,21 +10,22 @@ import { normalizeScoreUnclamped } from '../shaders/scoreScale.js.generated.js';
|
|
|
9
10
|
import { slangPass } from '../slangPass.js';
|
|
10
11
|
import { abgrToCssRgba } from './colorFill.js';
|
|
11
12
|
import { colorBits, isThreshold, paintColors, rampUniforms, } from './markRamp.js';
|
|
12
|
-
import { bandHeightPx, rowColor, rowLane, rowSlot, rowTableKeys, rowTableTextures, } from './rowLane.js';
|
|
13
|
+
import { bandHeightPx, rowColor, rowColorOverride, rowLane, rowSlot, rowTableKeys, rowTableTextures, } from './rowLane.js';
|
|
13
14
|
import { valueScaleUniforms } from './valueScale.js';
|
|
14
15
|
const NO_GAP = Number.POSITIVE_INFINITY;
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
// Whether instances `a` and `b` stand on one row and in one series.
|
|
17
|
+
function sameRun(c, row, a, b) {
|
|
18
|
+
return row[a] === row[b] && (!c.series || c.series[a] === c.series[b]);
|
|
17
19
|
}
|
|
18
|
-
// Whether instance `i` abuts the one before it
|
|
20
|
+
// Whether instance `i` abuts the one before it in its run, so the step joins
|
|
19
21
|
// them at a shared corner.
|
|
20
22
|
function abutsBefore(c, row, i) {
|
|
21
|
-
return i > 0 &&
|
|
23
|
+
return i > 0 && sameRun(c, row, i - 1, i) && c.x2[i - 1] === c.x[i];
|
|
22
24
|
}
|
|
23
25
|
// Whether the linear variant links instance `i` to the one before it: the same
|
|
24
|
-
// row, within `gapBp` midpoint to midpoint.
|
|
26
|
+
// row and series, within `gapBp` midpoint to midpoint.
|
|
25
27
|
function linksBefore(c, row, i) {
|
|
26
|
-
if (i === 0 || !
|
|
28
|
+
if (i === 0 || !sameRun(c, row, i - 1, i)) {
|
|
27
29
|
return false;
|
|
28
30
|
}
|
|
29
31
|
const gapBp = c.gapBp ?? NO_GAP;
|
|
@@ -66,6 +68,7 @@ function writeLineUniforms(scratch, clip, block, frame, params) {
|
|
|
66
68
|
domainMax: params.domain[1],
|
|
67
69
|
...valueScaleUniforms(params),
|
|
68
70
|
...rampUniforms(params.colorScale),
|
|
71
|
+
colorFromY: params.colorFromY ? 1 : 0,
|
|
69
72
|
rowHeight,
|
|
70
73
|
rowBandPx: params.rowBandPx ?? rowHeight,
|
|
71
74
|
rowOffsetPx: params.rowOffsetPx ?? 0,
|
|
@@ -96,16 +99,16 @@ function valueYPx(g, value, slot) {
|
|
|
96
99
|
return (rowBandTopPx(g.rowOffsetPx, g.pitch, slot) +
|
|
97
100
|
valueToYPxScaled(value, g.domainMin, g.domainMax, g.band, g.scaleType, g.symlogConstant));
|
|
98
101
|
}
|
|
99
|
-
// A value's y in the band for the
|
|
102
|
+
// A value's y in the band for the color test, placed UNCLAMPED where
|
|
100
103
|
// `valueYPx` clamps it to draw: the cuts go through it, and so does the line a
|
|
101
|
-
// threshold
|
|
104
|
+
// threshold colors along, as the shader's `lineColorYPx` does.
|
|
102
105
|
function colorYPx(g, value, slot) {
|
|
103
106
|
const norm = normalizeScoreUnclamped(value, g.domainMin, g.domainMax, g.scaleType, g.symlogConstant);
|
|
104
107
|
return rowBandTopPx(g.rowOffsetPx, g.pitch, slot) + (1 - norm) * g.band;
|
|
105
108
|
}
|
|
106
|
-
// One
|
|
107
|
-
// the pen, so a segment carries the
|
|
108
|
-
// the shader's per-instance
|
|
109
|
+
// One color per instance: a change ends the stroke batch and reopens it from
|
|
110
|
+
// the pen, so a segment carries the color of the instance it belongs to, as
|
|
111
|
+
// the shader's per-instance color does.
|
|
109
112
|
class InstancePen {
|
|
110
113
|
ctx;
|
|
111
114
|
colorOf;
|
|
@@ -152,7 +155,7 @@ class InstancePen {
|
|
|
152
155
|
}
|
|
153
156
|
// The part of the segment from y0 to y1, as a fraction of it, inside the band
|
|
154
157
|
// top < y <= bottom; undefined where it misses. A band owns its lower edge,
|
|
155
|
-
// so a line lying on a cut takes the
|
|
158
|
+
// so a line lying on a cut takes the color above it, as the shader does.
|
|
156
159
|
function bandSpan(y0, y1, top, bottom) {
|
|
157
160
|
if (y0 === y1) {
|
|
158
161
|
return top < y0 && y0 <= bottom ? [0, 1] : undefined;
|
|
@@ -166,10 +169,10 @@ function bandSpan(y0, y1, top, bottom) {
|
|
|
166
169
|
: undefined;
|
|
167
170
|
}
|
|
168
171
|
// Keeps the part of each segment inside one threshold band, cut where it
|
|
169
|
-
// crosses the band's edges, so a line stroked once per band changes
|
|
170
|
-
// where the shader's fragments do. A point's
|
|
172
|
+
// crosses the band's edges, so a line stroked once per band changes color
|
|
173
|
+
// where the shader's fragments do. A point's color y is where the band test
|
|
171
174
|
// reads it and its y where it draws, which part where the drawn y is clamped.
|
|
172
|
-
class BandPen {
|
|
175
|
+
export class BandPen {
|
|
173
176
|
ctx;
|
|
174
177
|
top;
|
|
175
178
|
bottom;
|
|
@@ -225,8 +228,8 @@ class BandPen {
|
|
|
225
228
|
}
|
|
226
229
|
// The step polyline over `indices`, in order: a rise from the origin, the top
|
|
227
230
|
// across the span, the step at each joint and a drop to the origin at a gap.
|
|
228
|
-
// A rise is
|
|
229
|
-
//
|
|
231
|
+
// A rise is colored along the y it is drawn at, a level run by its value's
|
|
232
|
+
// color y, as the shader reads them.
|
|
230
233
|
function traceStep(pen, c, g, rows, indices, origin) {
|
|
231
234
|
const { x, x2, y, row, count } = c;
|
|
232
235
|
let inRun = false;
|
|
@@ -281,29 +284,39 @@ function traceCenter(pen, c, g, rows, indices) {
|
|
|
281
284
|
pen.lineTo(cx, cy, colorY);
|
|
282
285
|
}
|
|
283
286
|
}
|
|
284
|
-
//
|
|
285
|
-
//
|
|
286
|
-
//
|
|
287
|
+
// Along y, the line is stroked once per band per row, each pass keeping the
|
|
288
|
+
// parts of the polyline inside its band; otherwise once, in each instance's
|
|
289
|
+
// own color. A row the table recolors takes its override either way.
|
|
287
290
|
function paintLine(ctx, c, g, params, trace) {
|
|
288
291
|
const { row, count } = c;
|
|
289
292
|
const scale = params.colorScale;
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
293
|
+
const bySlot = new Map();
|
|
294
|
+
const own = [];
|
|
295
|
+
const { rampMode } = rampUniforms(scale);
|
|
296
|
+
const colorFromY = params.colorFromY ? 1 : 0;
|
|
297
|
+
for (let i = 0; i < count; i++) {
|
|
298
|
+
const slot = rowSlot(row, i, g.table);
|
|
299
|
+
if (slot === undefined) {
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
const overridden = rowColorOverride(row, i, g.table) !== undefined;
|
|
303
|
+
if (lineColorAlongY(rampMode, colorFromY, overridden)) {
|
|
304
|
+
const held = bySlot.get(slot);
|
|
305
|
+
if (held) {
|
|
306
|
+
held.push(i);
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
bySlot.set(slot, [i]);
|
|
302
310
|
}
|
|
303
311
|
}
|
|
312
|
+
else {
|
|
313
|
+
own.push(i);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (scale && isThreshold(scale)) {
|
|
304
317
|
const { cuts, colors } = scale;
|
|
305
318
|
for (const [slot, indices] of bySlot) {
|
|
306
|
-
const cutYs = cuts.map(cut => colorYPx(g, cut, slot));
|
|
319
|
+
const cutYs = cuts.map(cut => colorYPx(g, Math.fround(cut), slot));
|
|
307
320
|
for (let k = 0; k <= cuts.length; k++) {
|
|
308
321
|
ctx.strokeStyle = abgrToCssRgba(colors[k] ?? 0);
|
|
309
322
|
const pen = new BandPen(ctx, k < cuts.length ? cutYs[k] : Number.NEGATIVE_INFINITY, k > 0 ? cutYs[k - 1] : Number.POSITIVE_INFINITY);
|
|
@@ -311,12 +324,13 @@ function paintLine(ctx, c, g, params, trace) {
|
|
|
311
324
|
pen.end();
|
|
312
325
|
}
|
|
313
326
|
}
|
|
314
|
-
return;
|
|
315
327
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
328
|
+
if (own.length > 0) {
|
|
329
|
+
const color = paintColors(c, count, scale);
|
|
330
|
+
const pen = new InstancePen(ctx, i => rowColor(color[i], row, i, g.table));
|
|
331
|
+
trace(pen, own);
|
|
332
|
+
pen.end();
|
|
333
|
+
}
|
|
320
334
|
}
|
|
321
335
|
function paintStep(ctx, c, block, frame, params) {
|
|
322
336
|
if (c.count === 0) {
|
package/esm/marks/linkMark.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { LINK_ELSEWHERE, LINK_NO_REGION } from '../shaders/linkMark.consts.generated.ts';
|
|
2
|
+
import type { RenderBlock } from '../renderBlock.ts';
|
|
2
3
|
import type { ColorChannel } from './markRamp.ts';
|
|
3
4
|
import type { RowChannel, RowParams } from './rowLane.ts';
|
|
4
|
-
import type { MarkColorScale, MarkShape } from './types.ts';
|
|
5
|
+
import type { MarkFrame, MarkColorScale, MarkShape } from './types.ts';
|
|
5
6
|
import type { MarkValueScale } from './valueScale.ts';
|
|
6
7
|
/**
|
|
7
8
|
* The `link` shape's channels: a stroked curve from `x`, on the displayed
|
|
@@ -24,10 +25,9 @@ export interface LinkChannels extends ColorChannel, RowChannel {
|
|
|
24
25
|
feet?: Uint8Array;
|
|
25
26
|
count: number;
|
|
26
27
|
}
|
|
27
|
-
export
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
export declare function linkFeet(xDir: number, x2Dir: number): number;
|
|
28
|
+
export { LINK_FOOT_FORWARD, LINK_FOOT_REVERSE, linkFeet } from '../linkFeet.ts';
|
|
29
|
+
/** The channels cut down to instance `i`, for its own painter to trace alone. */
|
|
30
|
+
export declare function linkInstance(c: LinkChannels, i: number): LinkChannels;
|
|
31
31
|
/**
|
|
32
32
|
* One displayed region as a foot places through it: the canvas px of an
|
|
33
33
|
* anchor bp chosen near the view, so a foot's offset from it stays inside
|
|
@@ -49,7 +49,7 @@ export interface LinkSizeScale {
|
|
|
49
49
|
}
|
|
50
50
|
export type LinkShape = 'dome' | 'arc' | 'line';
|
|
51
51
|
export interface LinkParams extends RowParams, MarkValueScale {
|
|
52
|
-
/** The quantitative
|
|
52
|
+
/** The quantitative color scale, for a link whose color is a ramp or a threshold. */
|
|
53
53
|
colorScale?: MarkColorScale;
|
|
54
54
|
/** The view's displayed regions, indexed as `x2Region` and the block's own index are. */
|
|
55
55
|
regions: readonly LinkRegion[];
|
|
@@ -72,5 +72,42 @@ export interface LinkParams extends RowParams, MarkValueScale {
|
|
|
72
72
|
/** How far inside the band the value range ends, the axis's own. */
|
|
73
73
|
insetPx?: number;
|
|
74
74
|
}
|
|
75
|
+
export declare function regionPx(regions: readonly LinkRegion[], index: number, bp: number): number;
|
|
76
|
+
/**
|
|
77
|
+
* Where an instance's curve peaks as the painter and the shader draw it: the
|
|
78
|
+
* centreline's apex in canvas px and how far it stands off its baseline.
|
|
79
|
+
*/
|
|
80
|
+
export interface LinkApex {
|
|
81
|
+
x: number;
|
|
82
|
+
y: number;
|
|
83
|
+
rise: number;
|
|
84
|
+
/** The curve's half-span at its baseline; 0 for a line or a stem, which enclose nothing. */
|
|
85
|
+
halfWidth: number;
|
|
86
|
+
strokePx: number;
|
|
87
|
+
/** Canvas y per px toward the baseline: 1 standing on the band's foot, -1 hanging from its top. */
|
|
88
|
+
inward: 1 | -1;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Instance `i`'s apex, undefined where it draws none on the band: no curve
|
|
92
|
+
* at all, or a far pair's circle, which the band clips to its legs.
|
|
93
|
+
*/
|
|
94
|
+
export declare function linkApex(c: LinkChannels, block: RenderBlock, frame: MarkFrame, params: LinkParams, i: number): LinkApex | undefined;
|
|
95
|
+
/**
|
|
96
|
+
* Where a label of instance `i` stands: its apex, or for a far dome whose
|
|
97
|
+
* apex is off the canvas, the dome's point over the middle of the stretch the
|
|
98
|
+
* canvas shows, so a view inside the pair still carries the label. `rise` is
|
|
99
|
+
* the curve's height there.
|
|
100
|
+
*/
|
|
101
|
+
export declare function linkLabelAnchor(c: LinkChannels, block: RenderBlock, frame: MarkFrame, params: LinkParams, i: number): LinkApex | undefined;
|
|
102
|
+
/**
|
|
103
|
+
* The view's region table as a view-spanning shader's uniforms take it: the
|
|
104
|
+
* entries in use, their count, and the drawing region's own entry. Shared
|
|
105
|
+
* arrays, rewritten per call, so a frame allocates nothing per block.
|
|
106
|
+
*/
|
|
107
|
+
export declare function viewRegionUniforms(regions: readonly LinkRegion[], own: number): {
|
|
108
|
+
regionCount: number;
|
|
109
|
+
ownEntry: [number, number, number, number];
|
|
110
|
+
regionTable: [[number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number]];
|
|
111
|
+
};
|
|
75
112
|
export declare const linkMark: MarkShape<LinkChannels, LinkParams>;
|
|
76
113
|
export { LINK_ELSEWHERE, LINK_NO_REGION };
|
package/esm/marks/linkMark.js
CHANGED
|
@@ -1,22 +1,30 @@
|
|
|
1
1
|
import { getDpr } from '../canvas2dUtils.js';
|
|
2
2
|
import { SCALE_TYPE_LOG } from '../scoreScale.js';
|
|
3
|
-
import { distToWideCirclePx } from '../shaders/curveDistance.js.generated.js';
|
|
4
|
-
import { LINK_CURVE_SEGMENTS, LINK_ELSEWHERE,
|
|
3
|
+
import { distToWideCirclePx, farDomeParam, legSweepAngle, } from '../shaders/curveDistance.js.generated.js';
|
|
4
|
+
import { LINK_CURVE_SEGMENTS, LINK_ELSEWHERE, LINK_FOOT_PX, LINK_MAX_REGIONS, LINK_NO_REGION, LINK_NO_SIZE, LINK_LINE_MIN_PX, LINK_SHAPE_ARC, LINK_SHAPE_DOME, LINK_SHAPE_LINE, LINK_SIZE_CONSTANT, LINK_SIZE_SCALED, LINK_STEM_PX, } from '../shaders/linkMark.consts.generated.js';
|
|
5
5
|
import * as shader from '../shaders/linkMark.generated.js';
|
|
6
|
-
import { linkApexPx, linkBaseYPx, linkFootDir, linkFootLenPx, linkRadiiPx, linkStrokeWidthPx, linkValuePx, } from '../shaders/linkMark.js.generated.js';
|
|
6
|
+
import { linkApexHeld, linkApexPx, linkBaseYPx, linkFootDir, linkFootLenPx, linkIsFar, linkRadiiPx, linkStrokeWidthPx, linkValuePx, } from '../shaders/linkMark.js.generated.js';
|
|
7
7
|
import { slangPass } from '../slangPass.js';
|
|
8
8
|
import { abgrToCssRgba } from './colorFill.js';
|
|
9
9
|
import { ellipseNearest } from './ellipseDistance.js';
|
|
10
10
|
import { nearestInk } from './markHit.js';
|
|
11
|
-
import { colorBits, paintColors, rampUniforms } from './markRamp.js';
|
|
11
|
+
import { colorBits, instanceColor, paintColors, rampUniforms, } from './markRamp.js';
|
|
12
12
|
import { bandHeightPx, rowColor, rowLane, rowSlot, rowTableKeys, rowTableTextures, } from './rowLane.js';
|
|
13
13
|
import { valueScaleUniforms } from './valueScale.js';
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
export { LINK_FOOT_FORWARD, LINK_FOOT_REVERSE, linkFeet } from '../linkFeet.js';
|
|
15
|
+
/** The channels cut down to instance `i`, for its own painter to trace alone. */
|
|
16
|
+
export function linkInstance(c, i) {
|
|
17
|
+
return {
|
|
18
|
+
x: c.x.subarray(i, i + 1),
|
|
19
|
+
x2: c.x2.subarray(i, i + 1),
|
|
20
|
+
x2Region: c.x2Region.subarray(i, i + 1),
|
|
21
|
+
y: c.y?.subarray(i, i + 1),
|
|
22
|
+
size: c.size?.subarray(i, i + 1),
|
|
23
|
+
feet: c.feet?.subarray(i, i + 1),
|
|
24
|
+
row: c.row?.subarray(i, i + 1),
|
|
25
|
+
color: instanceColor(c.color, i),
|
|
26
|
+
count: 1,
|
|
27
|
+
};
|
|
20
28
|
}
|
|
21
29
|
const KIND_ELLIPSE = 0;
|
|
22
30
|
const KIND_CIRCLE = 1;
|
|
@@ -26,6 +34,7 @@ const KIND_LINE = 4;
|
|
|
26
34
|
// The painter's leg polyline: enough segments that the chord sagitta on any
|
|
27
35
|
// leg the band can show is under a pixel.
|
|
28
36
|
const LEG_SEGMENTS = 32;
|
|
37
|
+
const FAR_DOME_COARSE = 16;
|
|
29
38
|
function linkFrame(block, frame, params) {
|
|
30
39
|
const band = bandHeightPx(params, frame.canvasHeight);
|
|
31
40
|
const { valueScaleType, valueSymlogConstant } = valueScaleUniforms(params);
|
|
@@ -88,7 +97,7 @@ function linkShapeCode(shape) {
|
|
|
88
97
|
? LINK_SHAPE_LINE
|
|
89
98
|
: LINK_SHAPE_DOME;
|
|
90
99
|
}
|
|
91
|
-
function regionPx(regions, index, bp) {
|
|
100
|
+
export function regionPx(regions, index, bp) {
|
|
92
101
|
const r = regions[index];
|
|
93
102
|
return r.anchorPx + (bp - r.anchorBp) * r.signedPxPerBp;
|
|
94
103
|
}
|
|
@@ -164,25 +173,71 @@ function placeCurve(c, g, i) {
|
|
|
164
173
|
g.x2Px = regionPx(regions, region, c.x2[i]);
|
|
165
174
|
const pairHalf = Math.abs(g.x2Px - g.xPx) / 2;
|
|
166
175
|
const valuePx = linkValuePx(c.y ? c.y[i] : 0, g.domainMin, g.domainMax, g.band, g.scaleType, g.insetPx, g.symlogConstant);
|
|
167
|
-
const apex = linkApexPx(pairHalf, g.reach, g.shape, g.valued, valuePx);
|
|
176
|
+
const apex = linkApexPx(pairHalf, g.reach, g.strokePx / 2, g.shape, g.valued, valuePx);
|
|
168
177
|
if (g.shape === LINK_SHAPE_LINE) {
|
|
169
178
|
g.kind = KIND_LINE;
|
|
170
179
|
g.rx = Math.max(2 * pairHalf, LINK_LINE_MIN_PX);
|
|
171
180
|
g.ry = apex;
|
|
172
181
|
return;
|
|
173
182
|
}
|
|
174
|
-
const [rx, ry] = linkRadiiPx(pairHalf, apex, g.screenW);
|
|
183
|
+
const [rx, ry] = linkRadiiPx(pairHalf, apex, g.screenW, linkApexHeld(g.valued, c.y ? c.y[i] : 0, g.domainMax));
|
|
175
184
|
g.rx = rx;
|
|
176
185
|
g.ry = ry;
|
|
177
|
-
|
|
186
|
+
// A far pair with a low apex keeps its apex (`linkRadiiPx`) and is an
|
|
187
|
+
// ellipse still, traced as chords (`farDomePoints`).
|
|
188
|
+
if (linkIsFar(pairHalf, g.screenW) && ry >= rx) {
|
|
178
189
|
g.kind = KIND_CIRCLE;
|
|
179
|
-
g.legSweep =
|
|
190
|
+
g.legSweep = legSweepAngle(rx, g.reach + g.strokePx / 2);
|
|
180
191
|
g.legHeight = rx * Math.sin(g.legSweep);
|
|
181
192
|
}
|
|
182
193
|
else {
|
|
183
194
|
g.kind = KIND_ELLIPSE;
|
|
184
195
|
}
|
|
185
196
|
}
|
|
197
|
+
function apexOf(g) {
|
|
198
|
+
if (g.kind === KIND_NONE || g.kind === KIND_CIRCLE) {
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
x: (g.xPx + g.x2Px) / 2,
|
|
203
|
+
y: yAt(g, g.ry),
|
|
204
|
+
rise: g.ry,
|
|
205
|
+
halfWidth: g.kind === KIND_ELLIPSE ? g.rx : 0,
|
|
206
|
+
strokePx: g.strokePx,
|
|
207
|
+
inward: g.reverse ? -1 : 1,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Instance `i`'s apex, undefined where it draws none on the band: no curve
|
|
212
|
+
* at all, or a far pair's circle, which the band clips to its legs.
|
|
213
|
+
*/
|
|
214
|
+
export function linkApex(c, block, frame, params, i) {
|
|
215
|
+
const g = linkFrame(block, frame, params);
|
|
216
|
+
placeCurve(c, g, i);
|
|
217
|
+
return apexOf(g);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Where a label of instance `i` stands: its apex, or for a far dome whose
|
|
221
|
+
* apex is off the canvas, the dome's point over the middle of the stretch the
|
|
222
|
+
* canvas shows, so a view inside the pair still carries the label. `rise` is
|
|
223
|
+
* the curve's height there.
|
|
224
|
+
*/
|
|
225
|
+
export function linkLabelAnchor(c, block, frame, params, i) {
|
|
226
|
+
const g = linkFrame(block, frame, params);
|
|
227
|
+
placeCurve(c, g, i);
|
|
228
|
+
const apex = apexOf(g);
|
|
229
|
+
if (!apex || g.kind !== KIND_ELLIPSE || !linkIsFar(g.rx, g.screenW)) {
|
|
230
|
+
return apex;
|
|
231
|
+
}
|
|
232
|
+
const from = Math.max(apex.x - g.rx, 0);
|
|
233
|
+
const to = Math.min(apex.x + g.rx, g.screenW);
|
|
234
|
+
if (to <= from || (apex.x >= 0 && apex.x <= g.screenW)) {
|
|
235
|
+
return apex;
|
|
236
|
+
}
|
|
237
|
+
const x = (from + to) / 2;
|
|
238
|
+
const rise = g.ry * Math.sqrt(Math.max(1 - ((x - apex.x) / g.rx) ** 2, 0));
|
|
239
|
+
return { ...apex, x, y: yAt(g, rise), rise };
|
|
240
|
+
}
|
|
186
241
|
function sizeLane(c) {
|
|
187
242
|
const lane = new Float32Array(c.count);
|
|
188
243
|
for (let i = 0; i < c.count; i++) {
|
|
@@ -224,6 +279,18 @@ function fillTable(regions) {
|
|
|
224
279
|
}
|
|
225
280
|
return n;
|
|
226
281
|
}
|
|
282
|
+
/**
|
|
283
|
+
* The view's region table as a view-spanning shader's uniforms take it: the
|
|
284
|
+
* entries in use, their count, and the drawing region's own entry. Shared
|
|
285
|
+
* arrays, rewritten per call, so a frame allocates nothing per block.
|
|
286
|
+
*/
|
|
287
|
+
export function viewRegionUniforms(regions, own) {
|
|
288
|
+
return {
|
|
289
|
+
regionCount: fillTable(regions),
|
|
290
|
+
ownEntry: tableEntry(regions[own]),
|
|
291
|
+
regionTable: TABLE,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
227
294
|
// Canvas y of a point `up` px from the baseline on the drawn side.
|
|
228
295
|
function yAt(g, up) {
|
|
229
296
|
return g.baseY - g.ySign * up;
|
|
@@ -246,6 +313,34 @@ function legPoints(g, footX, legDir) {
|
|
|
246
313
|
}
|
|
247
314
|
return points;
|
|
248
315
|
}
|
|
316
|
+
// A far dome as chords from the foot nearer the canvas, stepped as the shader
|
|
317
|
+
// steps it: fine over the stretch the canvas shows, coarse over the rest. A
|
|
318
|
+
// canvas flattens an ellipse tens of thousands of px wide into too few curves
|
|
319
|
+
// to hold its feet.
|
|
320
|
+
function farDomePoints(g, left, right) {
|
|
321
|
+
const fromRight = g.screenW / 2 - left > g.rx;
|
|
322
|
+
const footX = fromRight ? right : left;
|
|
323
|
+
const dir = fromRight ? -1 : 1;
|
|
324
|
+
const margin = g.strokePx;
|
|
325
|
+
const u0 = farDomeParam(fromRight ? footX - g.screenW - margin : -margin - footX, g.rx);
|
|
326
|
+
const u1 = Math.max(u0, farDomeParam(fromRight ? footX + margin : g.screenW + margin - footX, g.rx));
|
|
327
|
+
const coarse = Array.from({ length: FAR_DOME_COARSE + 1 }, (_, k) => Math.sin(((k / FAR_DOME_COARSE) * Math.PI) / 2));
|
|
328
|
+
const fine = Array.from({ length: LINK_CURVE_SEGMENTS + 1 }, (_, k) => u0 + ((u1 - u0) * k) / LINK_CURVE_SEGMENTS);
|
|
329
|
+
return [
|
|
330
|
+
...coarse.filter(u => u < u0),
|
|
331
|
+
...fine,
|
|
332
|
+
...coarse.filter(u => u > u1),
|
|
333
|
+
].map(u => ({
|
|
334
|
+
x: footX + dir * 2 * g.rx * u * u,
|
|
335
|
+
y: yAt(g, 2 * g.ry * u * Math.sqrt(1 - u * u)),
|
|
336
|
+
}));
|
|
337
|
+
}
|
|
338
|
+
function traceChords(ctx, points) {
|
|
339
|
+
ctx.moveTo(points[0].x, points[0].y);
|
|
340
|
+
for (let k = 1; k < points.length; k++) {
|
|
341
|
+
ctx.lineTo(points[k].x, points[k].y);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
249
344
|
function tracePath(ctx, g) {
|
|
250
345
|
const { baseY } = g;
|
|
251
346
|
if (g.kind === KIND_STEM) {
|
|
@@ -267,14 +362,14 @@ function tracePath(ctx, g) {
|
|
|
267
362
|
[right, 1],
|
|
268
363
|
[left, -1],
|
|
269
364
|
]) {
|
|
270
|
-
|
|
271
|
-
ctx.moveTo(points[0].x, points[0].y);
|
|
272
|
-
for (let k = 1; k < points.length; k++) {
|
|
273
|
-
ctx.lineTo(points[k].x, points[k].y);
|
|
274
|
-
}
|
|
365
|
+
traceChords(ctx, legPoints(g, footX, legDir));
|
|
275
366
|
}
|
|
276
367
|
return;
|
|
277
368
|
}
|
|
369
|
+
if (linkIsFar(g.rx, g.screenW)) {
|
|
370
|
+
traceChords(ctx, farDomePoints(g, left, right));
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
278
373
|
const mid = (left + right) / 2;
|
|
279
374
|
if (g.reverse) {
|
|
280
375
|
ctx.moveTo(mid + g.rx, baseY);
|
|
@@ -439,7 +534,9 @@ function nearestRising(g, px, py) {
|
|
|
439
534
|
return { x, y: baseY, dist: Math.hypot(px - x, py - baseY) };
|
|
440
535
|
}
|
|
441
536
|
const near = ellipseNearest(px - mid, py - baseY, g.rx, g.ry);
|
|
442
|
-
|
|
537
|
+
// The drawn half rises from the baseline. A cursor on the baseline is as
|
|
538
|
+
// near the mirrored half, and the solve may name that one.
|
|
539
|
+
return { x: mid + near.x, y: baseY - Math.abs(near.y), dist: near.dist };
|
|
443
540
|
}
|
|
444
541
|
export const linkMark = {
|
|
445
542
|
id: 'link',
|
|
@@ -110,7 +110,7 @@ export declare function createMarkBackend<TRegion, TState extends FrameDimension
|
|
|
110
110
|
/**
|
|
111
111
|
* What the frame is cleared to, when transparent is the wrong answer. The
|
|
112
112
|
* GPU side hands it to `beginFrame` and the Canvas2D side fills it after
|
|
113
|
-
* `prepareCanvas`, so both backends composite over the same
|
|
113
|
+
* `prepareCanvas`, so both backends composite over the same color — which
|
|
114
114
|
* synteny needs, its indel wedges being pre-blended against the band's
|
|
115
115
|
* ground rather than composited over it.
|
|
116
116
|
*/
|
package/esm/marks/markBackend.js
CHANGED
|
@@ -63,7 +63,7 @@ export function drawPlannedPasses(hal, plan, regionKey) {
|
|
|
63
63
|
// its sampler unconditionally, and a textured pass missing a texture draws
|
|
64
64
|
// nothing on either GPU HAL. A mark declaring no `texture` at all is the same case —
|
|
65
65
|
// `pointMark` carries a sampler for the shapes that ramp, and a point of a
|
|
66
|
-
// constant
|
|
66
|
+
// constant color draws it without one.
|
|
67
67
|
const INERT_RAMP = new Uint8Array(COLOR_RAMP_LUT_ENTRIES * 4);
|
|
68
68
|
/**
|
|
69
69
|
* The texture each textured pass holds, by identity — the mirror of the one
|