@jbrowse/render-core 4.3.0 → 5.0.0-beta.10
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/RenderCanvas.d.ts +2 -2
- package/esm/RenderLifecycleMixin.d.ts +52 -20
- package/esm/RenderLifecycleMixin.js +89 -23
- package/esm/canvas2dUtils.d.ts +90 -23
- package/esm/canvas2dUtils.js +119 -36
- package/esm/colorRampLut.d.ts +5 -5
- package/esm/colorRampLut.js +6 -6
- package/esm/computePipeline.d.ts +14 -0
- package/esm/computePipeline.js +62 -0
- package/esm/contractReports.d.ts +54 -0
- package/esm/contractReports.js +112 -0
- package/esm/coverageBand.d.ts +13 -8
- package/esm/coverageBand.js +15 -5
- package/esm/coverageBandBuffers.d.ts +2 -9
- package/esm/coverageBandBuffers.js +1 -13
- package/esm/createRenderingBackend.d.ts +14 -18
- package/esm/createRenderingBackend.js +4 -2
- package/esm/displayPhase.d.ts +83 -104
- package/esm/displayPhase.js +42 -82
- package/esm/encodeMemo.d.ts +14 -0
- package/esm/encodeMemo.js +48 -0
- package/esm/gpuContextLostError.d.ts +30 -0
- package/esm/gpuContextLostError.js +40 -0
- package/esm/gpuDevice.d.ts +29 -10
- package/esm/gpuDevice.js +35 -16
- package/esm/graphicsCapabilities.d.ts +27 -19
- package/esm/graphicsCapabilities.js +40 -18
- package/esm/hal/createHal.d.ts +4 -5
- package/esm/hal/createHal.js +14 -4
- package/esm/hal/deviceGpuCache.d.ts +25 -30
- package/esm/hal/deviceGpuCache.js +88 -97
- package/esm/hal/gpuHalBase.d.ts +5 -4
- package/esm/hal/gpuHalBase.js +25 -14
- package/esm/hal/index.d.ts +2 -2
- package/esm/hal/index.js +1 -1
- package/esm/hal/mockHal.d.ts +15 -10
- package/esm/hal/mockHal.js +31 -18
- package/esm/hal/oomReporter.d.ts +7 -0
- package/esm/hal/oomReporter.js +10 -0
- package/esm/hal/passIds.d.ts +8 -7
- package/esm/hal/passIds.js +11 -10
- package/esm/hal/regionRegistry.d.ts +0 -1
- package/esm/hal/regionRegistry.js +3 -15
- package/esm/hal/types.d.ts +55 -18
- package/esm/hal/webgl2Hal.d.ts +23 -6
- package/esm/hal/webgl2Hal.js +161 -155
- package/esm/hal/webgpuHal.d.ts +29 -13
- package/esm/hal/webgpuHal.js +221 -153
- package/esm/installUpload.d.ts +16 -3
- package/esm/installUpload.js +13 -41
- package/esm/instanceCache.d.ts +10 -7
- package/esm/instanceCache.js +14 -15
- package/esm/instancePass.d.ts +2 -1
- package/esm/mapUploadSync.d.ts +2 -10
- package/esm/mapUploadSync.js +2 -3
- package/esm/marks/barMark.d.ts +30 -0
- package/esm/marks/barMark.js +149 -0
- package/esm/marks/colorFill.d.ts +17 -0
- package/esm/marks/colorFill.js +26 -0
- package/esm/marks/drawAgainstHit.d.ts +113 -0
- package/esm/marks/drawAgainstHit.js +407 -0
- package/esm/marks/ellipseDistance.d.ts +10 -0
- package/esm/marks/ellipseDistance.js +136 -0
- package/esm/marks/glyphPaint.d.ts +36 -0
- package/esm/marks/glyphPaint.js +77 -0
- package/esm/marks/index.d.ts +33 -0
- package/esm/marks/index.js +20 -0
- package/esm/marks/lineMark.d.ts +37 -0
- package/esm/marks/lineMark.js +416 -0
- package/esm/marks/linkMark.d.ts +76 -0
- package/esm/marks/linkMark.js +585 -0
- package/esm/marks/markBackend.d.ts +118 -0
- package/esm/marks/markBackend.js +196 -0
- package/esm/marks/markHit.d.ts +32 -0
- package/esm/marks/markHit.js +61 -0
- package/esm/marks/markInk.d.ts +18 -0
- package/esm/marks/markInk.js +71 -0
- package/esm/marks/markPaint.d.ts +14 -0
- package/esm/marks/markPaint.js +32 -0
- package/esm/marks/markPlan.d.ts +25 -0
- package/esm/marks/markPlan.js +20 -0
- package/esm/marks/markRamp.d.ts +72 -0
- package/esm/marks/markRamp.js +197 -0
- package/esm/marks/nearestMarkHit.d.ts +55 -0
- package/esm/marks/nearestMarkHit.js +158 -0
- package/esm/marks/pathRecorder.d.ts +14 -0
- package/esm/marks/pathRecorder.js +80 -0
- package/esm/marks/pointMark.d.ts +42 -0
- package/esm/marks/pointMark.js +125 -0
- package/esm/marks/rowLane.d.ts +39 -0
- package/esm/marks/rowLane.js +41 -0
- package/esm/marks/rowSpanIndex.d.ts +29 -0
- package/esm/marks/rowSpanIndex.js +141 -0
- package/esm/marks/rowTable.d.ts +41 -0
- package/esm/marks/rowTable.js +75 -0
- package/esm/marks/ruleMark.d.ts +21 -0
- package/esm/marks/ruleMark.js +91 -0
- package/esm/marks/spanMark.d.ts +42 -0
- package/esm/marks/spanMark.js +81 -0
- package/esm/marks/types.d.ts +261 -0
- package/esm/marks/types.js +175 -0
- package/esm/marks/valueScale.d.ts +23 -0
- package/esm/marks/valueScale.js +11 -0
- package/esm/namedReactions.d.ts +3 -4
- package/esm/namedReactions.js +3 -4
- package/esm/perRegionRenderingBackend.d.ts +21 -3
- package/esm/perRegionRenderingBackend.js +37 -3
- package/esm/regionDataMap.d.ts +2 -2
- package/esm/regionDataMap.js +25 -23
- package/esm/renderBlock.d.ts +14 -0
- package/esm/renderBlock.js +25 -0
- package/esm/renderingBackendBase.d.ts +20 -14
- package/esm/renderingBackendBase.js +12 -7
- package/esm/scoreScale.d.ts +27 -0
- package/esm/scoreScale.js +88 -0
- package/esm/shaders/barMark.generated.d.ts +3 -0
- package/esm/shaders/barMark.generated.js +7 -0
- package/esm/shaders/barMark.glsl.generated.d.ts +2 -0
- package/esm/shaders/barMark.glsl.generated.js +4 -0
- package/esm/shaders/barMark.iface.generated.d.ts +90 -0
- package/esm/shaders/barMark.iface.generated.js +205 -0
- package/esm/shaders/barMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/barMark.wgsl.generated.js +3 -0
- package/esm/shaders/capsule.js.generated.d.ts +1 -0
- package/esm/shaders/capsule.js.generated.js +11 -0
- package/esm/shaders/clipStrip.generated.d.ts +2 -0
- package/esm/shaders/clipStrip.generated.js +4 -0
- package/esm/shaders/clipStrip.js.generated.d.ts +1 -0
- package/esm/shaders/clipStrip.js.generated.js +21 -0
- package/esm/shaders/colorRampLut.js.generated.d.ts +1 -0
- package/esm/shaders/colorRampLut.js.generated.js +17 -0
- package/esm/shaders/coverageBar.generated.d.ts +2 -3
- package/esm/shaders/coverageBar.generated.js +4 -3
- package/esm/shaders/coverageBar.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageBar.glsl.generated.js +4 -0
- package/esm/shaders/coverageBar.iface.generated.js +1 -1
- package/esm/shaders/coverageBar.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageBar.wgsl.generated.js +3 -0
- package/esm/shaders/coverageClip.generated.d.ts +3 -0
- package/esm/shaders/coverageClip.generated.js +7 -0
- package/esm/shaders/coverageClip.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageClip.glsl.generated.js +4 -0
- package/esm/shaders/coverageClip.iface.generated.d.ts +85 -0
- package/esm/shaders/coverageClip.iface.generated.js +114 -0
- package/esm/shaders/coverageClip.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageClip.wgsl.generated.js +3 -0
- package/esm/shaders/coverageIndicator.generated.d.ts +2 -3
- package/esm/shaders/coverageIndicator.generated.js +4 -3
- package/esm/shaders/coverageIndicator.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageIndicator.glsl.generated.js +4 -0
- package/esm/shaders/coverageIndicator.iface.generated.js +1 -1
- package/esm/shaders/coverageIndicator.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageIndicator.wgsl.generated.js +3 -0
- package/esm/shaders/coverageInterbase.generated.d.ts +2 -3
- package/esm/shaders/coverageInterbase.generated.js +4 -3
- package/esm/shaders/coverageInterbase.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageInterbase.glsl.generated.js +4 -0
- package/esm/shaders/coverageInterbase.iface.generated.js +1 -1
- package/esm/shaders/coverageInterbase.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageInterbase.wgsl.generated.js +3 -0
- package/esm/shaders/coverageMod.generated.d.ts +2 -3
- package/esm/shaders/coverageMod.generated.js +4 -3
- package/esm/shaders/coverageMod.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageMod.glsl.generated.js +4 -0
- package/esm/shaders/coverageMod.iface.generated.js +1 -1
- package/esm/shaders/coverageMod.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageMod.wgsl.generated.js +3 -0
- package/esm/shaders/coverageSnp.generated.d.ts +2 -3
- package/esm/shaders/coverageSnp.generated.js +4 -3
- package/esm/shaders/coverageSnp.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageSnp.glsl.generated.js +4 -0
- package/esm/shaders/coverageSnp.iface.generated.js +1 -1
- package/esm/shaders/coverageSnp.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageSnp.wgsl.generated.js +3 -0
- package/esm/shaders/curveDistance.js.generated.d.ts +5 -0
- package/esm/shaders/curveDistance.js.generated.js +87 -0
- package/esm/shaders/hpmath.js.generated.d.ts +2 -1
- package/esm/shaders/hpmath.js.generated.js +5 -2
- package/esm/shaders/lineCenterMark.generated.d.ts +3 -0
- package/esm/shaders/lineCenterMark.generated.js +7 -0
- package/esm/shaders/lineCenterMark.glsl.generated.d.ts +2 -0
- package/esm/shaders/lineCenterMark.glsl.generated.js +4 -0
- package/esm/shaders/lineCenterMark.iface.generated.d.ts +104 -0
- package/esm/shaders/lineCenterMark.iface.generated.js +237 -0
- package/esm/shaders/lineCenterMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/lineCenterMark.wgsl.generated.js +3 -0
- package/esm/shaders/lineCommon.generated.d.ts +2 -0
- package/esm/shaders/lineCommon.generated.js +4 -0
- package/esm/shaders/lineStepMark.generated.d.ts +3 -0
- package/esm/shaders/lineStepMark.generated.js +7 -0
- package/esm/shaders/lineStepMark.glsl.generated.d.ts +2 -0
- package/esm/shaders/lineStepMark.glsl.generated.js +4 -0
- package/esm/shaders/lineStepMark.iface.generated.d.ts +98 -0
- package/esm/shaders/lineStepMark.iface.generated.js +225 -0
- package/esm/shaders/lineStepMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/lineStepMark.wgsl.generated.js +3 -0
- package/esm/shaders/linkMark.consts.generated.d.ts +14 -0
- package/esm/shaders/linkMark.consts.generated.js +16 -0
- package/esm/shaders/linkMark.generated.d.ts +4 -0
- package/esm/shaders/linkMark.generated.js +8 -0
- package/esm/shaders/linkMark.glsl.generated.d.ts +2 -0
- package/esm/shaders/linkMark.glsl.generated.js +4 -0
- package/esm/shaders/linkMark.iface.generated.d.ts +142 -0
- package/esm/shaders/linkMark.iface.generated.js +1829 -0
- package/esm/shaders/linkMark.js.generated.d.ts +7 -0
- package/esm/shaders/linkMark.js.generated.js +159 -0
- package/esm/shaders/linkMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/linkMark.wgsl.generated.js +3 -0
- package/esm/shaders/markColor.generated.d.ts +9 -0
- package/esm/shaders/markColor.generated.js +11 -0
- package/esm/shaders/pointGlyph.js.generated.d.ts +2 -0
- package/esm/shaders/pointGlyph.js.generated.js +15 -0
- package/esm/shaders/pointMark.consts.generated.d.ts +7 -0
- package/esm/shaders/pointMark.consts.generated.js +9 -0
- package/esm/shaders/pointMark.generated.d.ts +4 -0
- package/esm/shaders/pointMark.generated.js +8 -0
- package/esm/shaders/pointMark.glsl.generated.d.ts +2 -0
- package/esm/shaders/pointMark.glsl.generated.js +4 -0
- package/esm/shaders/pointMark.iface.generated.d.ts +98 -0
- package/esm/shaders/pointMark.iface.generated.js +219 -0
- package/esm/shaders/pointMark.js.generated.d.ts +3 -0
- package/esm/shaders/pointMark.js.generated.js +93 -0
- package/esm/shaders/pointMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/pointMark.wgsl.generated.js +3 -0
- package/esm/shaders/rowRect.generated.d.ts +0 -1
- package/esm/shaders/rowRect.generated.js +0 -1
- package/esm/shaders/rowTable.generated.d.ts +1 -0
- package/esm/shaders/rowTable.generated.js +3 -0
- package/esm/shaders/rowTable.js.generated.d.ts +5 -0
- package/esm/shaders/rowTable.js.generated.js +29 -0
- package/esm/shaders/scoreScale.generated.d.ts +2 -0
- package/esm/shaders/scoreScale.generated.js +4 -0
- package/esm/shaders/scoreScale.js.generated.d.ts +1 -0
- package/esm/shaders/scoreScale.js.generated.js +50 -23
- package/esm/shaders/spanMark.generated.d.ts +3 -0
- package/esm/shaders/spanMark.generated.js +7 -0
- package/esm/shaders/spanMark.glsl.generated.d.ts +2 -0
- package/esm/shaders/spanMark.glsl.generated.js +4 -0
- package/esm/shaders/spanMark.iface.generated.d.ts +81 -0
- package/esm/shaders/spanMark.iface.generated.js +215 -0
- package/esm/shaders/spanMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/spanMark.wgsl.generated.js +3 -0
- package/esm/sharedBackendKey.d.ts +18 -0
- package/esm/{keyedRenderingBackend.js → sharedBackendKey.js} +5 -0
- package/esm/slangPass.d.ts +18 -11
- package/esm/slangPass.js +12 -4
- package/esm/useGraphicsCapabilities.d.ts +1 -1
- package/esm/useGraphicsCapabilities.js +1 -1
- package/esm/useRenderingBackend.d.ts +13 -13
- package/esm/useRenderingBackend.js +58 -20
- package/esm/webgpuUtils.d.ts +3 -11
- package/esm/webgpuUtils.js +12 -33
- package/package.json +32 -7
- package/src/shaders/antialias.slang +46 -0
- package/src/shaders/arcHull.test.ts +267 -0
- package/src/shaders/barMark.generated.ts +11 -0
- package/src/shaders/barMark.glsl.generated.ts +6 -0
- package/src/shaders/barMark.iface.generated.ts +285 -0
- package/src/shaders/barMark.slang +167 -0
- package/src/shaders/barMark.wgsl.generated.ts +4 -0
- package/src/shaders/capsule.js.generated.ts +14 -0
- package/src/shaders/capsule.slang +12 -4
- package/src/shaders/clipStrip.generated.ts +6 -0
- package/src/shaders/clipStrip.js.generated.ts +22 -0
- package/src/shaders/clipStrip.slang +34 -0
- package/src/shaders/clipStrip.test.ts +39 -0
- package/src/shaders/colorPack.slang +5 -1
- package/src/shaders/colorRampLut.js.generated.ts +22 -0
- package/src/shaders/colorRampLut.slang +15 -1
- package/src/shaders/coverageBand.slang +64 -32
- package/src/shaders/coverageBar.generated.ts +6 -5
- package/src/shaders/coverageBar.glsl.generated.ts +6 -0
- package/src/shaders/coverageBar.iface.generated.ts +1 -1
- package/src/shaders/coverageBar.slang +10 -10
- package/src/shaders/coverageBar.wgsl.generated.ts +4 -0
- package/src/shaders/coverageClip.generated.ts +11 -0
- package/src/shaders/coverageClip.glsl.generated.ts +6 -0
- package/src/shaders/coverageClip.iface.generated.ts +172 -0
- package/src/shaders/coverageClip.slang +38 -0
- package/src/shaders/coverageClip.wgsl.generated.ts +4 -0
- package/src/shaders/coverageIndicator.generated.ts +6 -5
- package/src/shaders/coverageIndicator.glsl.generated.ts +6 -0
- package/src/shaders/coverageIndicator.iface.generated.ts +1 -1
- package/src/shaders/coverageIndicator.wgsl.generated.ts +4 -0
- package/src/shaders/coverageInterbase.generated.ts +6 -5
- package/src/shaders/coverageInterbase.glsl.generated.ts +6 -0
- package/src/shaders/coverageInterbase.iface.generated.ts +1 -1
- package/src/shaders/coverageInterbase.wgsl.generated.ts +4 -0
- package/src/shaders/coverageMod.generated.ts +6 -5
- package/src/shaders/coverageMod.glsl.generated.ts +6 -0
- package/src/shaders/coverageMod.iface.generated.ts +1 -1
- package/src/shaders/coverageMod.wgsl.generated.ts +4 -0
- package/src/shaders/coverageSnp.generated.ts +6 -5
- package/src/shaders/coverageSnp.glsl.generated.ts +6 -0
- package/src/shaders/coverageSnp.iface.generated.ts +1 -1
- package/src/shaders/coverageSnp.wgsl.generated.ts +4 -0
- package/src/shaders/curveDistance.js.generated.ts +91 -0
- package/src/shaders/curveDistance.slang +265 -0
- package/src/shaders/hpClampFloor.test.ts +87 -0
- package/src/shaders/hpmath.js.generated.ts +6 -2
- package/src/shaders/hpmath.slang +100 -48
- package/src/shaders/insetValueScale.test.ts +67 -0
- package/src/shaders/lineCenterMark.generated.ts +11 -0
- package/src/shaders/lineCenterMark.glsl.generated.ts +6 -0
- package/src/shaders/lineCenterMark.iface.generated.ts +328 -0
- package/src/shaders/lineCenterMark.slang +127 -0
- package/src/shaders/lineCenterMark.wgsl.generated.ts +4 -0
- package/src/shaders/lineCommon.generated.ts +6 -0
- package/src/shaders/lineCommon.slang +102 -0
- package/src/shaders/lineStepMark.generated.ts +11 -0
- package/src/shaders/lineStepMark.glsl.generated.ts +6 -0
- package/src/shaders/lineStepMark.iface.generated.ts +311 -0
- package/src/shaders/lineStepMark.slang +125 -0
- package/src/shaders/lineStepMark.wgsl.generated.ts +4 -0
- package/src/shaders/linkMark.consts.generated.ts +30 -0
- package/src/shaders/linkMark.generated.ts +12 -0
- package/src/shaders/linkMark.glsl.generated.ts +6 -0
- package/src/shaders/linkMark.iface.generated.ts +1954 -0
- package/src/shaders/linkMark.js.generated.ts +167 -0
- package/src/shaders/linkMark.slang +439 -0
- package/src/shaders/linkMark.wgsl.generated.ts +4 -0
- package/src/shaders/markColor.generated.ts +20 -0
- package/src/shaders/markColor.slang +128 -0
- package/src/shaders/pointGlyph.js.generated.ts +20 -0
- package/src/shaders/pointGlyph.slang +23 -7
- package/src/shaders/pointGlyphPad.test.ts +242 -0
- package/src/shaders/pointMark.consts.generated.ts +16 -0
- package/src/shaders/pointMark.generated.ts +12 -0
- package/src/shaders/pointMark.glsl.generated.ts +6 -0
- package/src/shaders/pointMark.iface.generated.ts +304 -0
- package/src/shaders/pointMark.js.generated.ts +99 -0
- package/src/shaders/pointMark.slang +227 -0
- package/src/shaders/pointMark.wgsl.generated.ts +4 -0
- package/src/shaders/rowRect.generated.ts +0 -2
- package/src/shaders/rowRect.slang +39 -58
- package/src/shaders/rowTable.generated.ts +4 -0
- package/src/shaders/rowTable.js.generated.ts +37 -0
- package/src/shaders/rowTable.slang +98 -0
- package/src/shaders/scoreScale.generated.ts +6 -0
- package/src/shaders/scoreScale.js.generated.ts +48 -23
- package/src/shaders/scoreScale.slang +49 -55
- package/src/shaders/sdEllipse.test.ts +211 -0
- package/src/shaders/spanMark.generated.ts +11 -0
- package/src/shaders/spanMark.glsl.generated.ts +6 -0
- package/src/shaders/spanMark.iface.generated.ts +289 -0
- package/src/shaders/spanMark.slang +77 -0
- package/src/shaders/spanMark.wgsl.generated.ts +4 -0
- package/src/shaders/valueScale.slang +26 -0
- package/src/shaders/wideCircleLeg.test.ts +85 -0
- package/esm/globalRenderingBackend.d.ts +0 -90
- package/esm/globalRenderingBackend.js +0 -53
- package/esm/keyedRenderingBackend.d.ts +0 -41
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { Canvas2DPerRegionRenderingBackend, GpuPerRegionRenderingBackend } from '../perRegionRenderingBackend.ts';
|
|
2
|
+
import type { BlockClipResult } from '../blockClipUtils.ts';
|
|
3
|
+
import type { CanvasScale } from '../canvas2dUtils.ts';
|
|
4
|
+
import type { GpuHal } from '../hal/index.ts';
|
|
5
|
+
import type { SampleCount } from '../hal/types.ts';
|
|
6
|
+
import type { InstancePass } from '../instancePass.ts';
|
|
7
|
+
import type { ClearColor, PerRegionRenderingBackend } from '../perRegionRenderingBackend.ts';
|
|
8
|
+
import type { RenderBlock } from '../renderBlock.ts';
|
|
9
|
+
import type { FrameDimensions } from '../renderingBackendBase.ts';
|
|
10
|
+
import type { MarkPlan } from './markPlan.ts';
|
|
11
|
+
import type { Mark, MarkTexture, TextureBinder } from './types.ts';
|
|
12
|
+
/** Pack and upload one region's buffers for a mark list. */
|
|
13
|
+
export declare function uploadMarks<TRegion, TState extends FrameDimensions>(hal: GpuHal, regionKey: number, marks: readonly Mark<TRegion, TState>[], data: TRegion): void;
|
|
14
|
+
/**
|
|
15
|
+
* Draw one already-clipped block through a mark list.
|
|
16
|
+
*
|
|
17
|
+
* **The viewport is this helper's, the scissor is the caller's.** Every shape
|
|
18
|
+
* maps bp across the block column, so nobody wants a viewport narrower than the
|
|
19
|
+
* clip; a caller does want a narrower scissor, which is how alignments clips a
|
|
20
|
+
* coverage strip inside the column it hands over. `GpuPerRegionRenderingBackend`
|
|
21
|
+
* sets both per block already, so for a backend on that scaffold the viewport
|
|
22
|
+
* set here is the same rect twice.
|
|
23
|
+
*
|
|
24
|
+
* A caller that narrows it must hand over marks declaring no `band`, and that
|
|
25
|
+
* is the one way the two clips do not compose: a banded mark scissors to its
|
|
26
|
+
* strip and hands the BLOCK COLUMN back rather than what the caller had. The
|
|
27
|
+
* split is deliberate — `render-core/CLAUDE.md` §Drawing has which displays
|
|
28
|
+
* take which side — but nothing enforces it, so it is stated at both.
|
|
29
|
+
*/
|
|
30
|
+
export declare function drawMarks<TRegion, TState extends FrameDimensions>(hal: GpuHal, scratch: ArrayBuffer, marks: readonly Mark<TRegion, TState>[], block: RenderBlock, clip: BlockClipResult, region: TRegion, state: TState, regionKey: number, textures?: TextureBinder): void;
|
|
31
|
+
/**
|
|
32
|
+
* Draw one block's planned passes off the uniforms the caller has already
|
|
33
|
+
* staged for it. No gate, no lens, no write: `planMarks` settled the frame
|
|
34
|
+
* question and the caller's own `hal.writeUniforms` settled the block's, so
|
|
35
|
+
* what is left per mark is the `drawPass` — the loop a display with its own
|
|
36
|
+
* frame scaffold was already running over a hand-kept pass list.
|
|
37
|
+
*
|
|
38
|
+
* Viewport and scissor are the caller's, as with `drawMarks`' scissor: a
|
|
39
|
+
* plan's marks declare no `band`, so nothing here narrows or restores either.
|
|
40
|
+
*/
|
|
41
|
+
export declare function drawPlannedPasses(hal: GpuHal, plan: Pick<MarkPlan<never, never>, 'passes'>, regionKey: number): void;
|
|
42
|
+
/**
|
|
43
|
+
* The texture each textured pass holds, by identity — the mirror of the one
|
|
44
|
+
* texture the HAL keeps per pass, so an unchanged ramp, strip or table costs a
|
|
45
|
+
* frame nothing and a backend rebuilt after context loss re-uploads. Per pass
|
|
46
|
+
* and per backend, never per region: a lens answering nothing for a region
|
|
47
|
+
* leaves the pass's texture where it is, and binds the inert table only while
|
|
48
|
+
* the pass has none at all, since a ring canvas draws one region per pass and
|
|
49
|
+
* a lens with no opinion on the other regions must not swap the strip out and
|
|
50
|
+
* back on every frame.
|
|
51
|
+
*/
|
|
52
|
+
export declare class MarkTextureBinder implements TextureBinder {
|
|
53
|
+
private hal;
|
|
54
|
+
private bound;
|
|
55
|
+
constructor(hal: GpuHal);
|
|
56
|
+
bind(passId: string, sampler: string, texture: MarkTexture | undefined): void;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The HAL-side half of `createMarkBackend`, exported so a display's mark list
|
|
60
|
+
* can be driven against `MockHal`: which marks upload, which draw off another's
|
|
61
|
+
* buffer, and which blocks a shape declines.
|
|
62
|
+
*/
|
|
63
|
+
export declare class GpuMarkBackend<TRegion, TState extends FrameDimensions> extends GpuPerRegionRenderingBackend<TRegion, TState> {
|
|
64
|
+
private clear?;
|
|
65
|
+
protected regionPasses: InstancePass<TRegion>[];
|
|
66
|
+
private textures;
|
|
67
|
+
private blockMarks;
|
|
68
|
+
private viewMarks;
|
|
69
|
+
constructor(hal: GpuHal, marks: readonly Mark<TRegion, TState>[], clear?: ((state: TState) => ClearColor) | undefined);
|
|
70
|
+
protected clearColor(state: TState): ClearColor;
|
|
71
|
+
private drawWith;
|
|
72
|
+
protected drawRegion(block: RenderBlock, clip: BlockClipResult, region: TRegion, state: TState): void;
|
|
73
|
+
protected drawOverBlocks(regions: ReadonlyMap<number, TRegion>, state: TState, scale: CanvasScale): boolean;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The Canvas2D half, exported for the same reason `GpuMarkBackend` is: a
|
|
77
|
+
* display's frame scaffold — the ground it clears to, the blocks it paints — is
|
|
78
|
+
* a property of the pair, and a suite pinning it on one backend has to pin it
|
|
79
|
+
* on the other.
|
|
80
|
+
*/
|
|
81
|
+
export declare class Canvas2DMarkBackend<TRegion, TState extends FrameDimensions> extends Canvas2DPerRegionRenderingBackend<TRegion, TState> {
|
|
82
|
+
private marks;
|
|
83
|
+
private clear?;
|
|
84
|
+
constructor(canvas: HTMLCanvasElement, marks: readonly Mark<TRegion, TState>[], clear?: ((state: TState) => ClearColor) | undefined);
|
|
85
|
+
protected clearColor(state: TState): ClearColor | undefined;
|
|
86
|
+
protected draw(blocks: RenderBlock[], regions: ReadonlyMap<number, TRegion>, state: TState): void;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Build a display's rendering backend from its mark declarations.
|
|
90
|
+
*
|
|
91
|
+
* **This is the only module on the mark path that reaches the HAL**, which is
|
|
92
|
+
* why it is not on the `marks` subpath: a display's declaration, its painter
|
|
93
|
+
* and its hit test are all things a state model can legitimately reach, and a
|
|
94
|
+
* state model is eager (ADR-091). Reaching the backend costs the GPU stack at
|
|
95
|
+
* plugin-install time, so it is a separate import a display makes once, from
|
|
96
|
+
* the factory the component hands to `DisplayChrome`.
|
|
97
|
+
*
|
|
98
|
+
* This is the whole of what a display used to spell as a `GpuXxxRenderer`
|
|
99
|
+
* class, a `Canvas2DXxxRenderer` class, a pass list and a factory: the passes
|
|
100
|
+
* are the marks' own and each backend walks the same list.
|
|
101
|
+
*
|
|
102
|
+
* One uniform buffer serves every mark because each writes its own layout into
|
|
103
|
+
* the scratch immediately before its `drawPass`, the ordering the HAL's
|
|
104
|
+
* write-then-draw contract already guarantees for a multi-pass renderer — or
|
|
105
|
+
* draws off the struct the previous mark of the same block already staged, per
|
|
106
|
+
* `StagedUniforms`.
|
|
107
|
+
*/
|
|
108
|
+
export declare function createMarkBackend<TRegion, TState extends FrameDimensions>(canvas: HTMLCanvasElement, marks: readonly Mark<TRegion, TState>[], opts?: {
|
|
109
|
+
sampleCount?: SampleCount;
|
|
110
|
+
/**
|
|
111
|
+
* What the frame is cleared to, when transparent is the wrong answer. The
|
|
112
|
+
* GPU side hands it to `beginFrame` and the Canvas2D side fills it after
|
|
113
|
+
* `prepareCanvas`, so both backends composite over the same colour — which
|
|
114
|
+
* synteny needs, its indel wedges being pre-blended against the band's
|
|
115
|
+
* ground rather than composited over it.
|
|
116
|
+
*/
|
|
117
|
+
clearColor?: (state: TState) => ClearColor;
|
|
118
|
+
}): Promise<PerRegionRenderingBackend<TRegion, TState>>;
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { clipBlock } from '../blockClipUtils.js';
|
|
2
|
+
import { COLOR_RAMP_LUT_ENTRIES, uploadColorRampLut } from '../colorRampLut.js';
|
|
3
|
+
import { createRenderingBackend } from '../createRenderingBackend.js';
|
|
4
|
+
import { uploadPass } from '../instancePass.js';
|
|
5
|
+
import { Canvas2DPerRegionRenderingBackend, GpuPerRegionRenderingBackend, } from '../perRegionRenderingBackend.js';
|
|
6
|
+
import { canvasWideBlock } from '../renderBlock.js';
|
|
7
|
+
import { paintMarkBlocks } from './markPaint.js';
|
|
8
|
+
// The passes a mark list owns a buffer for: the whole list minus those drawing
|
|
9
|
+
// off another's (`bufferOf`). Uploading to a borrowed pass is silent and its
|
|
10
|
+
// buffer is the lender's, so the skip is stated once, here — module-local,
|
|
11
|
+
// because `marks/backend` is a published subpath and nothing outside wants it.
|
|
12
|
+
function ownedPasses(marks) {
|
|
13
|
+
return marks.filter(m => !m.bufferOf).map(m => m.pass);
|
|
14
|
+
}
|
|
15
|
+
/** Pack and upload one region's buffers for a mark list. */
|
|
16
|
+
export function uploadMarks(hal, regionKey, marks, data) {
|
|
17
|
+
for (const pass of ownedPasses(marks)) {
|
|
18
|
+
uploadPass(hal, regionKey, pass, data);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Draw one already-clipped block through a mark list.
|
|
23
|
+
*
|
|
24
|
+
* **The viewport is this helper's, the scissor is the caller's.** Every shape
|
|
25
|
+
* maps bp across the block column, so nobody wants a viewport narrower than the
|
|
26
|
+
* clip; a caller does want a narrower scissor, which is how alignments clips a
|
|
27
|
+
* coverage strip inside the column it hands over. `GpuPerRegionRenderingBackend`
|
|
28
|
+
* sets both per block already, so for a backend on that scaffold the viewport
|
|
29
|
+
* set here is the same rect twice.
|
|
30
|
+
*
|
|
31
|
+
* A caller that narrows it must hand over marks declaring no `band`, and that
|
|
32
|
+
* is the one way the two clips do not compose: a banded mark scissors to its
|
|
33
|
+
* strip and hands the BLOCK COLUMN back rather than what the caller had. The
|
|
34
|
+
* split is deliberate — `render-core/CLAUDE.md` §Drawing has which displays
|
|
35
|
+
* take which side — but nothing enforces it, so it is stated at both.
|
|
36
|
+
*/
|
|
37
|
+
export function drawMarks(hal, scratch, marks, block, clip, region, state, regionKey, textures) {
|
|
38
|
+
hal.setViewport(clip.pxX, 0, clip.pxW, clip.pxH);
|
|
39
|
+
// One call is one block, which is exactly the life of a `StagedUniforms`
|
|
40
|
+
const staged = { writer: undefined, params: undefined };
|
|
41
|
+
for (const mark of marks) {
|
|
42
|
+
mark.drawRegion(hal, scratch, block, clip, region, state, regionKey, staged, textures);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Draw one block's planned passes off the uniforms the caller has already
|
|
47
|
+
* staged for it. No gate, no lens, no write: `planMarks` settled the frame
|
|
48
|
+
* question and the caller's own `hal.writeUniforms` settled the block's, so
|
|
49
|
+
* what is left per mark is the `drawPass` — the loop a display with its own
|
|
50
|
+
* frame scaffold was already running over a hand-kept pass list.
|
|
51
|
+
*
|
|
52
|
+
* Viewport and scissor are the caller's, as with `drawMarks`' scissor: a
|
|
53
|
+
* plan's marks declare no `band`, so nothing here narrows or restores either.
|
|
54
|
+
*/
|
|
55
|
+
export function drawPlannedPasses(hal, plan, regionKey) {
|
|
56
|
+
const { passes } = plan;
|
|
57
|
+
for (let i = 0; i < passes.length; i++) {
|
|
58
|
+
const pass = passes[i];
|
|
59
|
+
hal.drawPass(pass.id, regionKey, pass.bufferOf);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// What a textured pass binds while its mark names no ramp: a shader declares
|
|
63
|
+
// its sampler unconditionally, and a textured pass missing a texture draws
|
|
64
|
+
// nothing on either GPU HAL. A mark declaring no `texture` at all is the same case —
|
|
65
|
+
// `pointMark` carries a sampler for the shapes that ramp, and a point of a
|
|
66
|
+
// constant colour draws it without one.
|
|
67
|
+
const INERT_RAMP = new Uint8Array(COLOR_RAMP_LUT_ENTRIES * 4);
|
|
68
|
+
/**
|
|
69
|
+
* The texture each textured pass holds, by identity — the mirror of the one
|
|
70
|
+
* texture the HAL keeps per pass, so an unchanged ramp, strip or table costs a
|
|
71
|
+
* frame nothing and a backend rebuilt after context loss re-uploads. Per pass
|
|
72
|
+
* and per backend, never per region: a lens answering nothing for a region
|
|
73
|
+
* leaves the pass's texture where it is, and binds the inert table only while
|
|
74
|
+
* the pass has none at all, since a ring canvas draws one region per pass and
|
|
75
|
+
* a lens with no opinion on the other regions must not swap the strip out and
|
|
76
|
+
* back on every frame.
|
|
77
|
+
*/
|
|
78
|
+
export class MarkTextureBinder {
|
|
79
|
+
hal;
|
|
80
|
+
bound = new Map();
|
|
81
|
+
constructor(hal) {
|
|
82
|
+
this.hal = hal;
|
|
83
|
+
}
|
|
84
|
+
bind(passId, sampler, texture) {
|
|
85
|
+
const key = `${passId}\u0000${sampler}`;
|
|
86
|
+
const bound = this.bound.get(key);
|
|
87
|
+
const next = texture ?? bound ?? INERT_RAMP;
|
|
88
|
+
if (next !== bound) {
|
|
89
|
+
if (next instanceof Uint8Array) {
|
|
90
|
+
uploadColorRampLut(this.hal, next, [passId], sampler);
|
|
91
|
+
}
|
|
92
|
+
else if ('image' in next) {
|
|
93
|
+
this.hal.uploadTexture(passId, next.image, next.width, next.height, sampler);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
this.hal.uploadTexture(passId, next.bytes, next.width, next.height, sampler);
|
|
97
|
+
}
|
|
98
|
+
this.bound.set(key, next);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The HAL-side half of `createMarkBackend`, exported so a display's mark list
|
|
104
|
+
* can be driven against `MockHal`: which marks upload, which draw off another's
|
|
105
|
+
* buffer, and which blocks a shape declines.
|
|
106
|
+
*/
|
|
107
|
+
export class GpuMarkBackend extends GpuPerRegionRenderingBackend {
|
|
108
|
+
clear;
|
|
109
|
+
// The base's upload loop already IS the mark walk, so this backend names its
|
|
110
|
+
// passes instead of overriding `upload` with a second spelling of it.
|
|
111
|
+
regionPasses;
|
|
112
|
+
textures;
|
|
113
|
+
blockMarks;
|
|
114
|
+
viewMarks;
|
|
115
|
+
constructor(hal, marks, clear) {
|
|
116
|
+
super(hal);
|
|
117
|
+
this.clear = clear;
|
|
118
|
+
this.regionPasses = ownedPasses(marks);
|
|
119
|
+
this.textures = new MarkTextureBinder(hal);
|
|
120
|
+
this.blockMarks = marks.filter(m => !m.spansView);
|
|
121
|
+
this.viewMarks = marks.filter(m => m.spansView);
|
|
122
|
+
}
|
|
123
|
+
clearColor(state) {
|
|
124
|
+
return this.clear ? this.clear(state) : super.clearColor(state);
|
|
125
|
+
}
|
|
126
|
+
drawWith(marks, block, clip, region, state) {
|
|
127
|
+
drawMarks(this.hal, this.uniformData, marks, block, clip, region, state, block.displayedRegionIndex, this.textures);
|
|
128
|
+
}
|
|
129
|
+
drawRegion(block, clip, region, state) {
|
|
130
|
+
this.drawWith(this.blockMarks, block, clip, region, state);
|
|
131
|
+
}
|
|
132
|
+
drawOverBlocks(regions, state, scale) {
|
|
133
|
+
if (this.viewMarks.length === 0 || regions.size === 0) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
const { canvasWidth, canvasHeight } = state;
|
|
137
|
+
for (const [key, region] of regions) {
|
|
138
|
+
const block = canvasWideBlock(key, canvasWidth);
|
|
139
|
+
const clip = clipBlock(block, canvasWidth, canvasHeight, scale);
|
|
140
|
+
if (clip) {
|
|
141
|
+
this.drawWith(this.viewMarks, block, clip, region, state);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* The Canvas2D half, exported for the same reason `GpuMarkBackend` is: a
|
|
149
|
+
* display's frame scaffold — the ground it clears to, the blocks it paints — is
|
|
150
|
+
* a property of the pair, and a suite pinning it on one backend has to pin it
|
|
151
|
+
* on the other.
|
|
152
|
+
*/
|
|
153
|
+
export class Canvas2DMarkBackend extends Canvas2DPerRegionRenderingBackend {
|
|
154
|
+
marks;
|
|
155
|
+
clear;
|
|
156
|
+
constructor(canvas, marks, clear) {
|
|
157
|
+
super(canvas);
|
|
158
|
+
this.marks = marks;
|
|
159
|
+
this.clear = clear;
|
|
160
|
+
}
|
|
161
|
+
clearColor(state) {
|
|
162
|
+
return this.clear ? this.clear(state) : super.clearColor(state);
|
|
163
|
+
}
|
|
164
|
+
draw(blocks, regions, state) {
|
|
165
|
+
paintMarkBlocks(this.ctx, this.marks, regions, blocks, state);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Build a display's rendering backend from its mark declarations.
|
|
170
|
+
*
|
|
171
|
+
* **This is the only module on the mark path that reaches the HAL**, which is
|
|
172
|
+
* why it is not on the `marks` subpath: a display's declaration, its painter
|
|
173
|
+
* and its hit test are all things a state model can legitimately reach, and a
|
|
174
|
+
* state model is eager (ADR-091). Reaching the backend costs the GPU stack at
|
|
175
|
+
* plugin-install time, so it is a separate import a display makes once, from
|
|
176
|
+
* the factory the component hands to `DisplayChrome`.
|
|
177
|
+
*
|
|
178
|
+
* This is the whole of what a display used to spell as a `GpuXxxRenderer`
|
|
179
|
+
* class, a `Canvas2DXxxRenderer` class, a pass list and a factory: the passes
|
|
180
|
+
* are the marks' own and each backend walks the same list.
|
|
181
|
+
*
|
|
182
|
+
* One uniform buffer serves every mark because each writes its own layout into
|
|
183
|
+
* the scratch immediately before its `drawPass`, the ordering the HAL's
|
|
184
|
+
* write-then-draw contract already guarantees for a multi-pass renderer — or
|
|
185
|
+
* draws off the struct the previous mark of the same block already staged, per
|
|
186
|
+
* `StagedUniforms`.
|
|
187
|
+
*/
|
|
188
|
+
export function createMarkBackend(canvas, marks, opts = {}) {
|
|
189
|
+
const { clearColor } = opts;
|
|
190
|
+
return createRenderingBackend(canvas, {
|
|
191
|
+
passes: marks.map(m => m.pass),
|
|
192
|
+
sampleCount: opts.sampleCount,
|
|
193
|
+
createGpuBackend: hal => new GpuMarkBackend(hal, marks, clearColor),
|
|
194
|
+
createCanvas2DBackend: c => new Canvas2DMarkBackend(c, marks, clearColor),
|
|
195
|
+
});
|
|
196
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { MarkHit, MarkShape } from './types.ts';
|
|
2
|
+
/** One instance's ink measured against a cursor — a `MarkHit` before it is one. */
|
|
3
|
+
export type InkHit = Omit<MarkHit, 'index'>;
|
|
4
|
+
/**
|
|
5
|
+
* Where a rect's ink sits nearest `(xPx, yPx)`, and how far: the point itself
|
|
6
|
+
* where the cursor is inside, else the nearest point on the edge.
|
|
7
|
+
*/
|
|
8
|
+
export declare function inkOnRect(xPx: number, yPx: number, left: number, top: number, width: number, height: number): InkHit;
|
|
9
|
+
/** The same for a mark with no extent to clamp into. */
|
|
10
|
+
export declare function inkAtPoint(xPx: number, yPx: number, x: number, y: number): InkHit;
|
|
11
|
+
/**
|
|
12
|
+
* The nearest of `candidates` by where each one's ink is, or undefined when
|
|
13
|
+
* none beats `maxDistSq`.
|
|
14
|
+
*
|
|
15
|
+
* This is the whole of every shape's `hitNearest` but the geometry: `inkAt`
|
|
16
|
+
* answers where instance `i` put ink and how far that is, and this keeps the
|
|
17
|
+
* closest. Only a STRICTLY nearer candidate replaces the best, so on a tie the
|
|
18
|
+
* first wins, so a caller that iterates back to front gets the mark on top.
|
|
19
|
+
* Five shapes used to spell that rule out separately.
|
|
20
|
+
*
|
|
21
|
+
* `inkAt` may answer `undefined` for an instance that drew nothing at all, as
|
|
22
|
+
* against one that drew somewhere far away: a bar clipped out of its band has
|
|
23
|
+
* no ink to be near.
|
|
24
|
+
*/
|
|
25
|
+
export declare function nearestInk(candidates: Iterable<number>, maxDistSq: number, inkAt: (index: number) => InkHit | undefined): MarkHit | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* The hit test a shape's `ink` implies: the nearest of its boxes, edges
|
|
28
|
+
* included, so a point inside one answers it at distance 0.
|
|
29
|
+
*/
|
|
30
|
+
export declare function inkHitNearest<C, P>(ink: NonNullable<MarkShape<C, P>['ink']>): NonNullable<MarkShape<C, P>['hitNearest']>;
|
|
31
|
+
/** A shape's own `hitNearest`, else the one its `ink` implies, else none. */
|
|
32
|
+
export declare function shapeHitNearest<C, P>(shape: MarkShape<C, P>): MarkShape<C, P>['hitNearest'];
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a rect's ink sits nearest `(xPx, yPx)`, and how far: the point itself
|
|
3
|
+
* where the cursor is inside, else the nearest point on the edge.
|
|
4
|
+
*/
|
|
5
|
+
export function inkOnRect(xPx, yPx, left, top, width, height) {
|
|
6
|
+
const x = Math.min(Math.max(xPx, left), left + width);
|
|
7
|
+
const y = Math.min(Math.max(yPx, top), top + height);
|
|
8
|
+
const dx = xPx - x;
|
|
9
|
+
const dy = yPx - y;
|
|
10
|
+
return { x, y, distSq: dx * dx + dy * dy };
|
|
11
|
+
}
|
|
12
|
+
/** The same for a mark with no extent to clamp into. */
|
|
13
|
+
export function inkAtPoint(xPx, yPx, x, y) {
|
|
14
|
+
const dx = xPx - x;
|
|
15
|
+
const dy = yPx - y;
|
|
16
|
+
return { x, y, distSq: dx * dx + dy * dy };
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The nearest of `candidates` by where each one's ink is, or undefined when
|
|
20
|
+
* none beats `maxDistSq`.
|
|
21
|
+
*
|
|
22
|
+
* This is the whole of every shape's `hitNearest` but the geometry: `inkAt`
|
|
23
|
+
* answers where instance `i` put ink and how far that is, and this keeps the
|
|
24
|
+
* closest. Only a STRICTLY nearer candidate replaces the best, so on a tie the
|
|
25
|
+
* first wins, so a caller that iterates back to front gets the mark on top.
|
|
26
|
+
* Five shapes used to spell that rule out separately.
|
|
27
|
+
*
|
|
28
|
+
* `inkAt` may answer `undefined` for an instance that drew nothing at all, as
|
|
29
|
+
* against one that drew somewhere far away: a bar clipped out of its band has
|
|
30
|
+
* no ink to be near.
|
|
31
|
+
*/
|
|
32
|
+
export function nearestInk(candidates, maxDistSq, inkAt) {
|
|
33
|
+
let best;
|
|
34
|
+
let bestDistSq = maxDistSq;
|
|
35
|
+
for (const index of candidates) {
|
|
36
|
+
const ink = inkAt(index);
|
|
37
|
+
if (ink && ink.distSq < bestDistSq) {
|
|
38
|
+
bestDistSq = ink.distSq;
|
|
39
|
+
// Field by field, not `{ index, ...ink }`, which is both slower and one
|
|
40
|
+
// shape away from minting a hidden class per ink source and making every
|
|
41
|
+
// downstream `hit.x` read polymorphic.
|
|
42
|
+
best = { index, x: ink.x, y: ink.y, distSq: ink.distSq };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return best;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The hit test a shape's `ink` implies: the nearest of its boxes, edges
|
|
49
|
+
* included, so a point inside one answers it at distance 0.
|
|
50
|
+
*/
|
|
51
|
+
export function inkHitNearest(ink) {
|
|
52
|
+
return (channels, block, frame, params, xPx, yPx, candidates, maxDistSq) => nearestInk(candidates, maxDistSq, i => {
|
|
53
|
+
const r = ink(channels, block, frame, params, i);
|
|
54
|
+
return r && inkOnRect(xPx, yPx, r.left, r.top, r.width, r.height);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/** A shape's own `hitNearest`, else the one its `ink` implies, else none. */
|
|
58
|
+
export function shapeHitNearest(shape) {
|
|
59
|
+
return (shape.hitNearest ??
|
|
60
|
+
(shape.ink ? inkHitNearest(shape.ink.bind(shape)) : undefined));
|
|
61
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { RenderBlock } from '../renderBlock.ts';
|
|
2
|
+
import type { InkRect, Mark, MarkFrame } from './types.ts';
|
|
3
|
+
/** `r` moved by (`dx`, `dy`), its stroke with it. */
|
|
4
|
+
export declare function shiftInk<R extends InkRect>(r: R, dx: number, dy: number): R;
|
|
5
|
+
/** One instance of one mark in a display's list, as a highlight names it. */
|
|
6
|
+
export interface MarkInstance {
|
|
7
|
+
mark: number;
|
|
8
|
+
index: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The boxes a set of instances painted this frame: each mark's `ink` for the
|
|
12
|
+
* instances a display names in each region, clipped to the block's column the
|
|
13
|
+
* way the painter was, or to the canvas for a mark spanning the view, asked
|
|
14
|
+
* over `regionKeys` (the blocks' own by default). What a highlight guide
|
|
15
|
+
* draws, so a display says WHICH instances are lit and nothing about where
|
|
16
|
+
* they are.
|
|
17
|
+
*/
|
|
18
|
+
export declare function inkOfInstances<TRegion, TState extends MarkFrame>(marks: readonly Mark<TRegion, TState>[], blocks: readonly RenderBlock[], regionOf: (displayedRegionIndex: number) => TRegion | undefined, state: TState, instancesOf: (displayedRegionIndex: number) => Iterable<MarkInstance> | undefined, regionKeys?: Iterable<number>): InkRect[];
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { canvasWideBlock } from '../renderBlock.js';
|
|
2
|
+
/** `r` moved by (`dx`, `dy`), its stroke with it. */
|
|
3
|
+
export function shiftInk(r, dx, dy) {
|
|
4
|
+
const { stroke } = r;
|
|
5
|
+
return {
|
|
6
|
+
...r,
|
|
7
|
+
left: r.left + dx,
|
|
8
|
+
top: r.top + dy,
|
|
9
|
+
...(stroke
|
|
10
|
+
? {
|
|
11
|
+
stroke: {
|
|
12
|
+
...stroke,
|
|
13
|
+
originX: stroke.originX + dx,
|
|
14
|
+
originY: stroke.originY + dy,
|
|
15
|
+
},
|
|
16
|
+
}
|
|
17
|
+
: {}),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function clippedTo(r, x0, x1) {
|
|
21
|
+
const left = Math.max(r.left, x0);
|
|
22
|
+
const right = Math.min(r.left + r.width, x1);
|
|
23
|
+
return right >= left ? { ...r, left, width: right - left } : undefined;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The boxes a set of instances painted this frame: each mark's `ink` for the
|
|
27
|
+
* instances a display names in each region, clipped to the block's column the
|
|
28
|
+
* way the painter was, or to the canvas for a mark spanning the view, asked
|
|
29
|
+
* over `regionKeys` (the blocks' own by default). What a highlight guide
|
|
30
|
+
* draws, so a display says WHICH instances are lit and nothing about where
|
|
31
|
+
* they are.
|
|
32
|
+
*/
|
|
33
|
+
export function inkOfInstances(marks, blocks, regionOf, state, instancesOf, regionKeys) {
|
|
34
|
+
const rects = [];
|
|
35
|
+
for (const block of blocks) {
|
|
36
|
+
const region = regionOf(block.displayedRegionIndex);
|
|
37
|
+
const instances = region && instancesOf(block.displayedRegionIndex);
|
|
38
|
+
if (!instances) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const x0 = Math.min(block.screenStartPx, block.screenEndPx);
|
|
42
|
+
const x1 = Math.max(block.screenStartPx, block.screenEndPx);
|
|
43
|
+
for (const { mark, index } of instances) {
|
|
44
|
+
const m = marks[mark];
|
|
45
|
+
const r = m && !m.spansView && m.ink?.(region, block, state, index);
|
|
46
|
+
const clipped = r && clippedTo(r, x0, x1);
|
|
47
|
+
if (clipped) {
|
|
48
|
+
rects.push(clipped);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (marks.some(m => m.spansView)) {
|
|
53
|
+
for (const key of regionKeys ?? blocks.map(b => b.displayedRegionIndex)) {
|
|
54
|
+
const region = regionOf(key);
|
|
55
|
+
const instances = region && instancesOf(key);
|
|
56
|
+
if (!instances) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const block = canvasWideBlock(key, state.canvasWidth);
|
|
60
|
+
for (const { mark, index } of instances) {
|
|
61
|
+
const m = marks[mark];
|
|
62
|
+
const r = m?.spansView && m.ink?.(region, block, state, index);
|
|
63
|
+
const clipped = r && clippedTo(r, 0, state.canvasWidth);
|
|
64
|
+
if (clipped) {
|
|
65
|
+
rects.push(clipped);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return rects;
|
|
71
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { RenderBlock } from '../renderBlock.ts';
|
|
2
|
+
import type { Mark, MarkContext2D, MarkFrame } from './types.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Paint one frame's blocks with a mark list — the Canvas2D half of a
|
|
5
|
+
* declaration, and the SVG export path, since `MarkContext2D` is satisfied by
|
|
6
|
+
* both a real 2D context and the SVG one. A mark that spans the view paints
|
|
7
|
+
* after the blocks, over the whole canvas, from every region in `regions`.
|
|
8
|
+
*
|
|
9
|
+
* Its own module, away from the backend that calls it: an SVG export runs with
|
|
10
|
+
* no backend attached at all (and must, or a headless export would depend on a
|
|
11
|
+
* GPU device having been probed), so the path that draws must not pull the HAL
|
|
12
|
+
* in behind it.
|
|
13
|
+
*/
|
|
14
|
+
export declare function paintMarkBlocks<TRegion, TState extends MarkFrame>(ctx: MarkContext2D, marks: readonly Mark<TRegion, TState>[], regions: ReadonlyMap<number, TRegion>, blocks: RenderBlock[], state: TState): void;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { forEachClippedBlock, withClip } from '../canvas2dUtils.js';
|
|
2
|
+
import { canvasWideBlock } from '../renderBlock.js';
|
|
3
|
+
/**
|
|
4
|
+
* Paint one frame's blocks with a mark list — the Canvas2D half of a
|
|
5
|
+
* declaration, and the SVG export path, since `MarkContext2D` is satisfied by
|
|
6
|
+
* both a real 2D context and the SVG one. A mark that spans the view paints
|
|
7
|
+
* after the blocks, over the whole canvas, from every region in `regions`.
|
|
8
|
+
*
|
|
9
|
+
* Its own module, away from the backend that calls it: an SVG export runs with
|
|
10
|
+
* no backend attached at all (and must, or a headless export would depend on a
|
|
11
|
+
* GPU device having been probed), so the path that draws must not pull the HAL
|
|
12
|
+
* in behind it.
|
|
13
|
+
*/
|
|
14
|
+
export function paintMarkBlocks(ctx, marks, regions, blocks, state) {
|
|
15
|
+
const blockMarks = marks.filter(m => !m.spansView);
|
|
16
|
+
forEachClippedBlock(ctx, blocks, state.canvasWidth, state.canvasHeight, block => regions.get(block.displayedRegionIndex), (region, block) => {
|
|
17
|
+
for (const mark of blockMarks) {
|
|
18
|
+
mark.paintBlock(ctx, region, block, state);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
const viewMarks = marks.filter(m => m.spansView);
|
|
22
|
+
if (viewMarks.length > 0) {
|
|
23
|
+
withClip(ctx, 0, 0, state.canvasWidth, state.canvasHeight, () => {
|
|
24
|
+
for (const [key, region] of regions) {
|
|
25
|
+
const block = canvasWideBlock(key, state.canvasWidth);
|
|
26
|
+
for (const mark of viewMarks) {
|
|
27
|
+
mark.paintBlock(ctx, region, block, state);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Mark, MarkFrame, PlannedPass } from './types.ts';
|
|
2
|
+
/**
|
|
3
|
+
* A mark list resolved against one frame's state: the marks whose `enabled`
|
|
4
|
+
* said yes, in list order, and the passes they draw.
|
|
5
|
+
*
|
|
6
|
+
* `drawMarks` asks every mark's gate, lens and staged compare per block, which
|
|
7
|
+
* is the right shape for a display that stages its uniforms through the marks
|
|
8
|
+
* — and at 120 section blocks a frame it is the whole cost the pileup measured
|
|
9
|
+
* and declined (`benches/pileupUniformWrite.bench.ts` has the table). A
|
|
10
|
+
* display that writes the uniforms itself, once per section, has nothing left
|
|
11
|
+
* for the per-block walk to decide: `planMarks` answers the frame question
|
|
12
|
+
* once, `drawPlannedPasses` issues a `drawPass` per planned mark against
|
|
13
|
+
* whatever the caller staged, and `marks` is the painter's list.
|
|
14
|
+
*/
|
|
15
|
+
export interface MarkPlan<TRegion, TState extends MarkFrame> {
|
|
16
|
+
readonly marks: readonly Mark<TRegion, TState>[];
|
|
17
|
+
readonly passes: readonly PlannedPass[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Resolve which marks draw this frame. Throws for an enabled mark the plan
|
|
21
|
+
* form cannot carry — one with a `band` or a `paintsBlock` — since a plan
|
|
22
|
+
* neither scissors nor asks the block anything, and without the throw the GPU
|
|
23
|
+
* would draw what Canvas2D declines.
|
|
24
|
+
*/
|
|
25
|
+
export declare function planMarks<TRegion, TState extends MarkFrame>(marks: readonly Mark<TRegion, TState>[], state: TState): MarkPlan<TRegion, TState>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve which marks draw this frame. Throws for an enabled mark the plan
|
|
3
|
+
* form cannot carry — one with a `band` or a `paintsBlock` — since a plan
|
|
4
|
+
* neither scissors nor asks the block anything, and without the throw the GPU
|
|
5
|
+
* would draw what Canvas2D declines.
|
|
6
|
+
*/
|
|
7
|
+
export function planMarks(marks, state) {
|
|
8
|
+
const enabled = [];
|
|
9
|
+
const passes = [];
|
|
10
|
+
for (const mark of marks) {
|
|
11
|
+
if (!mark.enabled || mark.enabled(state)) {
|
|
12
|
+
if (!mark.planned) {
|
|
13
|
+
throw new Error(`mark ${mark.pass.id} declares a band or a block gate, which a frame plan does not carry; draw it through drawMarks`);
|
|
14
|
+
}
|
|
15
|
+
enabled.push(mark);
|
|
16
|
+
passes.push(mark.planned);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return { marks: enabled, passes };
|
|
20
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { MarkColorScale, MarkThreshold } from './types.ts';
|
|
2
|
+
/**
|
|
3
|
+
* A colour channel as a shape reads it: `color`, the packed ABGR the worker
|
|
4
|
+
* resolved — one per instance, or one number for all of them — or
|
|
5
|
+
* `colorValue`, the raw values a frame's {@link MarkColorScale} turns into
|
|
6
|
+
* colours here. A shape reading a scale declares both optional and asks
|
|
7
|
+
* {@link colorBits} for the GPU and {@link paintColors} for Canvas2D.
|
|
8
|
+
*/
|
|
9
|
+
export interface ColorChannel {
|
|
10
|
+
color?: Uint32Array | number;
|
|
11
|
+
colorValue?: Float32Array;
|
|
12
|
+
/** The Canvas2D bake, memoized on the payload; nothing else writes it. */
|
|
13
|
+
rampBake?: RampBake;
|
|
14
|
+
/** A constant `color` as Canvas2D paints it, memoized as `rampBake` is. */
|
|
15
|
+
constantBake?: Uint32Array;
|
|
16
|
+
}
|
|
17
|
+
interface RampBake {
|
|
18
|
+
values: Float32Array;
|
|
19
|
+
scale: MarkColorScale;
|
|
20
|
+
colors: Uint32Array;
|
|
21
|
+
}
|
|
22
|
+
/** Whether a colour scale is a threshold rather than a ramp. */
|
|
23
|
+
export declare function isThreshold(scale: MarkColorScale): scale is MarkThreshold;
|
|
24
|
+
type Rgba = [number, number, number, number];
|
|
25
|
+
type Bands = [Rgba, Rgba, Rgba, Rgba, Rgba, Rgba, Rgba, Rgba, Rgba];
|
|
26
|
+
/**
|
|
27
|
+
* `markColor.slang`'s uniforms for a frame's colour scale, or for none: the
|
|
28
|
+
* mode, a ramp's domain and middle, and a threshold's cuts and band colours.
|
|
29
|
+
* Every valued shape's uniform block carries the set.
|
|
30
|
+
*/
|
|
31
|
+
export declare function rampUniforms(scale: MarkColorScale | undefined): {
|
|
32
|
+
rampMode: number;
|
|
33
|
+
rampMin: number;
|
|
34
|
+
rampMax: number;
|
|
35
|
+
rampMidNorm: number;
|
|
36
|
+
colorCutCount: number;
|
|
37
|
+
colorCuts: [Rgba, Rgba];
|
|
38
|
+
colorBands: Bands;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* The 4-byte instance lane, for the packer. Under a scale it is the value's
|
|
42
|
+
* float32 bits viewed as the `uint` the attribute declares — the reinterpret
|
|
43
|
+
* `markColor.slang`'s `asfloat` undoes, and the reason a quantitative colour
|
|
44
|
+
* adds no lane. A constant colour fills a lane for the pack alone, which the
|
|
45
|
+
* payload never holds.
|
|
46
|
+
*/
|
|
47
|
+
export declare function colorBits(c: ColorChannel, count: number): ArrayLike<number>;
|
|
48
|
+
/** Instance `i`'s `color` alone, as a one-instance channel carries it. */
|
|
49
|
+
export declare function instanceColor(color: ColorChannel['color'], i: number): number | Uint32Array<ArrayBufferLike> | undefined;
|
|
50
|
+
/** A value lane's float32 values viewed as their bits. */
|
|
51
|
+
export declare function rampValueBits(values: Float32Array): Uint32Array<ArrayBufferLike>;
|
|
52
|
+
/**
|
|
53
|
+
* A value lane's values kept where `keep` says, copied as bits: a JS number
|
|
54
|
+
* read out of the lane may lose the payload `RAMP_NO_VALUE_BITS` marks a
|
|
55
|
+
* feature with no value by.
|
|
56
|
+
*/
|
|
57
|
+
export declare function keepRampValues(values: Float32Array, keep: (value: number, index: number) => boolean): Float32Array<ArrayBuffer>;
|
|
58
|
+
/**
|
|
59
|
+
* The interval a value is in on a threshold: how many of the ascending cuts it
|
|
60
|
+
* is at or past, `thresholdIndex`'s rule and the shader's `thresholdBand`.
|
|
61
|
+
*/
|
|
62
|
+
export declare function thresholdBandOf(value: number, cuts: readonly number[]): number;
|
|
63
|
+
/**
|
|
64
|
+
* The packed colours a painter fills with. Under a scale the values are baked
|
|
65
|
+
* against it once and kept on the payload, so a repaint at an unchanged scale
|
|
66
|
+
* — every pan and every hover — walks no values and the painters' fill
|
|
67
|
+
* batching still sees runs of one colour. The bake reruns when the domain, the
|
|
68
|
+
* scale type, the ramp's bytes or a threshold's cuts or colours move. A
|
|
69
|
+
* constant colour expands once and is kept the same way.
|
|
70
|
+
*/
|
|
71
|
+
export declare function paintColors(c: ColorChannel, count: number, scale: MarkColorScale | undefined): ArrayLike<number>;
|
|
72
|
+
export {};
|