@weasel-js/core 1.8.1 → 1.9.0

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.
Files changed (129) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +9 -5
  3. package/dist/{builtins-CMEh6-Oc.d.ts → builtins-CQcvcLXh.d.ts} +98 -31
  4. package/dist/canvas/DrawCanvas.js +33 -33
  5. package/dist/canvas/DrawCanvas.js.map +1 -1
  6. package/dist/canvas/MinimapCanvas.js +29 -29
  7. package/dist/canvas/MinimapCanvas.js.map +1 -1
  8. package/dist/canvas/NodeShape.js +63 -54
  9. package/dist/canvas/NodeShape.js.map +1 -1
  10. package/dist/canvas/SceneCanvas/features.js +26 -5
  11. package/dist/canvas/SceneCanvas/features.js.map +1 -1
  12. package/dist/canvas/SceneCanvas/useSceneSelectTool.js +3 -2
  13. package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
  14. package/dist/canvas/SceneCanvas.js +526 -529
  15. package/dist/canvas/SceneCanvas.js.map +1 -1
  16. package/dist/canvas/SceneViewCanvas.js +35 -35
  17. package/dist/canvas/SceneViewCanvas.js.map +1 -1
  18. package/dist/canvas/defaultDrawOne.js +6 -1
  19. package/dist/canvas/defaultDrawOne.js.map +1 -1
  20. package/dist/canvas/pickWalk.js.map +1 -1
  21. package/dist/canvas/sceneAdapter.js.map +1 -1
  22. package/dist/canvas/useHitObserver.js +23 -0
  23. package/dist/canvas/useHitObserver.js.map +1 -0
  24. package/dist/canvas/useHostAnchor.js.map +1 -1
  25. package/dist/core/device/useDeviceProfile.js.map +1 -1
  26. package/dist/core/scene/derivedPath.js.map +1 -1
  27. package/dist/core/scene/effectivePose.js.map +1 -1
  28. package/dist/core/scene/kitRegistry.js.map +1 -1
  29. package/dist/core/scene/scene.js.map +1 -1
  30. package/dist/core/scene/types.js.map +1 -1
  31. package/dist/core/scene/useScene.js.map +1 -1
  32. package/dist/core/viewport/fitViewToBounds.js +3 -3
  33. package/dist/core/viewport/fitViewToBounds.js.map +1 -1
  34. package/dist/core/viewport/screenSlop.js +8 -2
  35. package/dist/core/viewport/screenSlop.js.map +1 -1
  36. package/dist/features/minimap/actions.js.map +1 -1
  37. package/dist/features/paths/markerCommands.js +95 -63
  38. package/dist/features/paths/markerCommands.js.map +1 -1
  39. package/dist/features/paths/tessellate/dash.js +52 -0
  40. package/dist/features/paths/tessellate/dash.js.map +1 -0
  41. package/dist/features/paths/tessellate/insets.js +32 -0
  42. package/dist/features/paths/tessellate/insets.js.map +1 -0
  43. package/dist/features/paths/tessellate/metric.js +72 -0
  44. package/dist/features/paths/tessellate/metric.js.map +1 -0
  45. package/dist/features/paths/tessellate/stroke.js +74 -134
  46. package/dist/features/paths/tessellate/stroke.js.map +1 -1
  47. package/dist/features/paths/tessellate/vertexWidths.js +32 -0
  48. package/dist/features/paths/tessellate/vertexWidths.js.map +1 -0
  49. package/dist/features/pointer/PointerContext.js +2 -2
  50. package/dist/features/pointer/PointerContext.js.map +1 -1
  51. package/dist/features/poseRun/usePoseRun.js.map +1 -1
  52. package/dist/features/text/useTextEdit.js +86 -84
  53. package/dist/features/text/useTextEdit.js.map +1 -1
  54. package/dist/index.d.ts +78 -39
  55. package/dist/index.js +148 -148
  56. package/dist/interactions/actions/defaults/align.js.map +1 -1
  57. package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
  58. package/dist/interactions/actions/defaults/areaSelect.js.map +1 -1
  59. package/dist/interactions/actions/defaults/booleans.js.map +1 -1
  60. package/dist/interactions/actions/defaults/cancelGesture.js.map +1 -1
  61. package/dist/interactions/actions/defaults/clearSelection.js.map +1 -1
  62. package/dist/interactions/actions/defaults/clipboard.js.map +1 -1
  63. package/dist/interactions/actions/defaults/clone.js.map +1 -1
  64. package/dist/interactions/actions/defaults/createOutlines.js +1 -1
  65. package/dist/interactions/actions/defaults/createOutlines.js.map +1 -1
  66. package/dist/interactions/actions/defaults/createPaintAction.js.map +1 -1
  67. package/dist/interactions/actions/defaults/delete.js.map +1 -1
  68. package/dist/interactions/actions/defaults/distribute.js.map +1 -1
  69. package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
  70. package/dist/interactions/actions/defaults/editAnchors.js.map +1 -1
  71. package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
  72. package/dist/interactions/actions/defaults/enterTextEdit.js.map +1 -1
  73. package/dist/interactions/actions/defaults/escape.js +1 -1
  74. package/dist/interactions/actions/defaults/escape.js.map +1 -1
  75. package/dist/interactions/actions/defaults/exitPathEdit.js.map +1 -1
  76. package/dist/interactions/actions/defaults/flip.js.map +1 -1
  77. package/dist/interactions/actions/defaults/group.js.map +1 -1
  78. package/dist/interactions/actions/defaults/ingest.js.map +1 -1
  79. package/dist/interactions/actions/defaults/insert.js.map +1 -1
  80. package/dist/interactions/actions/defaults/insertPathAnchor.js +1 -1
  81. package/dist/interactions/actions/defaults/insertPathAnchor.js.map +1 -1
  82. package/dist/interactions/actions/defaults/lassoSelect.js.map +1 -1
  83. package/dist/interactions/actions/defaults/move.js.map +1 -1
  84. package/dist/interactions/actions/defaults/nudge.js.map +1 -1
  85. package/dist/interactions/actions/defaults/pinchZoom.js.map +1 -1
  86. package/dist/interactions/actions/defaults/reorder.js.map +1 -1
  87. package/dist/interactions/actions/defaults/requiresSelection.js.map +1 -1
  88. package/dist/interactions/actions/defaults/resize.js.map +1 -1
  89. package/dist/interactions/actions/defaults/rotate.js.map +1 -1
  90. package/dist/interactions/actions/defaults/selectAll.js.map +1 -1
  91. package/dist/interactions/actions/defaults/slice.js.map +1 -1
  92. package/dist/interactions/actions/defaults/toolActivate.js.map +1 -1
  93. package/dist/interactions/actions/defaults/toolResetToDefault.js.map +1 -1
  94. package/dist/interactions/actions/defaults/undoRedo.js +1 -1
  95. package/dist/interactions/actions/defaults/undoRedo.js.map +1 -1
  96. package/dist/interactions/actions/defaults/viewportDragPan.js.map +1 -1
  97. package/dist/interactions/actions/defaults/viewportWheelPan.js.map +1 -1
  98. package/dist/interactions/actions/defaults/viewportZoom.js.map +1 -1
  99. package/dist/interactions/actions/planeInput.js.map +1 -1
  100. package/dist/interactions/gestures/shared/screenTolerance.js.map +1 -1
  101. package/dist/{registerProgram-ateHDGjw.d.ts → registerProgram-BMZRDgrh.d.ts} +4 -0
  102. package/dist/renderer/cache/markerCommandCache.js +46 -42
  103. package/dist/renderer/cache/markerCommandCache.js.map +1 -1
  104. package/dist/renderer/cache/outlineStrokeMeshCache.js +30 -25
  105. package/dist/renderer/cache/outlineStrokeMeshCache.js.map +1 -1
  106. package/dist/renderer/cache/strokeMeshCache.js +42 -31
  107. package/dist/renderer/cache/strokeMeshCache.js.map +1 -1
  108. package/dist/renderer/cullDrawCommands.js +32 -31
  109. package/dist/renderer/cullDrawCommands.js.map +1 -1
  110. package/dist/renderer/draw.js +315 -362
  111. package/dist/renderer/draw.js.map +1 -1
  112. package/dist/renderer/glyphOutlineMesh.js +67 -0
  113. package/dist/renderer/glyphOutlineMesh.js.map +1 -0
  114. package/dist/renderer/math/mat3.js +8 -3
  115. package/dist/renderer/math/mat3.js.map +1 -1
  116. package/dist/renderer/strokeLengths.js +50 -0
  117. package/dist/renderer/strokeLengths.js.map +1 -0
  118. package/dist/renderer.d.ts +2 -2
  119. package/dist/{test-seams-fYf6m5pu.d.ts → test-seams-CuIxe0xY.d.ts} +1 -1
  120. package/dist/test-seams.d.ts +2 -2
  121. package/dist/tools/builtin/eyedropper/useEyedropperTool.js.map +1 -1
  122. package/dist/tools/builtin/pen/usePenTool.js +1 -1
  123. package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
  124. package/dist/tools/builtin/select/selectionContributions.js +43 -23
  125. package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
  126. package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
  127. package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
  128. package/dist/version.js +1 -1
  129. package/package.json +17 -17
@@ -1 +1 @@
1
- {"version":3,"file":"markerCommands.js","names":["placed","src","path","cos","site","sin","coords","i","x","y","resolvePaint","v","p","fallback","stroke","markerHeads","want","refFor","role","out","pl","extractPolylines","markerSites","ref","entry","getMarker","markerKeyOf","size","resolveMarkerSize","strokeWidth","angle","headCommands","heads","fill","outline","markerReadsKey","key","field","READINGS_PER_ENTRY","REACH_BY_READING","REACH_BY_STROKE","measureUnitReach","b","boundsOfPath","unitReach","byStroke","reach","byReading","markerReach","scale"],"sources":["../../../src/features/paths/markerCommands.ts"],"sourcesContent":["/**\n * Turning a stroke's markers into draw commands.\n *\n * Separate `PathDrawCommand`s rather than triangles appended to the stroke\n * ribbon: an entry may carry a fill and an outline at once, or a paint that\n * differs from the line's, neither of which one mesh can express — and folding\n * them in would drag the whole marker vocabulary into the ribbon cache key.\n */\n\nimport type { MarkerRef, Stroke } from '@weasel-js/paint';\nimport { type Path, type PolygonPath, boundsOfPath } from '@weasel-js/geom';\nimport type { PathDrawCommand } from '../../renderer/DrawCommand';\nimport { getMarker, type MarkerEntry, type MarkerPaint } from '../../core/strokeMarkers';\nimport { markerKeyOf, resolveMarkerSize } from '../../core/markerInset';\nimport { extractPolylines } from '@weasel-js/geom/tessellate';\nimport { markerSites, type MarkerSite } from './markerSites';\n\n/** Rotate + translate a marker's geometry onto its site. */\nfunction placed(path: Path, site: MarkerSite): PolygonPath {\n const src = path as PolygonPath;\n const cos = Math.cos(site.angle);\n const sin = Math.sin(site.angle);\n const coords = new Float32Array(src.coords.length);\n for (let i = 0; i < src.coords.length; i += 2) {\n const x = src.coords[i], y = src.coords[i + 1];\n coords[i] = site.x + x * cos - y * sin;\n coords[i + 1] = site.y + x * sin + y * cos;\n }\n return { kind: 'polygon', commands: src.commands, coords, fillRule: src.fillRule };\n}\n\nfunction resolvePaint(p: MarkerPaint | undefined, stroke: Stroke, fallback: MarkerPaint) {\n const v = p ?? fallback;\n if (v === 'none') return undefined;\n return v === 'line' ? stroke.paint : v;\n}\n\n/** One head's placed geometry, before any paint is applied. */\nexport interface MarkerHead {\n readonly entry: MarkerEntry;\n readonly path: PolygonPath;\n readonly size: number;\n}\n\n/**\n * Every marker head for `path` under `stroke`, placed but unpainted.\n * `strokeWidth` is the already width-resolved stroke width; `flattenTolerance`\n * matches what the ribbon used, so heads land on the same flattened vertices\n * the stroke did.\n */\nexport function markerHeads(\n path: Path,\n stroke: Stroke,\n strokeWidth: number,\n flattenTolerance: number | undefined,\n): MarkerHead[] {\n const want = {\n start: stroke.markerStart !== undefined,\n mid: stroke.markerMid !== undefined,\n end: stroke.markerEnd !== undefined,\n };\n if (!want.start && !want.mid && !want.end) return [];\n\n const refFor = (role: MarkerSite['role']): MarkerRef | undefined =>\n role === 'start' ? stroke.markerStart : role === 'mid' ? stroke.markerMid : stroke.markerEnd;\n\n const out: MarkerHead[] = [];\n for (const pl of extractPolylines(path, { flattenTolerance })) {\n for (const site of markerSites(pl, want)) {\n const ref = refFor(site.role);\n if (ref === undefined) continue;\n const entry = getMarker(markerKeyOf(ref));\n if (entry === undefined) continue;\n const size = resolveMarkerSize(ref, strokeWidth);\n const angle = entry.orient === undefined || entry.orient === 'auto' ? site.angle : entry.orient;\n out.push({ entry, size, path: placed(entry.path({ size, stroke }), { ...site, angle }) });\n }\n }\n return out;\n}\n\n/** `heads` as draw commands in `stroke`'s paint. A head whose fill and outline\n * both resolve to no paint is dropped. */\nexport function headCommands(heads: readonly MarkerHead[], stroke: Stroke): PathDrawCommand[] {\n const out: PathDrawCommand[] = [];\n for (const { entry, path, size } of heads) {\n const fill = resolvePaint(entry.fill, stroke, 'line');\n const outline = entry.outline\n ? { paint: resolvePaint(entry.outline.paint, stroke, 'line'), width: entry.outline.width * size }\n : null;\n if (fill === undefined && (outline === null || outline.paint === undefined)) continue;\n out.push({\n kind: 'path',\n path,\n ...(fill ? { fill } : {}),\n ...(outline && outline.paint\n ? { stroke: { paint: outline.paint, width: outline.width, cap: 'round', join: 'round' } }\n : {}),\n });\n }\n return out;\n}\n\n/** Every marker command for `path` under `stroke`; see {@link markerHeads}. */\nexport function markerDrawCommands(\n path: Path,\n stroke: Stroke,\n strokeWidth: number,\n flattenTolerance: number | undefined,\n): PathDrawCommand[] {\n return headCommands(markerHeads(path, stroke, strokeWidth, flattenTolerance), stroke);\n}\n\n/**\n * A key for the stroke fields `entry`'s geometry reads, or `null` when the\n * entry does not say which it reads and only the stroke's identity can stand\n * in for them.\n */\nexport function markerReadsKey(entry: MarkerEntry, stroke: Stroke): string | null {\n if (entry.reads === undefined) return null;\n let key = '';\n for (const field of entry.reads) key += `${JSON.stringify(stroke[field]) ?? ''}|`;\n return key;\n}\n\n/** Distinct stroke readings kept per entry before its map is dropped. */\nconst READINGS_PER_ENTRY = 64;\n\n/** Each entry's farthest reach from its anchor at `size` 1, outline included,\n * per value of the fields it reads — or per stroke object, when it does not\n * say which. */\nconst REACH_BY_READING = new WeakMap<MarkerEntry, Map<string, number>>();\nconst REACH_BY_STROKE = new WeakMap<MarkerEntry, WeakMap<Stroke, number>>();\n\nfunction measureUnitReach(entry: MarkerEntry, stroke: Stroke): number {\n const b = boundsOfPath(entry.path({ size: 1, stroke }));\n return Math.max(Math.abs(b.x), Math.abs(b.x + b.width), Math.abs(b.y), Math.abs(b.y + b.height))\n + (entry.outline ? entry.outline.width / 2 : 0);\n}\n\nfunction unitReach(entry: MarkerEntry, stroke: Stroke): number {\n const key = markerReadsKey(entry, stroke);\n if (key === null) {\n let byStroke = REACH_BY_STROKE.get(entry);\n if (byStroke === undefined) REACH_BY_STROKE.set(entry, (byStroke = new WeakMap()));\n let reach = byStroke.get(stroke);\n if (reach === undefined) byStroke.set(stroke, (reach = measureUnitReach(entry, stroke)));\n return reach;\n }\n let byReading = REACH_BY_READING.get(entry);\n if (byReading === undefined) REACH_BY_READING.set(entry, (byReading = new Map()));\n let reach = byReading.get(key);\n if (reach === undefined) {\n if (byReading.size >= READINGS_PER_ENTRY) byReading.clear();\n byReading.set(key, (reach = measureUnitReach(entry, stroke)));\n }\n return reach;\n}\n\n/**\n * How far any of `stroke`'s markers can paint from the vertex it sits on, in\n * world units. `strokeWidth` is the resolved width and `scale` the view scale\n * a `{ px }` size resolves against. Culling and a path node's grab reach both\n * measure through here.\n */\nexport function markerReach(stroke: Stroke, strokeWidth: number, scale = 1): number {\n let reach = 0;\n for (const ref of [stroke.markerStart, stroke.markerMid, stroke.markerEnd]) {\n if (ref === undefined) continue;\n const entry = getMarker(markerKeyOf(ref));\n if (entry === undefined) continue;\n reach = Math.max(reach, unitReach(entry, stroke) * resolveMarkerSize(ref, strokeWidth, scale));\n }\n return reach;\n}\n"],"mappings":";;;;;;AAkBA,SAASA,EAAO,GAAY,GAA+B;CACzD,IAAMC,IAAMC,GACNC,IAAM,KAAK,IAAIC,EAAK,KAAK,GACzBC,IAAM,KAAK,IAAID,EAAK,KAAK,GACzBE,IAAS,IAAI,aAAaL,EAAI,OAAO,MAAM;CACjD,KAAK,IAAIM,IAAI,GAAGA,IAAIN,EAAI,OAAO,QAAQ,KAAK,GAAG;EAC7C,IAAMO,IAAIP,EAAI,OAAOM,IAAIE,IAAIR,EAAI,OAAOM,IAAI;EAE5C,AADA,EAAOA,KAAKH,EAAK,IAAII,IAAIL,IAAMM,IAAIJ,GACnC,EAAOE,IAAI,KAAKH,EAAK,IAAII,IAAIH,IAAMI,IAAIN;CACzC;CACA,OAAO;EAAE,MAAM;EAAW,UAAUF,EAAI;EAAU;EAAQ,UAAUA,EAAI;CAAS;AACnF;AAEA,SAASS,EAAa,GAA4B,GAAgB,GAAuB;CACvF,IAAMC,IAAIC,KAAKC;CACf,IAAIF,MAAM,QACV,OAAOA,MAAM,SAASG,EAAO,QAAQH;AACvC;AAeA,SAAgBI,EACd,GACA,GACA,GACA,GACc;CACd,IAAMC,IAAO;EACX,OAAOF,EAAO,gBAAgB,KAAA;EAC9B,KAAKA,EAAO,cAAc,KAAA;EAC1B,KAAKA,EAAO,cAAc,KAAA;CAC5B;CACA,IAAI,CAACE,EAAK,SAAS,CAACA,EAAK,OAAO,CAACA,EAAK,KAAK,OAAO,CAAC;CAEnD,IAAMC,KAAU,MACdC,MAAS,UAAUJ,EAAO,cAAcI,MAAS,QAAQJ,EAAO,YAAYA,EAAO,WAE/EK,IAAoB,CAAC;CAC3B,KAAK,IAAMC,KAAMC,EAAiBnB,GAAM,EAAE,oBAAiB,CAAC,GAC1D,KAAK,IAAME,KAAQkB,EAAYF,GAAIJ,CAAI,GAAG;EACxC,IAAMO,IAAMN,EAAOb,EAAK,IAAI;EAC5B,IAAImB,MAAQ,KAAA,GAAW;EACvB,IAAMC,IAAQC,EAAUC,EAAYH,CAAG,CAAC;EACxC,IAAIC,MAAU,KAAA,GAAW;EACzB,IAAMG,IAAOC,EAAkBL,GAAKM,CAAW,GACzCC,IAAQN,EAAM,WAAW,KAAA,KAAaA,EAAM,WAAW,SAASpB,EAAK,QAAQoB,EAAM;EACzF,EAAI,KAAK;GAAE;GAAO;GAAM,MAAMxB,EAAOwB,EAAM,KAAK;IAAE;IAAM;GAAO,CAAC,GAAG;IAAE,GAAGpB;IAAM;GAAM,CAAC;EAAE,CAAC;CAC1F;CAEF,OAAOe;AACT;AAIA,SAAgBY,EAAa,GAA8B,GAAmC;CAC5F,IAAMZ,IAAyB,CAAC;CAChC,KAAK,IAAM,EAAE,UAAO,SAAM,aAAUa,GAAO;EACzC,IAAMC,IAAOvB,EAAac,EAAM,MAAMV,GAAQ,MAAM,GAC9CoB,IAAUV,EAAM,UAClB;GAAE,OAAOd,EAAac,EAAM,QAAQ,OAAOV,GAAQ,MAAM;GAAG,OAAOU,EAAM,QAAQ,QAAQG;EAAK,IAC9F;EACJ,CAAIM,MAAS,KAAA,KAAcC,MAAY,QAAQA,EAAQ,UAAU,KAAA,MACjEf,EAAI,KAAK;GACP,MAAM;GACN;GACA,GAAIc,IAAO,EAAE,QAAK,IAAI,CAAC;GACvB,GAAIC,KAAWA,EAAQ,QACnB,EAAE,QAAQ;IAAE,OAAOA,EAAQ;IAAO,OAAOA,EAAQ;IAAO,KAAK;IAAS,MAAM;GAAQ,EAAE,IACtF,CAAC;EACP,CAAC;CACH;CACA,OAAOf;AACT;AAiBA,SAAgBgB,EAAe,GAAoB,GAA+B;CAChF,IAAIX,EAAM,UAAU,KAAA,GAAW,OAAO;CACtC,IAAIY,IAAM;CACV,KAAK,IAAMC,KAASb,EAAM,OAAO,KAAO,GAAG,KAAK,UAAUV,EAAOuB,EAAM,KAAK,GAAG;CAC/E,OAAOD;AACT;AAGA,IAAME,IAAqB,IAKrBC,oBAAmB,IAAI,QAA0C,GACjEC,oBAAkB,IAAI,QAA8C;AAE1E,SAASC,EAAiB,GAAoB,GAAwB;CACpE,IAAMC,IAAIC,EAAanB,EAAM,KAAK;EAAE,MAAM;EAAG;CAAO,CAAC,CAAC;CACtD,OAAO,KAAK,IAAI,KAAK,IAAIkB,EAAE,CAAC,GAAG,KAAK,IAAIA,EAAE,IAAIA,EAAE,KAAK,GAAG,KAAK,IAAIA,EAAE,CAAC,GAAG,KAAK,IAAIA,EAAE,IAAIA,EAAE,MAAM,CAAC,KAC1FlB,EAAM,UAAUA,EAAM,QAAQ,QAAQ,IAAI;AACjD;AAEA,SAASoB,EAAU,GAAoB,GAAwB;CAC7D,IAAMR,IAAMD,EAAeX,GAAOV,CAAM;CACxC,IAAIsB,MAAQ,MAAM;EAChB,IAAIS,IAAWL,EAAgB,IAAIhB,CAAK;EACxC,AAAIqB,MAAa,KAAA,KAAWL,EAAgB,IAAIhB,GAAQ,oBAAW,IAAI,QAAQ,CAAE;EACjF,IAAIsB,IAAQD,EAAS,IAAI/B,CAAM;EAE/B,OADIgC,MAAU,KAAA,KAAWD,EAAS,IAAI/B,GAAS,IAAQ2B,EAAiBjB,GAAOV,CAAM,CAAE,GAChFgC;CACT;CACA,IAAIC,IAAYR,EAAiB,IAAIf,CAAK;CAC1C,AAAIuB,MAAc,KAAA,KAAWR,EAAiB,IAAIf,GAAQ,oBAAY,IAAI,IAAI,CAAE;CAChF,IAAIsB,IAAQC,EAAU,IAAIX,CAAG;CAK7B,OAJIU,MAAU,KAAA,MACRC,EAAU,QAAQT,KAAoBS,EAAU,MAAM,GAC1DA,EAAU,IAAIX,GAAM,IAAQK,EAAiBjB,GAAOV,CAAM,CAAE,IAEvDgC;AACT;AAQA,SAAgBE,EAAY,GAAgB,GAAqB,IAAQ,GAAW;CAClF,IAAIF,IAAQ;CACZ,KAAK,IAAMvB,KAAO;EAACT,EAAO;EAAaA,EAAO;EAAWA,EAAO;CAAS,GAAG;EAC1E,IAAIS,MAAQ,KAAA,GAAW;EACvB,IAAMC,IAAQC,EAAUC,EAAYH,CAAG,CAAC;EACxC,AAAIC,MAAU,KAAA,MACd,IAAQ,KAAK,IAAIsB,GAAOF,EAAUpB,GAAOV,CAAM,IAAIc,EAAkBL,GAAKM,GAAaoB,CAAK,CAAC;CAC/F;CACA,OAAOH;AACT"}
1
+ {"version":3,"file":"markerCommands.js","names":["placed","src","path","cos","site","sin","coords","i","x","y","resolvePaint","v","p","fallback","stroke","markerHeads","refs","groups","role","space","spaces","want","out","pl","extractPolylines","inSpace","polylineIntoMetric","markerSites","ref","entry","getMarker","markerKeyOf","size","resolveMarkerSize","strokeWidth","angle","head","coordsOutOfMetric","headCommands","heads","fill","width","outline","markerReadsKey","key","field","READINGS_PER_ENTRY","REACH_BY_READING","REACH_BY_STROKE","measureUnitReach","b","boundsOfPath","unitReach","byStroke","reach","byReading","markerReach","unit"],"sources":["../../../src/features/paths/markerCommands.ts"],"sourcesContent":["/**\n * Turning a stroke's markers into draw commands.\n *\n * Separate `PathDrawCommand`s rather than triangles appended to the stroke\n * ribbon: an entry may carry a fill and an outline at once, or a paint that\n * differs from the line's, neither of which one mesh can express — and folding\n * them in would drag the whole marker vocabulary into the ribbon cache key.\n */\n\nimport type { MarkerRef, Stroke } from '@weasel-js/paint';\nimport { type Path, type PolygonPath, boundsOfPath } from '@weasel-js/geom';\nimport type { PathDrawCommand } from '../../renderer/DrawCommand';\nimport { getMarker, type MarkerEntry, type MarkerPaint } from '../../core/strokeMarkers';\nimport { markerKeyOf, resolveMarkerSize } from '../../core/markerInset';\nimport { extractPolylines } from '@weasel-js/geom/tessellate';\nimport { markerSites, type MarkerSite } from './markerSites';\nimport {\n coordsOutOfMetric, polylineIntoMetric, type ScreenSpace, type StrokeSpaces,\n} from './tessellate/metric';\n\n/** Rotate + translate a marker's geometry onto its site. */\nfunction placed(path: Path, site: MarkerSite): PolygonPath {\n const src = path as PolygonPath;\n const cos = Math.cos(site.angle);\n const sin = Math.sin(site.angle);\n const coords = new Float32Array(src.coords.length);\n for (let i = 0; i < src.coords.length; i += 2) {\n const x = src.coords[i], y = src.coords[i + 1];\n coords[i] = site.x + x * cos - y * sin;\n coords[i + 1] = site.y + x * sin + y * cos;\n }\n return { kind: 'polygon', commands: src.commands, coords, fillRule: src.fillRule };\n}\n\nfunction resolvePaint(p: MarkerPaint | undefined, stroke: Stroke, fallback: MarkerPaint) {\n const v = p ?? fallback;\n if (v === 'none') return undefined;\n return v === 'line' ? stroke.paint : v;\n}\n\n/** One head's placed geometry, before any paint is applied. */\nexport interface MarkerHead {\n readonly entry: MarkerEntry;\n readonly path: PolygonPath;\n readonly size: number;\n /** Where it was built, when not in world — `size` is in this space's units. */\n readonly space?: ScreenSpace;\n}\n\n/**\n * Every marker head for `path` under `stroke`, placed but unpainted.\n * `strokeWidth` is the already width-resolved stroke width; `flattenTolerance`\n * matches what the ribbon used, so heads land on the same flattened vertices\n * the stroke did. A head `spaces` puts in a screen space is placed and sized\n * there and comes back in world units, so it meets the line it caps.\n */\nexport function markerHeads(\n path: Path,\n stroke: Stroke,\n strokeWidth: number,\n flattenTolerance: number | undefined,\n spaces: StrokeSpaces = {},\n): MarkerHead[] {\n const refs = { start: stroke.markerStart, mid: stroke.markerMid, end: stroke.markerEnd };\n if (refs.start === undefined && refs.mid === undefined && refs.end === undefined) return [];\n\n // Heads sharing a space share one pass over the polyline mapped into it.\n const groups = new Map<ScreenSpace | undefined, { start: boolean; mid: boolean; end: boolean }>();\n for (const role of ['start', 'mid', 'end'] as const) {\n if (refs[role] === undefined) continue;\n const space = spaces[role];\n let want = groups.get(space);\n if (want === undefined) groups.set(space, (want = { start: false, mid: false, end: false }));\n want[role] = true;\n }\n\n const out: MarkerHead[] = [];\n for (const pl of extractPolylines(path, { flattenTolerance })) {\n for (const [space, want] of groups) {\n const inSpace = space ? { ...pl, points: pl.points.slice() } : pl;\n if (space) polylineIntoMetric(inSpace, space.metric);\n for (const site of markerSites(inSpace, want)) {\n const ref: MarkerRef | undefined = refs[site.role];\n if (ref === undefined) continue;\n const entry = getMarker(markerKeyOf(ref));\n if (entry === undefined) continue;\n const size = resolveMarkerSize(ref, strokeWidth);\n const angle = entry.orient === undefined || entry.orient === 'auto' ? site.angle : entry.orient;\n const head = placed(entry.path({ size, stroke }), { ...site, angle });\n if (space) coordsOutOfMetric(head.coords, space.metric);\n out.push(space ? { entry, size, path: head, space } : { entry, size, path: head });\n }\n }\n }\n return out;\n}\n\n/** `heads` as draw commands in `stroke`'s paint. A head whose fill and outline\n * both resolve to no paint is dropped. */\nexport function headCommands(heads: readonly MarkerHead[], stroke: Stroke): PathDrawCommand[] {\n const out: PathDrawCommand[] = [];\n for (const { entry, path, size, space } of heads) {\n const fill = resolvePaint(entry.fill, stroke, 'line');\n // A head built in a screen space is in world once placed, but its outline\n // width is still in that space's units — so it goes out as the screen\n // width it stands for, and is stroked as exactly as the line.\n const width = entry.outline ? entry.outline.width * size : 0;\n const outline = entry.outline\n ? { paint: resolvePaint(entry.outline.paint, stroke, 'line'), width: space ? { px: width * space.pxPerUnit } : width }\n : null;\n if (fill === undefined && (outline === null || outline.paint === undefined)) continue;\n out.push({\n kind: 'path',\n path,\n ...(fill ? { fill } : {}),\n ...(outline && outline.paint\n ? { stroke: { paint: outline.paint, width: outline.width, cap: 'round', join: 'round' } }\n : {}),\n });\n }\n return out;\n}\n\n/** Every marker command for `path` under `stroke`; see {@link markerHeads}. */\nexport function markerDrawCommands(\n path: Path,\n stroke: Stroke,\n strokeWidth: number,\n flattenTolerance: number | undefined,\n): PathDrawCommand[] {\n return headCommands(markerHeads(path, stroke, strokeWidth, flattenTolerance), stroke);\n}\n\n/**\n * A key for the stroke fields `entry`'s geometry reads, or `null` when the\n * entry does not say which it reads and only the stroke's identity can stand\n * in for them.\n */\nexport function markerReadsKey(entry: MarkerEntry, stroke: Stroke): string | null {\n if (entry.reads === undefined) return null;\n let key = '';\n for (const field of entry.reads) key += `${JSON.stringify(stroke[field]) ?? ''}|`;\n return key;\n}\n\n/** Distinct stroke readings kept per entry before its map is dropped. */\nconst READINGS_PER_ENTRY = 64;\n\n/** Each entry's farthest reach from its anchor at `size` 1, outline included,\n * per value of the fields it reads — or per stroke object, when it does not\n * say which. */\nconst REACH_BY_READING = new WeakMap<MarkerEntry, Map<string, number>>();\nconst REACH_BY_STROKE = new WeakMap<MarkerEntry, WeakMap<Stroke, number>>();\n\nfunction measureUnitReach(entry: MarkerEntry, stroke: Stroke): number {\n const b = boundsOfPath(entry.path({ size: 1, stroke }));\n return Math.max(Math.abs(b.x), Math.abs(b.x + b.width), Math.abs(b.y), Math.abs(b.y + b.height))\n + (entry.outline ? entry.outline.width / 2 : 0);\n}\n\nfunction unitReach(entry: MarkerEntry, stroke: Stroke): number {\n const key = markerReadsKey(entry, stroke);\n if (key === null) {\n let byStroke = REACH_BY_STROKE.get(entry);\n if (byStroke === undefined) REACH_BY_STROKE.set(entry, (byStroke = new WeakMap()));\n let reach = byStroke.get(stroke);\n if (reach === undefined) byStroke.set(stroke, (reach = measureUnitReach(entry, stroke)));\n return reach;\n }\n let byReading = REACH_BY_READING.get(entry);\n if (byReading === undefined) REACH_BY_READING.set(entry, (byReading = new Map()));\n let reach = byReading.get(key);\n if (reach === undefined) {\n if (byReading.size >= READINGS_PER_ENTRY) byReading.clear();\n byReading.set(key, (reach = measureUnitReach(entry, stroke)));\n }\n return reach;\n}\n\n/**\n * How far `stroke`'s markers can paint from the vertex each sits on, split by\n * where each is built: `world` in world units for a head with a world size,\n * `px` in screen pixels for one with a `{ px }` size. A head with no size\n * takes the stroke width, and with it the width's units. Culling and a path\n * node's grab reach both measure through here.\n */\nexport function markerReach(stroke: Stroke): { world: number; px: number } {\n const reach = { world: 0, px: 0 };\n const width = stroke.width ?? 1;\n for (const ref of [stroke.markerStart, stroke.markerMid, stroke.markerEnd]) {\n if (ref === undefined) continue;\n const entry = getMarker(markerKeyOf(ref));\n if (entry === undefined) continue;\n const size = typeof ref === 'string' || ref.size === undefined ? width : ref.size;\n const unit = unitReach(entry, stroke);\n if (typeof size === 'number') reach.world = Math.max(reach.world, unit * size);\n else reach.px = Math.max(reach.px, unit * size.px);\n }\n return reach;\n}\n"],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ //#region src/features/paths/tessellate/dash.ts
2
+ function e(e, n) {
3
+ let r = [], i = e.anchorA ?? new Uint32Array(e.points.length / 2), a = e.anchorB ?? new Uint32Array(e.points.length / 2), o = e.anchorT ?? new Float32Array(e.points.length / 2), s = 0, c = n[0], l = !0, u = l ? [e.points[0], e.points[1]] : null, d = l ? [i[0]] : null, f = l ? [a[0]] : null, p = l ? [o[0]] : null, m = () => {
4
+ u && u.length >= 4 && r.push({
5
+ points: u,
6
+ closed: !1,
7
+ anchorA: new Uint32Array(d),
8
+ anchorB: new Uint32Array(f),
9
+ anchorT: new Float32Array(p)
10
+ }), u = null, d = null, f = null, p = null, s = (s + 1) % n.length, c = n[s], l = !l;
11
+ }, h = e.points[0], g = e.points[1], _ = e.points.length / 2, v = e.closed ? _ : _ - 1;
12
+ for (let t = 0; t < v; t++) {
13
+ let n = (t + 1) % _, r = e.points[n * 2], s = e.points[n * 2 + 1], v = i[t], y = a[t], b = o[t], x = i[n], S = a[n], C = o[n], w = r - h, T = s - g, E = Math.hypot(w, T), D = w, O = T, k = E, A = 0;
14
+ for (; k > 1e-9;) if (k <= c) l && u && (u.push(r, s), d.push(x), f.push(S), p.push(C)), c -= k, h = r, g = s, A = E, k = 0, c <= 1e-9 && (m(), l && (u = [h, g], d = [x], f = [S], p = [C]));
15
+ else {
16
+ let e = c / k, t = h + D * e, n = g + O * e;
17
+ A += c;
18
+ let i = A / E, a, o, _;
19
+ v === x && y === S ? (a = v, o = y, _ = b + (C - b) * i) : i < .5 ? (a = v, o = y, _ = b) : (a = x, o = S, _ = C), l && u && (u.push(t, n), d.push(a), f.push(o), p.push(_)), h = t, g = n, D = r - h, O = s - g, k = Math.hypot(D, O), m(), l && (u = [h, g], d = [a], f = [o], p = [_]);
20
+ }
21
+ }
22
+ if (u && u.length >= 4) {
23
+ let n = {
24
+ points: u,
25
+ closed: !1,
26
+ anchorA: new Uint32Array(d),
27
+ anchorB: new Uint32Array(f),
28
+ anchorT: new Float32Array(p)
29
+ }, i = u[u.length - 2], a = u[u.length - 1], o = (e, t) => Math.hypot(i - e, a - t) <= 1e-9;
30
+ e.closed && r.length > 0 && o(r[0].points[0], r[0].points[1]) ? r[0] = t(n, r[0]) : e.closed && r.length === 0 && o(u[0], u[1]) ? r.push({
31
+ points: u.slice(0, -2),
32
+ closed: !0,
33
+ anchorA: n.anchorA.slice(0, -1),
34
+ anchorB: n.anchorB.slice(0, -1),
35
+ anchorT: n.anchorT.slice(0, -1)
36
+ }) : r.push(n);
37
+ }
38
+ return r;
39
+ }
40
+ function t(e, t) {
41
+ return {
42
+ points: [...e.points, ...t.points.slice(2)],
43
+ closed: !1,
44
+ anchorA: Uint32Array.from([...e.anchorA, ...t.anchorA.subarray(1)]),
45
+ anchorB: Uint32Array.from([...e.anchorB, ...t.anchorB.subarray(1)]),
46
+ anchorT: Float32Array.from([...e.anchorT, ...t.anchorT.subarray(1)])
47
+ };
48
+ }
49
+ //#endregion
50
+ export { e as splitForDash };
51
+
52
+ //# sourceMappingURL=dash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dash.js","names":["splitForDash","out","plA","pl","plB","plT","dashIdx","dashRemaining","dash","onPhase","curPts","curA","curB","curT","flushAndAdvance","prevX","prevY","ptCount","segCount","i","nextIdx","cx","cy","startA","startB","startT","endA","endB","endT","segFullDx","segFullDy","segFullLen","segDx","segDy","segLen","traveled","tConsume","ix","iy","frac","mA","mB","mT","tail","tx","ty","meets","x","y","joinAtSeam","head"],"sources":["../../../../src/features/paths/tessellate/dash.ts"],"sourcesContent":["import type { Polyline } from '@weasel-js/geom/tessellate';\n\n/**\n * Split a polyline into open sub-polylines for the \"on\" portions of a dash\n * pattern. Each output sub-polyline gets caps from the stroke's `cap` setting.\n * The \"off\" portions become invisible gaps. Anchor params are carried through:\n * sub-polyline endpoints inherit their source polyline-point's anchor params;\n * dash boundaries that land mid-segment (between two polyline points) snap\n * their anchor params to whichever endpoint they're closer to when the\n * segment crosses a path-anchor boundary, otherwise interpolate `t` linearly.\n */\nexport function splitForDash(pl: Polyline, dash: number[]): Polyline[] {\n const out: Polyline[] = [];\n const plA = pl.anchorA ?? new Uint32Array(pl.points.length / 2);\n const plB = pl.anchorB ?? new Uint32Array(pl.points.length / 2);\n const plT = pl.anchorT ?? new Float32Array(pl.points.length / 2);\n\n let dashIdx = 0;\n let dashRemaining = dash[0];\n let onPhase = true;\n let curPts: number[] | null = onPhase ? [pl.points[0], pl.points[1]] : null;\n let curA: number[] | null = onPhase ? [plA[0]] : null;\n let curB: number[] | null = onPhase ? [plB[0]] : null;\n let curT: number[] | null = onPhase ? [plT[0]] : null;\n\n const flushAndAdvance = () => {\n if (curPts && curPts.length >= 4) {\n out.push({\n points: curPts,\n closed: false,\n anchorA: new Uint32Array(curA!),\n anchorB: new Uint32Array(curB!),\n anchorT: new Float32Array(curT!),\n });\n }\n curPts = null; curA = null; curB = null; curT = null;\n dashIdx = (dashIdx + 1) % dash.length;\n dashRemaining = dash[dashIdx];\n onPhase = !onPhase;\n };\n\n let prevX = pl.points[0], prevY = pl.points[1];\n const ptCount = pl.points.length / 2;\n const segCount = pl.closed ? ptCount : ptCount - 1;\n\n for (let i = 0; i < segCount; i++) {\n const nextIdx = (i + 1) % ptCount;\n const cx = pl.points[nextIdx * 2], cy = pl.points[nextIdx * 2 + 1];\n const startA = plA[i], startB = plB[i], startT = plT[i];\n const endA = plA[nextIdx], endB = plB[nextIdx], endT = plT[nextIdx];\n const segFullDx = cx - prevX, segFullDy = cy - prevY;\n const segFullLen = Math.hypot(segFullDx, segFullDy);\n let segDx = segFullDx, segDy = segFullDy;\n let segLen = segFullLen;\n let traveled = 0;\n\n while (segLen > 1e-9) {\n if (segLen <= dashRemaining) {\n if (onPhase && curPts) {\n curPts.push(cx, cy);\n curA!.push(endA); curB!.push(endB); curT!.push(endT);\n }\n dashRemaining -= segLen;\n prevX = cx; prevY = cy;\n traveled = segFullLen;\n segLen = 0;\n if (dashRemaining <= 1e-9) {\n flushAndAdvance();\n if (onPhase) {\n curPts = [prevX, prevY];\n curA = [endA]; curB = [endB]; curT = [endT];\n }\n }\n } else {\n const tConsume = dashRemaining / segLen;\n const ix = prevX + segDx * tConsume;\n const iy = prevY + segDy * tConsume;\n traveled += dashRemaining;\n const frac = traveled / segFullLen;\n let mA: number, mB: number, mT: number;\n if (startA === endA && startB === endB) {\n mA = startA; mB = startB;\n mT = startT + (endT - startT) * frac;\n } else {\n // Cross-anchor segment: pick the nearer endpoint's params.\n if (frac < 0.5) { mA = startA; mB = startB; mT = startT; }\n else { mA = endA; mB = endB; mT = endT; }\n }\n if (onPhase && curPts) {\n curPts.push(ix, iy);\n curA!.push(mA); curB!.push(mB); curT!.push(mT);\n }\n prevX = ix; prevY = iy;\n segDx = cx - prevX; segDy = cy - prevY;\n segLen = Math.hypot(segDx, segDy);\n flushAndAdvance();\n if (onPhase) {\n curPts = [prevX, prevY];\n curA = [mA]; curB = [mB]; curT = [mT];\n }\n }\n }\n }\n\n if (curPts && curPts.length >= 4) {\n const tail: Polyline = {\n points: curPts,\n closed: false,\n anchorA: new Uint32Array(curA!),\n anchorB: new Uint32Array(curB!),\n anchorT: new Float32Array(curT!),\n };\n // A closed subpath's walk ends at the vertex it started from, so the run\n // still open there and the run that started there are one dash. Pushed\n // separately they are two butt-capped ribbons meeting at the seam — the\n // visible artifact whenever the pattern doesn't divide the perimeter.\n const tx = curPts[curPts.length - 2], ty = curPts[curPts.length - 1];\n const meets = (x: number, y: number) => Math.hypot(tx - x, ty - y) <= 1e-9;\n if (pl.closed && out.length > 0 && meets(out[0].points[0], out[0].points[1])) {\n out[0] = joinAtSeam(tail, out[0]);\n } else if (pl.closed && out.length === 0 && meets(curPts[0], curPts[1])) {\n // Every gap fell outside the perimeter: the dash is the whole loop, and\n // a loop stroked as an open run seams at its own start vertex too.\n out.push({\n points: curPts.slice(0, -2),\n closed: true,\n anchorA: tail.anchorA!.slice(0, -1),\n anchorB: tail.anchorB!.slice(0, -1),\n anchorT: tail.anchorT!.slice(0, -1),\n });\n } else {\n out.push(tail);\n }\n }\n return out;\n}\n\n/** `tail` continued by `head`, which starts where `tail` ends — the shared\n * vertex is carried once. */\nfunction joinAtSeam(tail: Polyline, head: Polyline): Polyline {\n return {\n points: [...tail.points, ...head.points.slice(2)],\n closed: false,\n anchorA: Uint32Array.from([...tail.anchorA!, ...head.anchorA!.subarray(1)]),\n anchorB: Uint32Array.from([...tail.anchorB!, ...head.anchorB!.subarray(1)]),\n anchorT: Float32Array.from([...tail.anchorT!, ...head.anchorT!.subarray(1)]),\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import { polylineIntoMetric as e, polylineOutOfMetric as t, sameMetric as n } from "./metric.js";
2
+ import { trimPolyline as r } from "@weasel-js/geom/tessellate";
3
+ //#region src/features/paths/tessellate/insets.ts
4
+ var i = (e, t) => e === void 0 ? {
5
+ length: 0,
6
+ metric: t
7
+ } : typeof e == "number" ? {
8
+ length: e,
9
+ metric: t
10
+ } : e;
11
+ function a(a, o, s, c, l) {
12
+ let u = i(c, s), d = i(l, s), f = a, p = o, m = (r) => {
13
+ f === null || n(p, r) || (p !== null && t(f, p), r !== null && e(f, r), p = r);
14
+ };
15
+ if (u.length > 0 || d.length > 0) {
16
+ if (n(u.metric, d.metric)) m(u.metric), f = r(f, u.length, d.length);
17
+ else for (let [e, t, n] of [[
18
+ u.length,
19
+ u.metric,
20
+ !0
21
+ ], [
22
+ d.length,
23
+ d.metric,
24
+ !1
25
+ ]]) e > 0 && f !== null && (m(t), f = r(f, n ? e : 0, n ? 0 : e));
26
+ }
27
+ return m(s), f;
28
+ }
29
+ //#endregion
30
+ export { a as trimInSpace };
31
+
32
+ //# sourceMappingURL=insets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insets.js","names":["trimOf","inset","ribbon","trimInSpace","s","start","e","end","cur","pl","space","from","moveTo","sameMetric","to","polylineOutOfMetric","polylineIntoMetric","trimPolyline","len","metric","atStart"],"sources":["../../../../src/features/paths/tessellate/insets.ts"],"sourcesContent":["/**\n * Trimming a stroke's ends where each inset is measured.\n *\n * A head stops the line by a length in the head's own space, which need not be\n * the ribbon's: a world-sized head on a `{ px }` stroke stops it a world\n * distance short, a `{ px }` head on a world-width stroke a screen distance.\n * Under a stretching view one length means different distances in the two\n * spaces depending on which way the line runs, so each end is trimmed in its\n * own space rather than converted.\n */\nimport { type Polyline, trimPolyline } from '@weasel-js/geom/tessellate';\nimport {\n type StrokeMetric, polylineIntoMetric, polylineOutOfMetric, sameMetric,\n} from './metric';\n\n/** A length along the line measured in `metric`'s space, or in world where\n * `metric` is `null`. */\nexport interface MeasuredLength {\n readonly length: number;\n readonly metric: StrokeMetric | null;\n}\n\n/** A plain number is a length in the ribbon's own space. */\nexport type InsetLength = number | MeasuredLength;\n\ninterface Trim {\n length: number;\n metric: StrokeMetric | null;\n}\n\nconst trimOf = (inset: InsetLength | undefined, ribbon: StrokeMetric | null): Trim =>\n inset === undefined ? { length: 0, metric: ribbon }\n : typeof inset === 'number' ? { length: inset, metric: ribbon }\n : inset;\n\n/**\n * `pl`, whose points are in `from`'s space, trimmed by `start` and `end` and\n * carried into `ribbon`'s — `null` for world in both. `pl` may be mapped in\n * place. Returns `null` when the insets consume the whole run.\n */\nexport function trimInSpace(\n pl: Polyline,\n from: StrokeMetric | null,\n ribbon: StrokeMetric | null,\n start: InsetLength | undefined,\n end: InsetLength | undefined,\n): Polyline | null {\n const s = trimOf(start, ribbon);\n const e = trimOf(end, ribbon);\n let cur: Polyline | null = pl;\n let space = from;\n const moveTo = (to: StrokeMetric | null) => {\n if (cur === null || sameMetric(space, to)) return;\n if (space !== null) polylineOutOfMetric(cur, space);\n if (to !== null) polylineIntoMetric(cur, to);\n space = to;\n };\n if (s.length > 0 || e.length > 0) {\n if (sameMetric(s.metric, e.metric)) {\n moveTo(s.metric);\n cur = trimPolyline(cur, s.length, e.length);\n } else {\n for (const [len, metric, atStart] of [[s.length, s.metric, true], [e.length, e.metric, false]] as const) {\n if (!(len > 0) || cur === null) continue;\n moveTo(metric);\n cur = trimPolyline(cur, atStart ? len : 0, atStart ? 0 : len);\n }\n }\n }\n moveTo(ribbon);\n return cur;\n}\n"],"mappings":";;;AA8BA,IAAMA,KAAU,GAAgC,MAC9CC,MAAU,KAAA,IAAY;CAAE,QAAQ;CAAG,QAAQC;AAAO,IAC9C,OAAOD,KAAU,WAAW;CAAE,QAAQA;CAAO,QAAQC;AAAO,IAC1DD;AAOR,SAAgBE,EACd,GACA,GACA,GACA,GACA,GACiB;CACjB,IAAMC,IAAIJ,EAAOK,GAAOH,CAAM,GACxBI,IAAIN,EAAOO,GAAKL,CAAM,GACxBM,IAAuBC,GACvBC,IAAQC,GACNC,KAAU,MAA4B;EAC1C,AAAIJ,MAAQ,QAAQK,EAAWH,GAAOI,CAAE,MACpCJ,MAAU,QAAMK,EAAoBP,GAAKE,CAAK,GAC9CI,MAAO,QAAME,EAAmBR,GAAKM,CAAE,GAC3C,IAAQA;CACV;CACA,IAAIV,EAAE,SAAS,KAAKE,EAAE,SAAS,GAAG;EAChC,IAAIO,EAAWT,EAAE,QAAQE,EAAE,MAAM,GAE/B,AADAM,EAAOR,EAAE,MAAM,GACf,IAAMa,EAAaT,GAAKJ,EAAE,QAAQE,EAAE,MAAM;OAE1C,KAAK,IAAM,CAACY,GAAKC,GAAQC,MAAY,CAAC;GAAChB,EAAE;GAAQA,EAAE;GAAQ;EAAI,GAAG;GAACE,EAAE;GAAQA,EAAE;GAAQ;EAAK,CAAC,GAC3F,AAAMY,IAAM,KAAMV,MAAQ,SAC1BI,EAAOO,CAAM,GACb,IAAMF,EAAaT,GAAKY,IAAUF,IAAM,GAAGE,IAAU,IAAIF,CAAG;CAGlE;CAEA,OADAN,EAAOV,CAAM,GACNM;AACT"}
@@ -0,0 +1,72 @@
1
+ //#region src/features/paths/tessellate/metric.ts
2
+ function e(e) {
3
+ return e === void 0 ? "" : `${r(e.metric)}@${e.pxPerUnit}`;
4
+ }
5
+ var t = 1e-9;
6
+ function n(e, n, r, i) {
7
+ let a = Math.abs(e * i - n * r), o = Math.sqrt(a);
8
+ if (!(a > 0) || !Number.isFinite(a)) return {
9
+ scale: 0,
10
+ metric: null
11
+ };
12
+ let s = (e * e + n * n) / a, c = (e * r + n * i) / a, l = (r * r + i * i) / a, u = Math.sqrt(s + l + 2), d = (s + 1) / u, f = c / u, p = (l + 1) / u;
13
+ return Math.abs(f) < t && Math.abs(d - p) < t ? {
14
+ scale: o,
15
+ metric: null
16
+ } : {
17
+ scale: o,
18
+ metric: {
19
+ p: d,
20
+ q: f,
21
+ r: p
22
+ }
23
+ };
24
+ }
25
+ function r(e) {
26
+ return e === void 0 ? "" : `${e.p},${e.q},${e.r}`;
27
+ }
28
+ function i(e, t) {
29
+ let n = e.points;
30
+ for (let e = 0; e < n.length; e += 2) {
31
+ let r = n[e], i = n[e + 1];
32
+ n[e] = t.p * r + t.q * i, n[e + 1] = t.q * r + t.r * i;
33
+ }
34
+ }
35
+ function a(e, t) {
36
+ let n = e.points;
37
+ for (let e = 0; e < n.length; e += 2) {
38
+ let r = n[e], i = n[e + 1];
39
+ n[e] = t.r * r - t.q * i, n[e + 1] = t.p * i - t.q * r;
40
+ }
41
+ }
42
+ function o(e, t) {
43
+ return e === t || e !== null && t !== null && e.p === t.p && e.q === t.q && e.r === t.r;
44
+ }
45
+ function s(e, t, n) {
46
+ return Math.hypot(e.r * t - e.q * n, e.p * n - e.q * t);
47
+ }
48
+ function c(e, t, n) {
49
+ return 1 / Math.hypot(e.p * t + e.q * n, e.q * t + e.r * n);
50
+ }
51
+ function l(e, t) {
52
+ for (let n = 0; n < e.length; n += 2) {
53
+ let r = e[n], i = e[n + 1];
54
+ e[n] = t.r * r - t.q * i, e[n + 1] = t.p * i - t.q * r;
55
+ }
56
+ }
57
+ function u(e) {
58
+ return e.q === 0;
59
+ }
60
+ function d(e, t) {
61
+ return u(t) ? {
62
+ kind: "rect",
63
+ x: t.p * e.x,
64
+ y: t.r * e.y,
65
+ width: t.p * e.width,
66
+ height: t.r * e.height
67
+ } : null;
68
+ }
69
+ //#endregion
70
+ export { l as coordsOutOfMetric, u as metricKeepsRects, r as metricKey, i as polylineIntoMetric, a as polylineOutOfMetric, d as rectIntoMetric, o as sameMetric, e as screenSpaceKey, n as strokeSpaceOf, s as worldWidthInMetric, c as worldWidthInMetricAlongWorld };
71
+
72
+ //# sourceMappingURL=metric.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metric.js","names":["screenSpaceKey","space","metricKey","CONFORMAL_EPSILON","strokeSpaceOf","det2","a","d","b","c","scale","g11","g12","g22","s","p","q","r","metric","polylineIntoMetric","pts","pl","i","x","y","m","polylineOutOfMetric","sameMetric","worldWidthInMetric","tx","ty","worldWidthInMetricAlongWorld","ux","uy","coordsOutOfMetric","coords","metricKeepsRects","rectIntoMetric","rect"],"sources":["../../../../src/features/paths/tessellate/metric.ts"],"sourcesContent":["/**\n * The space a screen-pixel stroke is built in.\n *\n * A `{ px }` stroke is exact on screen only when its ribbon is offset in a\n * space that measures lengths the way the screen does. The transform's linear\n * part is `scale · R · P`: a uniform scale, a rotation (or reflection) that\n * moves no lengths, and `P`, the symmetric unit-determinant stretch left over.\n * Offsetting the ribbon after `P` and mapping it back through `P⁻¹` gives a\n * world-space mesh that the full transform turns into a ribbon of exactly\n * `width · scale` pixels in every direction — and keeps the mesh drawable by\n * every path that draws a world-space ribbon.\n *\n * `P` depends only on the lengths the transform induces (`LᵀL`), not on any\n * rotation applied after it, so a view that only pans or zooms uniformly keeps\n * the same metric and the ribbon cache keeps hitting.\n */\nimport type { Polyline } from '@weasel-js/geom/tessellate';\n\n/** `P` = [[p, q], [q, r]], with `pr − q² = 1`. */\nexport interface StrokeMetric {\n readonly p: number;\n readonly q: number;\n readonly r: number;\n}\n\n/** A space a screen-sized part of a stroke is built in: the metric, and how\n * many screen pixels one of its units is. */\nexport interface ScreenSpace {\n readonly metric: StrokeMetric;\n readonly pxPerUnit: number;\n}\n\n/**\n * Where each part of a stroke is built. An absent part is built in world:\n * everything is under a similarity, and otherwise a part whose size is a world\n * length. The ribbon is screen-sized for a `{ px }` width; a head for a\n * `{ px }` size, or for no size on a screen-sized ribbon, since it then takes\n * the stroke width.\n */\nexport interface StrokeSpaces {\n readonly ribbon?: ScreenSpace;\n readonly start?: ScreenSpace;\n readonly mid?: ScreenSpace;\n readonly end?: ScreenSpace;\n}\n\n/** A cache-key fragment naming `space`; empty for world. */\nexport function screenSpaceKey(space: ScreenSpace | undefined): string {\n return space === undefined ? '' : `${metricKey(space.metric)}@${space.pxPerUnit}`;\n}\n\n/** Below this, a stretch is a rounding error and the transform a similarity. */\nconst CONFORMAL_EPSILON = 1e-9;\n\n/**\n * Split the linear map `x' = a·x + c·y, y' = b·x + d·y` — a GL `mat3`'s\n * `m[0], m[1], m[3], m[4]` — into its geometric-mean scale and its stroke\n * metric. `metric` is `null` when the map is a similarity, where world space\n * already measures like the screen; `scale` is `0` for a singular map.\n */\nexport function strokeSpaceOf(\n a: number, b: number, c: number, d: number,\n): { scale: number; metric: StrokeMetric | null } {\n const det2 = Math.abs(a * d - b * c);\n const scale = Math.sqrt(det2);\n if (!(det2 > 0) || !Number.isFinite(det2)) return { scale: 0, metric: null };\n // G = LᵀL / det, whose determinant is 1, so √G = (G + I) / √(tr G + 2).\n const g11 = (a * a + b * b) / det2;\n const g12 = (a * c + b * d) / det2;\n const g22 = (c * c + d * d) / det2;\n const s = Math.sqrt(g11 + g22 + 2);\n const p = (g11 + 1) / s;\n const q = g12 / s;\n const r = (g22 + 1) / s;\n if (Math.abs(q) < CONFORMAL_EPSILON && Math.abs(p - r) < CONFORMAL_EPSILON) {\n return { scale, metric: null };\n }\n return { scale, metric: { p, q, r } };\n}\n\n/** A cache-key fragment naming `metric`; empty for none. */\nexport function metricKey(metric: StrokeMetric | undefined): string {\n return metric === undefined ? '' : `${metric.p},${metric.q},${metric.r}`;\n}\n\n/** Map `pl`'s points through `P`, in place. */\nexport function polylineIntoMetric(pl: Polyline, m: StrokeMetric): void {\n const pts = pl.points;\n for (let i = 0; i < pts.length; i += 2) {\n const x = pts[i], y = pts[i + 1];\n pts[i] = m.p * x + m.q * y;\n pts[i + 1] = m.q * x + m.r * y;\n }\n}\n\n/** Map `pl`'s points back out through `P⁻¹`, in place. */\nexport function polylineOutOfMetric(pl: Polyline, m: StrokeMetric): void {\n const pts = pl.points;\n for (let i = 0; i < pts.length; i += 2) {\n const x = pts[i], y = pts[i + 1];\n pts[i] = m.r * x - m.q * y;\n pts[i + 1] = m.p * y - m.q * x;\n }\n}\n\n/** Whether two spaces are the same — world being `null`. */\nexport function sameMetric(a: StrokeMetric | null, b: StrokeMetric | null): boolean {\n if (a === b) return true;\n return a !== null && b !== null && a.p === b.p && a.q === b.q && a.r === b.r;\n}\n\n/**\n * What one world unit of width, across a line running along the unit\n * direction `(tx, ty)` of the metric's own space, measures in that space.\n * `P` keeps areas, so a strip keeps its area while its length stretches by\n * `|P u|` (`u` the line's world direction): its width shrinks by the same,\n * and `1 / |P u|` is `|P⁻¹ t|`.\n */\nexport function worldWidthInMetric(m: StrokeMetric, tx: number, ty: number): number {\n return Math.hypot(m.r * tx - m.q * ty, m.p * ty - m.q * tx);\n}\n\n/** The same, for a line running along the unit world direction `(ux, uy)`. */\nexport function worldWidthInMetricAlongWorld(m: StrokeMetric, ux: number, uy: number): number {\n return 1 / Math.hypot(m.p * ux + m.q * uy, m.q * ux + m.r * uy);\n}\n\n/** Map interleaved x,y pairs back out through `P⁻¹` = [[r, −q], [−q, p]], in\n * place. */\nexport function coordsOutOfMetric(coords: Float32Array, m: StrokeMetric): void {\n for (let i = 0; i < coords.length; i += 2) {\n const x = coords[i], y = coords[i + 1];\n coords[i] = m.r * x - m.q * y;\n coords[i + 1] = m.p * y - m.q * x;\n }\n}\n\n/** Whether `P` takes an axis-aligned rect to another one. */\nexport function metricKeepsRects(m: StrokeMetric): boolean {\n return m.q === 0;\n}\n\n/** `rect` in the metric's space, while `P` keeps it axis-aligned; otherwise\n * `null`, and the rect has to be carried as a polygon. */\nexport function rectIntoMetric(\n rect: { x: number; y: number; width: number; height: number },\n m: StrokeMetric,\n): { kind: 'rect'; x: number; y: number; width: number; height: number } | null {\n if (!metricKeepsRects(m)) return null;\n return { kind: 'rect', x: m.p * rect.x, y: m.r * rect.y, width: m.p * rect.width, height: m.r * rect.height };\n}\n"],"mappings":"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"}
@@ -1,163 +1,103 @@
1
- import { alignedStrokeRect as e, resolveScreenLength as t } from "@weasel-js/paint";
2
- import { extractPolylines as n, trimPolyline as r } from "@weasel-js/geom/tessellate";
1
+ import { splitForDash as e } from "./dash.js";
2
+ import { populatePolylineWidths as t } from "./vertexWidths.js";
3
+ import { coordsOutOfMetric as n, rectIntoMetric as r, worldWidthInMetric as i, worldWidthInMetricAlongWorld as a } from "./metric.js";
4
+ import { trimInSpace as o } from "./insets.js";
5
+ import { alignedStrokeRect as s, resolveScreenLength as c } from "@weasel-js/paint";
6
+ import { extractPolylines as l } from "@weasel-js/geom/tessellate";
3
7
  //#region src/features/paths/tessellate/stroke.ts
4
- var i = {
8
+ var u = {
5
9
  vertices: /* @__PURE__ */ new Float32Array(),
6
10
  indices: /* @__PURE__ */ new Uint32Array(),
7
11
  anchorA: /* @__PURE__ */ new Uint32Array(),
8
12
  anchorB: /* @__PURE__ */ new Uint32Array(),
9
13
  anchorT: /* @__PURE__ */ new Float32Array()
10
14
  };
11
- function a(e, n) {
12
- return t(e, n);
15
+ function d(e, t) {
16
+ return c(e, t);
13
17
  }
14
- function o(t, o, l = {}) {
15
- let u = a(o.width ?? 1, 1);
16
- if (u <= 0) return i;
17
- let f = o.join ?? "miter", p = o.cap ?? "butt", g = o.align ?? "center", _ = o.miterLimit ?? m, v = o.varyingWidthJoinThreshold ?? h, y = t;
18
- if (t.kind === "rect" && g !== "center") {
19
- let n = e(t, g, u);
20
- y = {
18
+ function f(c, f, m = {}) {
19
+ let h = d(f.width ?? 1, 1);
20
+ if (h <= 0) return u;
21
+ let g = f.join ?? "miter", y = f.cap ?? "butt", b = f.align ?? "center", x = f.miterLimit ?? _, S = f.varyingWidthJoinThreshold ?? v, C = m.metric, w = C && c.kind === "rect" ? r(c, C) : null, T = w ?? c;
22
+ if (T.kind === "rect" && b !== "center") {
23
+ let e = s(T, b, h);
24
+ T = {
21
25
  kind: "rect",
22
- x: n.x,
23
- y: n.y,
24
- width: n.width,
25
- height: n.height
26
+ x: e.x,
27
+ y: e.y,
28
+ width: e.width,
29
+ height: e.height
26
30
  };
27
31
  }
28
- let b = n(y, l);
29
- if (o.vertexWidths && o.vertexWidths.length > 0) for (let e of b) c(e, o.vertexWidths, u);
30
- let x = o.dash ?? [], S = [], C = [], w = [], T = [], E = [], D = l.startInset ?? 0, O = l.endInset ?? 0;
31
- for (let e of b) {
32
- let t = D > 0 || O > 0 ? r(e, D, O) : e;
33
- if (t === null) continue;
34
- let n = x.length > 0 ? d(t, x) : [t];
35
- for (let e of n) s(e, u, f, p, _, v, S, C, w, T, E);
32
+ let E = l(T, m), D = f.vertexWidths && f.vertexWidths.length > 0 ? f.vertexWidths : null, O = C ? w ? (e, t) => i(C, e, t) : (e, t) => a(C, e, t) : void 0, k = f.dash ?? [], A = [], j = [], M = [], N = [], P = [], F = C ?? null;
33
+ for (let n of E) {
34
+ D && t(n, D, h, O);
35
+ let r = o(n, w ? F : null, F, m.startInset, m.endInset);
36
+ if (r === null) continue;
37
+ let i = k.length > 0 ? e(r, k) : [r];
38
+ for (let e of i) p(e, h, g, y, x, S, A, j, M, N, P);
36
39
  }
37
- return {
38
- vertices: new Float32Array(S),
39
- indices: new Uint32Array(C),
40
- anchorA: new Uint32Array(w),
41
- anchorB: new Uint32Array(T),
42
- anchorT: new Float32Array(E)
40
+ let I = new Float32Array(A);
41
+ return C && n(I, C), {
42
+ vertices: I,
43
+ indices: new Uint32Array(j),
44
+ anchorA: new Uint32Array(M),
45
+ anchorB: new Uint32Array(N),
46
+ anchorT: new Float32Array(P)
43
47
  };
44
48
  }
45
- function s(e, t, n, r, i, a, o, s, c, l, d) {
46
- let f = t / 2, m = e.points, h = m.length / 2, _ = h - 1;
47
- if (_ < 1) return;
48
- let v = e.anchorA ?? new Uint32Array(h), y = e.anchorB ?? new Uint32Array(h), b = e.anchorT ?? new Float32Array(h), x = e.widths ? e.widths.map((e) => e / 2) : null, S = [], C = [];
49
- for (let e = 0; e < _; e++) {
50
- let t = x ? x[e] : f, n = x ? x[e + 1] : f, r = p(m[e * 2], m[e * 2 + 1], m[(e + 1) * 2], m[(e + 1) * 2 + 1], t, n);
49
+ function p(e, t, n, r, i, a, o, s, c, l, u) {
50
+ let d = t / 2, f = e.points, p = f.length / 2, m = p - 1;
51
+ if (m < 1) return;
52
+ let _ = e.anchorA ?? new Uint32Array(p), v = e.anchorB ?? new Uint32Array(p), b = e.anchorT ?? new Float32Array(p), x = e.widths ? e.widths.map((e) => e / 2) : null, S = [], C = [];
53
+ for (let e = 0; e < m; e++) {
54
+ let t = x ? x[e] : d, n = x ? x[e + 1] : d, r = g(f[e * 2], f[e * 2 + 1], f[(e + 1) * 2], f[(e + 1) * 2 + 1], t, n);
51
55
  r && (S.push(r), C.push(e));
52
56
  }
53
57
  let w = -1;
54
58
  if (e.closed && S.length >= 1) {
55
- let e = S[S.length - 1], t = S[0], n = x ? x[h - 1] : f, r = x ? x[0] : f, i = p(e.bx, e.by, t.ax, t.ay, n, r);
56
- i && (S.push(i), w = h - 1, C.push(w));
59
+ let e = S[S.length - 1], t = S[0], n = x ? x[p - 1] : d, r = x ? x[0] : d, i = g(e.bx, e.by, t.ax, t.ay, n, r);
60
+ i && (S.push(i), w = p - 1, C.push(w));
57
61
  }
58
62
  if (S.length === 0) return;
59
63
  let T = [];
60
64
  for (let t = 0; t < S.length; t++) {
61
65
  let n = S[t], r = C[t], i = t === S.length - 1 && e.closed && w >= 0 ? 0 : r + 1, a = o.length / 2;
62
66
  T.push(a);
63
- let u = n.nxUnit * n.halfA, f = n.nyUnit * n.halfA, p = n.nxUnit * n.halfB, m = n.nyUnit * n.halfB;
64
- o.push(n.ax + u, n.ay + f), c.push(v[r]), l.push(y[r]), d.push(b[r]), o.push(n.ax - u, n.ay - f), c.push(v[r]), l.push(y[r]), d.push(b[r]), o.push(n.bx + p, n.by + m), c.push(v[i]), l.push(y[i]), d.push(b[i]), o.push(n.bx - p, n.by - m), c.push(v[i]), l.push(y[i]), d.push(b[i]), s.push(a, a + 1, a + 2, a + 1, a + 3, a + 2);
67
+ let d = n.nxUnit * n.halfA, f = n.nyUnit * n.halfA, p = n.nxUnit * n.halfB, m = n.nyUnit * n.halfB;
68
+ o.push(n.ax + d, n.ay + f), c.push(_[r]), l.push(v[r]), u.push(b[r]), o.push(n.ax - d, n.ay - f), c.push(_[r]), l.push(v[r]), u.push(b[r]), o.push(n.bx + p, n.by + m), c.push(_[i]), l.push(v[i]), u.push(b[i]), o.push(n.bx - p, n.by - m), c.push(_[i]), l.push(v[i]), u.push(b[i]), s.push(a, a + 1, a + 2, a + 1, a + 3, a + 2);
65
69
  }
66
70
  let E = e.closed ? S.length : S.length - 1;
67
- for (let e = 0; e < E; e++) g(S, T, e, n, i, a, o, s, c, l, d, C, v, y, b);
71
+ for (let e = 0; e < E; e++) y(S, T, e, n, i, a, o, s, c, l, u, C, _, v, b);
68
72
  if (!e.closed && r !== "butt") {
69
73
  let e = S[0], t = T[0];
70
- u(e, t + 0, t + 1, !1, e.halfA, r, o, s, c, l, d, v[0], y[0], b[0]);
74
+ h(e, t + 0, t + 1, !1, e.halfA, r, o, s, c, l, u, _[0], v[0], b[0]);
71
75
  let n = S[S.length - 1], i = T[S.length - 1], a = C[S.length - 1] + 1;
72
- u(n, i + 2, i + 3, !0, n.halfB, r, o, s, c, l, d, v[a], y[a], b[a]);
73
- }
74
- }
75
- function c(e, t, n) {
76
- let r = e.points.length / 2;
77
- if (r === 0) return;
78
- let i = e.anchorA ?? new Uint32Array(r), a = e.anchorB ?? new Uint32Array(r), o = e.anchorT ?? new Float32Array(r), s = new Float32Array(r), c = (e) => {
79
- let r = t[e];
80
- return typeof r == "number" && isFinite(r) && r > 0 ? r : n;
81
- };
82
- for (let e = 0; e < r; e++) {
83
- let t = i[e], n = a[e], r = o[e];
84
- if (t === n) s[e] = c(t);
85
- else {
86
- let i = c(t), a = c(n);
87
- s[e] = i + (a - i) * r;
88
- }
76
+ h(n, i + 2, i + 3, !0, n.halfB, r, o, s, c, l, u, _[a], v[a], b[a]);
89
77
  }
90
- e.widths = s;
91
78
  }
92
- var l = 10 * Math.PI / 180;
93
- function u(e, t, n, r, i, a, o, s, c, u, d, f, p, m) {
79
+ var m = 10 * Math.PI / 180;
80
+ function h(e, t, n, r, i, a, o, s, c, l, u, d, f, p) {
94
81
  let h = r ? 1 : -1, g = (e.bx - e.ax) / e.len * h, _ = (e.by - e.ay) / e.len * h, v = r ? e.bx : e.ax, y = r ? e.by : e.ay, b = e.nxUnit * i, x = e.nyUnit * i;
95
82
  if (a === "square") {
96
- let e = v + g * i, a = y + _ * i, l = o.length / 2;
97
- o.push(e + b, a + x), c.push(f), u.push(p), d.push(m);
83
+ let e = v + g * i, a = y + _ * i, m = o.length / 2;
84
+ o.push(e + b, a + x), c.push(d), l.push(f), u.push(p);
98
85
  let h = o.length / 2;
99
- o.push(e - b, a - x), c.push(f), u.push(p), d.push(m), r ? s.push(t, l, h, t, h, n) : s.push(t, h, l, t, n, h);
86
+ o.push(e - b, a - x), c.push(d), l.push(f), u.push(p), r ? s.push(t, m, h, t, h, n) : s.push(t, h, m, t, n, h);
100
87
  return;
101
88
  }
102
89
  if (a === "round") {
103
90
  let e = o.length / 2;
104
- o.push(v, y), c.push(f), u.push(p), d.push(m);
105
- let a = Math.atan2(x, b), h = r ? -Math.PI : Math.PI, g = Math.max(1, Math.ceil(Math.abs(h) / l)), _ = h / g, S = t;
91
+ o.push(v, y), c.push(d), l.push(f), u.push(p);
92
+ let a = Math.atan2(x, b), h = r ? -Math.PI : Math.PI, g = Math.max(1, Math.ceil(Math.abs(h) / m)), _ = h / g, S = t;
106
93
  for (let t = 1; t < g; t++) {
107
- let n = a + t * _, l = v + Math.cos(n) * i, h = y + Math.sin(n) * i, g = o.length / 2;
108
- o.push(l, h), c.push(f), u.push(p), d.push(m), r ? s.push(S, g, e) : s.push(S, e, g), S = g;
94
+ let n = a + t * _, m = v + Math.cos(n) * i, h = y + Math.sin(n) * i, g = o.length / 2;
95
+ o.push(m, h), c.push(d), l.push(f), u.push(p), r ? s.push(S, g, e) : s.push(S, e, g), S = g;
109
96
  }
110
97
  r ? s.push(S, n, e) : s.push(S, e, n);
111
98
  }
112
99
  }
113
- function d(e, t) {
114
- let n = [], r = e.anchorA ?? new Uint32Array(e.points.length / 2), i = e.anchorB ?? new Uint32Array(e.points.length / 2), a = e.anchorT ?? new Float32Array(e.points.length / 2), o = 0, s = t[0], c = !0, l = c ? [e.points[0], e.points[1]] : null, u = c ? [r[0]] : null, d = c ? [i[0]] : null, p = c ? [a[0]] : null, m = () => {
115
- l && l.length >= 4 && n.push({
116
- points: l,
117
- closed: !1,
118
- anchorA: new Uint32Array(u),
119
- anchorB: new Uint32Array(d),
120
- anchorT: new Float32Array(p)
121
- }), l = null, u = null, d = null, p = null, o = (o + 1) % t.length, s = t[o], c = !c;
122
- }, h = e.points[0], g = e.points[1], _ = e.points.length / 2, v = e.closed ? _ : _ - 1;
123
- for (let t = 0; t < v; t++) {
124
- let n = (t + 1) % _, o = e.points[n * 2], f = e.points[n * 2 + 1], v = r[t], y = i[t], b = a[t], x = r[n], S = i[n], C = a[n], w = o - h, T = f - g, E = Math.hypot(w, T), D = w, O = T, k = E, A = 0;
125
- for (; k > 1e-9;) if (k <= s) c && l && (l.push(o, f), u.push(x), d.push(S), p.push(C)), s -= k, h = o, g = f, A = E, k = 0, s <= 1e-9 && (m(), c && (l = [h, g], u = [x], d = [S], p = [C]));
126
- else {
127
- let e = s / k, t = h + D * e, n = g + O * e;
128
- A += s;
129
- let r = A / E, i, a, _;
130
- v === x && y === S ? (i = v, a = y, _ = b + (C - b) * r) : r < .5 ? (i = v, a = y, _ = b) : (i = x, a = S, _ = C), c && l && (l.push(t, n), u.push(i), d.push(a), p.push(_)), h = t, g = n, D = o - h, O = f - g, k = Math.hypot(D, O), m(), c && (l = [h, g], u = [i], d = [a], p = [_]);
131
- }
132
- }
133
- if (l && l.length >= 4) {
134
- let t = {
135
- points: l,
136
- closed: !1,
137
- anchorA: new Uint32Array(u),
138
- anchorB: new Uint32Array(d),
139
- anchorT: new Float32Array(p)
140
- }, r = l[l.length - 2], i = l[l.length - 1], a = (e, t) => Math.hypot(r - e, i - t) <= 1e-9;
141
- e.closed && n.length > 0 && a(n[0].points[0], n[0].points[1]) ? n[0] = f(t, n[0]) : e.closed && n.length === 0 && a(l[0], l[1]) ? n.push({
142
- points: l.slice(0, -2),
143
- closed: !0,
144
- anchorA: t.anchorA.slice(0, -1),
145
- anchorB: t.anchorB.slice(0, -1),
146
- anchorT: t.anchorT.slice(0, -1)
147
- }) : n.push(t);
148
- }
149
- return n;
150
- }
151
- function f(e, t) {
152
- return {
153
- points: [...e.points, ...t.points.slice(2)],
154
- closed: !1,
155
- anchorA: Uint32Array.from([...e.anchorA, ...t.anchorA.subarray(1)]),
156
- anchorB: Uint32Array.from([...e.anchorB, ...t.anchorB.subarray(1)]),
157
- anchorT: Float32Array.from([...e.anchorT, ...t.anchorT.subarray(1)])
158
- };
159
- }
160
- function p(e, t, n, r, i, a) {
100
+ function g(e, t, n, r, i, a) {
161
101
  let o = n - e, s = r - t, c = Math.hypot(o, s);
162
102
  return c === 0 ? null : {
163
103
  ax: e,
@@ -171,23 +111,23 @@ function p(e, t, n, r, i, a) {
171
111
  len: c
172
112
  };
173
113
  }
174
- var m = 4, h = 1.5;
175
- function g(e, t, n, r, i, a, o, s, c, l, u, d, f, p, m) {
176
- let h = e[n], g = e[(n + 1) % e.length], _ = t[n], x = t[(n + 1) % e.length], S = h.bx - h.ax, C = h.by - h.ay, w = g.bx - g.ax, T = g.by - g.ay, E = S * T - C * w;
114
+ var _ = 4, v = 1.5;
115
+ function y(e, t, n, r, i, a, o, s, c, l, u, d, f, p, m) {
116
+ let h = e[n], g = e[(n + 1) % e.length], _ = t[n], v = t[(n + 1) % e.length], y = h.bx - h.ax, b = h.by - h.ay, w = g.bx - g.ax, T = g.by - g.ay, E = y * T - b * w;
177
117
  if (E === 0) return;
178
- let D = E > 0, O = D ? _ + 3 : _ + 2, k = D ? x + 1 : x + 0, A = d[(n + 1) % e.length], j = f[A], M = p[A], N = m[A], P = h.halfB, F = Math.max(h.halfA, h.halfB) / Math.max(1e-9, Math.min(h.halfA, h.halfB)), I = Math.max(g.halfA, g.halfB) / Math.max(1e-9, Math.min(g.halfA, g.halfB));
118
+ let D = E > 0, O = D ? _ + 3 : _ + 2, k = D ? v + 1 : v + 0, A = d[(n + 1) % e.length], j = f[A], M = p[A], N = m[A], P = h.halfB, F = Math.max(h.halfA, h.halfB) / Math.max(1e-9, Math.min(h.halfA, h.halfB)), I = Math.max(g.halfA, g.halfB) / Math.max(1e-9, Math.min(g.halfA, g.halfB));
179
119
  if (r === "bevel" || F > a || I > a) {
180
- y(h, O, k, D, o, s, c, l, u, j, M, N);
120
+ S(h, O, k, D, o, s, c, l, u, j, M, N);
181
121
  return;
182
122
  }
183
123
  if (r === "miter") {
184
- let e = o[O * 2], t = o[O * 2 + 1], n = o[k * 2], r = o[k * 2 + 1], a = b(e, t, S, C, n, r, -w, -T);
124
+ let e = o[O * 2], t = o[O * 2 + 1], n = o[k * 2], r = o[k * 2 + 1], a = C(e, t, y, b, n, r, -w, -T);
185
125
  if (!a) {
186
- y(h, O, k, D, o, s, c, l, u, j, M, N);
126
+ S(h, O, k, D, o, s, c, l, u, j, M, N);
187
127
  return;
188
128
  }
189
129
  if (Math.hypot(a[0] - h.bx, a[1] - h.by) > i * P) {
190
- y(h, O, k, D, o, s, c, l, u, j, M, N);
130
+ S(h, O, k, D, o, s, c, l, u, j, M, N);
191
131
  return;
192
132
  }
193
133
  let d = o.length / 2;
@@ -197,34 +137,34 @@ function g(e, t, n, r, i, a, o, s, c, l, u, d, f, p, m) {
197
137
  return;
198
138
  }
199
139
  if (r === "round") {
200
- v(h, O, k, D, o, s, c, l, u, j, M, N);
140
+ x(h, O, k, D, o, s, c, l, u, j, M, N);
201
141
  return;
202
142
  }
203
143
  }
204
- var _ = 10 * Math.PI / 180;
205
- function v(e, t, n, r, i, a, o, s, c, l, u, d) {
144
+ var b = 10 * Math.PI / 180;
145
+ function x(e, t, n, r, i, a, o, s, c, l, u, d) {
206
146
  let f = e.bx, p = e.by, m = i.length / 2;
207
147
  i.push(f, p), o.push(l), s.push(u), c.push(d);
208
- let h = i[t * 2] - f, g = i[t * 2 + 1] - p, v = i[n * 2] - f, y = i[n * 2 + 1] - p, b = Math.hypot(h, g), x = Math.atan2(g, h), S = Math.atan2(y, v) - x;
148
+ let h = i[t * 2] - f, g = i[t * 2 + 1] - p, _ = i[n * 2] - f, v = i[n * 2 + 1] - p, y = Math.hypot(h, g), x = Math.atan2(g, h), S = Math.atan2(v, _) - x;
209
149
  r && S < 0 ? S += 2 * Math.PI : !r && S > 0 && (S -= 2 * Math.PI);
210
- let C = Math.max(1, Math.ceil(Math.abs(S) / _)), w = S / C, T = t;
150
+ let C = Math.max(1, Math.ceil(Math.abs(S) / b)), w = S / C, T = t;
211
151
  for (let e = 1; e < C; e++) {
212
- let t = x + e * w, n = f + Math.cos(t) * b, h = p + Math.sin(t) * b, g = i.length / 2;
152
+ let t = x + e * w, n = f + Math.cos(t) * y, h = p + Math.sin(t) * y, g = i.length / 2;
213
153
  i.push(n, h), o.push(l), s.push(u), c.push(d), r ? a.push(T, m, g) : a.push(T, g, m), T = g;
214
154
  }
215
155
  r ? a.push(T, m, n) : a.push(T, n, m);
216
156
  }
217
- function y(e, t, n, r, i, a, o, s, c, l, u, d) {
157
+ function S(e, t, n, r, i, a, o, s, c, l, u, d) {
218
158
  let f = i.length / 2;
219
159
  i.push(e.bx, e.by), o.push(l), s.push(u), c.push(d), r ? a.push(t, f, n) : a.push(t, n, f);
220
160
  }
221
- function b(e, t, n, r, i, a, o, s) {
161
+ function C(e, t, n, r, i, a, o, s) {
222
162
  let c = n * s - r * o;
223
163
  if (Math.abs(c) < 1e-9) return null;
224
164
  let l = ((i - e) * s - (a - t) * o) / c;
225
165
  return [e + l * n, t + l * r];
226
166
  }
227
167
  //#endregion
228
- export { a as resolveStrokeWidth, o as tessellateStroke };
168
+ export { d as resolveStrokeWidth, f as tessellateStroke };
229
169
 
230
170
  //# sourceMappingURL=stroke.js.map