abstract-3d 4.1.0 → 4.1.2

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 (184) hide show
  1. package/lib/renderers/svg/svg-encoding.d.ts +0 -2
  2. package/lib/renderers/svg/svg-encoding.d.ts.map +1 -1
  3. package/lib/renderers/svg/svg-encoding.js +0 -12
  4. package/lib/renderers/svg/svg-encoding.js.map +1 -1
  5. package/lib/renderers/svg/svg-geometries/shared.d.ts +2 -19
  6. package/lib/renderers/svg/svg-geometries/shared.d.ts.map +1 -1
  7. package/lib/renderers/svg/svg-geometries/shared.js +1 -16
  8. package/lib/renderers/svg/svg-geometries/shared.js.map +1 -1
  9. package/lib/renderers/svg/svg-geometries/svg-box.d.ts.map +1 -1
  10. package/lib/renderers/svg/svg-geometries/svg-box.js +30 -44
  11. package/lib/renderers/svg/svg-geometries/svg-box.js.map +1 -1
  12. package/lib/renderers/svg/svg-geometries/svg-cone.d.ts.map +1 -1
  13. package/lib/renderers/svg/svg-geometries/svg-cone.js +3 -3
  14. package/lib/renderers/svg/svg-geometries/svg-cone.js.map +1 -1
  15. package/lib/renderers/svg/svg-geometries/svg-cylinder.d.ts.map +1 -1
  16. package/lib/renderers/svg/svg-geometries/svg-cylinder.js +6 -19
  17. package/lib/renderers/svg/svg-geometries/svg-cylinder.js.map +1 -1
  18. package/lib/renderers/svg/svg-geometries/svg-image.d.ts.map +1 -1
  19. package/lib/renderers/svg/svg-geometries/svg-image.js +2 -3
  20. package/lib/renderers/svg/svg-geometries/svg-image.js.map +1 -1
  21. package/lib/renderers/svg/svg-geometries/svg-line.d.ts.map +1 -1
  22. package/lib/renderers/svg/svg-geometries/svg-line.js +1 -3
  23. package/lib/renderers/svg/svg-geometries/svg-line.js.map +1 -1
  24. package/lib/renderers/svg/svg-geometries/svg-plane.d.ts.map +1 -1
  25. package/lib/renderers/svg/svg-geometries/svg-plane.js +2 -6
  26. package/lib/renderers/svg/svg-geometries/svg-plane.js.map +1 -1
  27. package/lib/renderers/svg/svg-geometries/svg-polygon.d.ts.map +1 -1
  28. package/lib/renderers/svg/svg-geometries/svg-polygon.js +2 -10
  29. package/lib/renderers/svg/svg-geometries/svg-polygon.js.map +1 -1
  30. package/lib/renderers/svg/svg-geometries/svg-shape.d.ts.map +1 -1
  31. package/lib/renderers/svg/svg-geometries/svg-shape.js +2 -5
  32. package/lib/renderers/svg/svg-geometries/svg-shape.js.map +1 -1
  33. package/lib/renderers/svg/svg-geometries/svg-text.js +1 -1
  34. package/lib/renderers/svg/svg-geometries/svg-text.js.map +1 -1
  35. package/lib/renderers/svg/svg-order.d.ts +5 -6
  36. package/lib/renderers/svg/svg-order.d.ts.map +1 -1
  37. package/lib/renderers/svg/svg-order.js +124 -233
  38. package/lib/renderers/svg/svg-order.js.map +1 -1
  39. package/lib/renderers/svg/svg.d.ts.map +1 -1
  40. package/lib/renderers/svg/svg.js +12 -11
  41. package/lib/renderers/svg/svg.js.map +1 -1
  42. package/lib/renderers/svg-bsp/class-names.d.ts +11 -0
  43. package/lib/renderers/svg-bsp/class-names.d.ts.map +1 -0
  44. package/lib/renderers/svg-bsp/class-names.js +11 -0
  45. package/lib/renderers/svg-bsp/class-names.js.map +1 -0
  46. package/lib/renderers/svg-bsp/index.d.ts +5 -0
  47. package/lib/renderers/svg-bsp/index.d.ts.map +1 -0
  48. package/lib/renderers/svg-bsp/index.js +4 -0
  49. package/lib/renderers/svg-bsp/index.js.map +1 -0
  50. package/lib/renderers/svg-bsp/svg-encoding.d.ts +19 -0
  51. package/lib/renderers/svg-bsp/svg-encoding.d.ts.map +1 -0
  52. package/lib/renderers/svg-bsp/svg-encoding.js +135 -0
  53. package/lib/renderers/svg-bsp/svg-encoding.js.map +1 -0
  54. package/lib/renderers/svg-bsp/svg-geometries/shared.d.ts +47 -0
  55. package/lib/renderers/svg-bsp/svg-geometries/shared.d.ts.map +1 -0
  56. package/lib/renderers/svg-bsp/svg-geometries/shared.js +45 -0
  57. package/lib/renderers/svg-bsp/svg-geometries/shared.js.map +1 -0
  58. package/lib/renderers/svg-bsp/svg-geometries/svg-box.d.ts +4 -0
  59. package/lib/renderers/svg-bsp/svg-geometries/svg-box.d.ts.map +1 -0
  60. package/lib/renderers/svg-bsp/svg-geometries/svg-box.js +58 -0
  61. package/lib/renderers/svg-bsp/svg-geometries/svg-box.js.map +1 -0
  62. package/lib/renderers/svg-bsp/svg-geometries/svg-cone.d.ts +4 -0
  63. package/lib/renderers/svg-bsp/svg-geometries/svg-cone.d.ts.map +1 -0
  64. package/lib/renderers/svg-bsp/svg-geometries/svg-cone.js +39 -0
  65. package/lib/renderers/svg-bsp/svg-geometries/svg-cone.js.map +1 -0
  66. package/lib/renderers/svg-bsp/svg-geometries/svg-cylinder.d.ts +4 -0
  67. package/lib/renderers/svg-bsp/svg-geometries/svg-cylinder.d.ts.map +1 -0
  68. package/lib/renderers/svg-bsp/svg-geometries/svg-cylinder.js +62 -0
  69. package/lib/renderers/svg-bsp/svg-geometries/svg-cylinder.js.map +1 -0
  70. package/lib/renderers/svg-bsp/svg-geometries/svg-image.d.ts +4 -0
  71. package/lib/renderers/svg-bsp/svg-geometries/svg-image.d.ts.map +1 -0
  72. package/lib/renderers/svg-bsp/svg-geometries/svg-image.js +44 -0
  73. package/lib/renderers/svg-bsp/svg-geometries/svg-image.js.map +1 -0
  74. package/lib/renderers/svg-bsp/svg-geometries/svg-line.d.ts +4 -0
  75. package/lib/renderers/svg-bsp/svg-geometries/svg-line.d.ts.map +1 -0
  76. package/lib/renderers/svg-bsp/svg-geometries/svg-line.js +12 -0
  77. package/lib/renderers/svg-bsp/svg-geometries/svg-line.js.map +1 -0
  78. package/lib/renderers/svg-bsp/svg-geometries/svg-plane.d.ts +4 -0
  79. package/lib/renderers/svg-bsp/svg-geometries/svg-plane.d.ts.map +1 -0
  80. package/lib/renderers/svg-bsp/svg-geometries/svg-plane.js +24 -0
  81. package/lib/renderers/svg-bsp/svg-geometries/svg-plane.js.map +1 -0
  82. package/lib/renderers/svg-bsp/svg-geometries/svg-polygon.d.ts +4 -0
  83. package/lib/renderers/svg-bsp/svg-geometries/svg-polygon.d.ts.map +1 -0
  84. package/lib/renderers/svg-bsp/svg-geometries/svg-polygon.js +24 -0
  85. package/lib/renderers/svg-bsp/svg-geometries/svg-polygon.js.map +1 -0
  86. package/lib/renderers/svg-bsp/svg-geometries/svg-shape.d.ts +4 -0
  87. package/lib/renderers/svg-bsp/svg-geometries/svg-shape.d.ts.map +1 -0
  88. package/lib/renderers/svg-bsp/svg-geometries/svg-shape.js +19 -0
  89. package/lib/renderers/svg-bsp/svg-geometries/svg-shape.js.map +1 -0
  90. package/lib/renderers/svg-bsp/svg-geometries/svg-text.d.ts +4 -0
  91. package/lib/renderers/svg-bsp/svg-geometries/svg-text.d.ts.map +1 -0
  92. package/lib/renderers/svg-bsp/svg-geometries/svg-text.js +26 -0
  93. package/lib/renderers/svg-bsp/svg-geometries/svg-text.js.map +1 -0
  94. package/lib/renderers/svg-bsp/svg-order.d.ts +10 -0
  95. package/lib/renderers/svg-bsp/svg-order.d.ts.map +1 -0
  96. package/lib/renderers/svg-bsp/svg-order.js +257 -0
  97. package/lib/renderers/svg-bsp/svg-order.js.map +1 -0
  98. package/lib/renderers/svg-bsp/svg.d.ts +16 -0
  99. package/lib/renderers/svg-bsp/svg.d.ts.map +1 -0
  100. package/lib/renderers/svg-bsp/svg.js +120 -0
  101. package/lib/renderers/svg-bsp/svg.js.map +1 -0
  102. package/lib/renderers/three/build/parts.d.ts +2 -1
  103. package/lib/renderers/three/build/parts.d.ts.map +1 -1
  104. package/lib/renderers/three/build/parts.js +25 -15
  105. package/lib/renderers/three/build/parts.js.map +1 -1
  106. package/package.json +6 -1
  107. package/src/renderers/svg/svg-encoding.ts +0 -13
  108. package/src/renderers/svg/svg-geometries/shared.ts +3 -31
  109. package/src/renderers/svg/svg-geometries/svg-box.ts +36 -51
  110. package/src/renderers/svg/svg-geometries/svg-cone.ts +3 -18
  111. package/src/renderers/svg/svg-geometries/svg-cylinder.ts +6 -26
  112. package/src/renderers/svg/svg-geometries/svg-image.ts +2 -3
  113. package/src/renderers/svg/svg-geometries/svg-line.ts +1 -3
  114. package/src/renderers/svg/svg-geometries/svg-plane.ts +2 -12
  115. package/src/renderers/svg/svg-geometries/svg-polygon.ts +2 -10
  116. package/src/renderers/svg/svg-geometries/svg-shape.ts +2 -11
  117. package/src/renderers/svg/svg-geometries/svg-text.ts +1 -1
  118. package/src/renderers/svg/svg.ts +12 -14
  119. package/src/renderers/svg-bsp/class-names.ts +10 -0
  120. package/src/renderers/svg-bsp/index.ts +4 -0
  121. package/src/renderers/svg-bsp/svg-encoding.ts +190 -0
  122. package/src/renderers/svg-bsp/svg-geometries/shared.ts +88 -0
  123. package/src/renderers/svg-bsp/svg-geometries/svg-box.ts +76 -0
  124. package/src/renderers/svg-bsp/svg-geometries/svg-cone.ts +68 -0
  125. package/src/renderers/svg-bsp/svg-geometries/svg-cylinder.ts +82 -0
  126. package/src/renderers/svg-bsp/svg-geometries/svg-image.ts +56 -0
  127. package/src/renderers/svg-bsp/svg-geometries/svg-line.ts +21 -0
  128. package/src/renderers/svg-bsp/svg-geometries/svg-plane.ts +43 -0
  129. package/src/renderers/svg-bsp/svg-geometries/svg-polygon.ts +33 -0
  130. package/src/renderers/svg-bsp/svg-geometries/svg-shape.ts +34 -0
  131. package/src/renderers/svg-bsp/svg-geometries/svg-text.ts +40 -0
  132. package/src/renderers/svg-bsp/svg.ts +183 -0
  133. package/src/renderers/three/build/build.test.ts +8 -4
  134. package/src/renderers/three/build/parts.ts +27 -19
  135. package/lib/renderers/dxf/color.d.ts +0 -7
  136. package/lib/renderers/dxf/color.d.ts.map +0 -1
  137. package/lib/renderers/dxf/color.js +0 -311
  138. package/lib/renderers/dxf/color.js.map +0 -1
  139. package/lib/renderers/dxf/dxf-encoding.d.ts +0 -17
  140. package/lib/renderers/dxf/dxf-encoding.d.ts.map +0 -1
  141. package/lib/renderers/dxf/dxf-encoding.js +0 -18379
  142. package/lib/renderers/dxf/dxf-encoding.js.map +0 -1
  143. package/lib/renderers/react/react-camera.d.ts +0 -71
  144. package/lib/renderers/react/react-camera.d.ts.map +0 -1
  145. package/lib/renderers/react/react-camera.js +0 -174
  146. package/lib/renderers/react/react-camera.js.map +0 -1
  147. package/lib/renderers/react/react-dimension.d.ts +0 -17
  148. package/lib/renderers/react/react-dimension.d.ts.map +0 -1
  149. package/lib/renderers/react/react-dimension.js +0 -15
  150. package/lib/renderers/react/react-dimension.js.map +0 -1
  151. package/lib/renderers/react/react-group.d.ts +0 -23
  152. package/lib/renderers/react/react-group.d.ts.map +0 -1
  153. package/lib/renderers/react/react-group.js +0 -69
  154. package/lib/renderers/react/react-group.js.map +0 -1
  155. package/lib/renderers/react/react-hotspot.d.ts +0 -27
  156. package/lib/renderers/react/react-hotspot.d.ts.map +0 -1
  157. package/lib/renderers/react/react-hotspot.js +0 -37
  158. package/lib/renderers/react/react-hotspot.js.map +0 -1
  159. package/lib/renderers/react/react-image-material.d.ts +0 -28
  160. package/lib/renderers/react/react-image-material.d.ts.map +0 -1
  161. package/lib/renderers/react/react-image-material.js +0 -81
  162. package/lib/renderers/react/react-image-material.js.map +0 -1
  163. package/lib/renderers/react/react-material.d.ts +0 -19
  164. package/lib/renderers/react/react-material.d.ts.map +0 -1
  165. package/lib/renderers/react/react-material.js +0 -39
  166. package/lib/renderers/react/react-material.js.map +0 -1
  167. package/lib/renderers/react/react-mesh.d.ts +0 -11
  168. package/lib/renderers/react/react-mesh.d.ts.map +0 -1
  169. package/lib/renderers/react/react-mesh.js +0 -337
  170. package/lib/renderers/react/react-mesh.js.map +0 -1
  171. package/lib/renderers/react/react-scene.d.ts +0 -28
  172. package/lib/renderers/react/react-scene.d.ts.map +0 -1
  173. package/lib/renderers/react/react-scene.js +0 -13
  174. package/lib/renderers/react/react-scene.js.map +0 -1
  175. package/lib/renderers/react/react-types.d.ts +0 -7
  176. package/lib/renderers/react/react-types.d.ts.map +0 -1
  177. package/lib/renderers/react/react-types.js +0 -3
  178. package/lib/renderers/react/react-types.js.map +0 -1
  179. package/lib/renderers/shared.d.ts +0 -27
  180. package/lib/renderers/shared.d.ts.map +0 -1
  181. package/lib/renderers/shared.js +0 -131
  182. package/lib/renderers/shared.js.map +0 -1
  183. /package/src/renderers/{svg → svg-bsp}/svg-order.test.ts +0 -0
  184. /package/src/renderers/{svg → svg-bsp}/svg-order.ts +0 -0
@@ -3,7 +3,7 @@ import { vec2Scale, vec3TransRot, vec3RotCombine, vec3Zero, vec3, vec3ZMean } fr
3
3
  import { rgbGrayScale } from "../../../utils.js";
4
4
  import { svgPolygon } from "../svg-encoding.js";
5
5
  import type { zOrderElement, SvgOptions } from "./shared.js";
6
- import { gray, black, isOpaque, zElem } from "./shared.js";
6
+ import { gray, black, zElem } from "./shared.js";
7
7
 
8
8
  export function plane(
9
9
  p: Plane,
@@ -29,15 +29,5 @@ export function plane(
29
29
  const [strokeColor, fill, strokeThickness] = opts.only_stroke
30
30
  ? [opts.gray_scale ? gray : material.normal, opts.background, opts.stroke_thickness]
31
31
  : [black, opts.gray_scale ? rgbGrayScale(material.normal) : material.normal, 0];
32
- const opacity = material.opacity ?? 1.0;
33
- const plain = !holes?.length;
34
- return [
35
- zElem(
36
- svgPolygon(rot, points, fill, opacity, strokeColor, strokeThickness, holes),
37
- vec3ZMean(v1, v2, v3, v4),
38
- [v1, v2, v3, v4],
39
- isOpaque(fill, opacity) && plain,
40
- plain ? { fill, opacity, stroke: strokeColor, strokeWidth: strokeThickness } : undefined
41
- ),
42
- ];
32
+ return [zElem(svgPolygon(rot, points, fill, material.opacity ?? 1.0, strokeColor, strokeThickness, holes), vec3ZMean(v1, v2, v3, v4))];
43
33
  }
@@ -3,7 +3,7 @@ import { vec3ZMean, vec3TransRot, vec3RotCombine, vec3Zero } from "../../../abst
3
3
  import { rgbGrayScale } from "../../../utils.js";
4
4
  import { svgPolygon } from "../svg-encoding.js";
5
5
  import type { zOrderElement, SvgOptions } from "./shared.js";
6
- import { gray, isOpaque, zElem, transparent } from "./shared.js";
6
+ import { gray, zElem, transparent } from "./shared.js";
7
7
 
8
8
  export function polygon(
9
9
  p: Polygon,
@@ -21,13 +21,5 @@ export function polygon(
21
21
  const [strokeColor, fill, strokeThickness] = opts.only_stroke
22
22
  ? [opts.gray_scale ? gray : color, opts.background, opts.stroke_thickness]
23
23
  : [transparent, opts.gray_scale ? rgbGrayScale(color) : color, 0];
24
- const opacity = material.opacity ?? 1.0;
25
- return [
26
- zElem(svgPolygon(rot, points, fill, opacity, strokeColor, strokeThickness), vec3ZMean(...rotatedPoints), rotatedPoints, isOpaque(fill, opacity), {
27
- fill,
28
- opacity,
29
- stroke: strokeColor,
30
- strokeWidth: strokeThickness,
31
- }),
32
- ];
24
+ return [zElem(svgPolygon(rot, points, fill, material.opacity ?? 1.0, strokeColor, strokeThickness), vec3ZMean(...rotatedPoints))];
33
25
  }
@@ -3,7 +3,7 @@ import { vec3ZMean, vec3TransRot, vec3RotCombine, vec3, vec3Zero } from "../../.
3
3
  import { rgbGrayScale } from "../../../utils.js";
4
4
  import { svgPolygon } from "../svg-encoding.js";
5
5
  import type { zOrderElement, SvgOptions } from "./shared.js";
6
- import { gray, isOpaque, zElem, transparent } from "./shared.js";
6
+ import { gray, zElem, transparent } from "./shared.js";
7
7
 
8
8
  export function shape(
9
9
  s: Shape,
@@ -21,14 +21,5 @@ export function shape(
21
21
  const [strokeColor, fill] = opts.only_stroke
22
22
  ? [opts.gray_scale ? gray : color, opts.background]
23
23
  : [transparent, opts.gray_scale ? rgbGrayScale(color) : color];
24
- const opacity = material.opacity ?? 1.0;
25
- return [
26
- zElem(
27
- svgPolygon(rot, points, fill, opacity, strokeColor, 0),
28
- vec3ZMean(...rotatedPoints),
29
- rotatedPoints,
30
- isOpaque(fill, opacity) && !s.holes?.length,
31
- { fill, opacity, stroke: strokeColor, strokeWidth: 0 }
32
- ),
33
- ];
24
+ return [zElem(svgPolygon(rot, points, fill, material.opacity ?? 1.0, strokeColor, 0), vec3ZMean(...rotatedPoints))];
34
25
  }
@@ -32,7 +32,7 @@ export function text(
32
32
  let posY = strings.length === 1 ? 0 : (fontSize * strings.length - fontSize) / 2;
33
33
  for (const s of strings) {
34
34
  const matrix = svgTrsMatrix(point(pos.x, pos.y + posY), rot);
35
- texts.push(zElem(svgText(s, matrix, fill, opts.font, fontSize), pos.z, [pos]));
35
+ texts.push(zElem(svgText(s, matrix, fill, opts.font, fontSize), pos.z));
36
36
  posY -= fontSize * 1.2;
37
37
  }
38
38
 
@@ -29,7 +29,6 @@ import { plane } from "./svg-geometries/svg-plane.js";
29
29
  import { polygon } from "./svg-geometries/svg-polygon.js";
30
30
  import { shape } from "./svg-geometries/svg-shape.js";
31
31
  import { text } from "./svg-geometries/svg-text.js";
32
- import { orderElements } from "./svg-order.js";
33
32
 
34
33
  export type SvgScene = {
35
34
  readonly scene: Scene;
@@ -93,13 +92,13 @@ function renderInternal(
93
92
  imageBg: options?.imageBg ?? false,
94
93
  className: options?.className ?? "",
95
94
  };
96
- // A three-quarter view: the front turned 30° to show the side, and tilted 20° to show the top, the front foremost
97
- const angled = opts.view === "TopLeft" || opts.view === "TopRight";
98
- const view = angled ? "front" : opts.view;
99
- const angledRot = angled
100
- ? vec3RotCombine(vec3((20 * Math.PI) / 180, 0, 0), vec3(0, ((opts.view === "TopRight" ? -1 : 1) * 30 * Math.PI) / 180, 0))
95
+ // Isometric: the front turned 45° to show the side, and tilted by arctan(1/√2) to show the top, so the three axes are foreshortened alike
96
+ const isometric = opts.view === "TopLeft" || opts.view === "TopRight";
97
+ const view = isometric ? "front" : opts.view;
98
+ const isometricRot = isometric
99
+ ? vec3RotCombine(vec3(Math.atan(1 / Math.SQRT2), 0, 0), vec3(0, ((opts.view === "TopRight" ? -1 : 1) * Math.PI) / 4, 0))
101
100
  : vec3Zero;
102
- const cameraRot = vec3RotCombine(angledRot, rotationForCameraPos(view));
101
+ const cameraRot = vec3RotCombine(isometricRot, rotationForCameraPos(view));
103
102
  const viewRot = opts.rotation ? vec3RotCombine(vec3(0, 0, (opts.rotation * Math.PI) / 180), cameraRot) : cameraRot;
104
103
  const unitRot = vec3RotCombine(viewRot, scene.rotation_deprecated ?? vec3Zero);
105
104
 
@@ -110,17 +109,16 @@ function renderInternal(
110
109
  const point = (x: number, y: number): Vec2 => vec2(svgCenter.x + x, svgCenter.y - y);
111
110
  const unitCenterFlipped = vec3Flip(unitCenter);
112
111
 
113
- const elements = [
114
- ...orderElements(
115
- scene.groups.flatMap((g) => svgGroup(g, unitCenterFlipped, unitRot, point, opts)),
116
- point
117
- ),
118
- ];
112
+ const elements = Array<zOrderElement>();
113
+ for (const g of scene.groups) {
114
+ elements.push(...svgGroup(g, unitCenterFlipped, unitRot, point, opts));
115
+ }
119
116
  const dimOpts: SvgOptions = { ...opts, only_stroke: false, gray_scale: false };
117
+ elements.sort((a, b) => a.zOrder - b.zOrder);
120
118
  const visibleViews = calculateVisibleViews(view, scene.rotation_deprecated);
121
119
 
122
120
  // Dimensions are drawn for the flat views
123
- for (const dim of angled ? [] : (scene.dimensions_deprecated?.dimensions ?? [])) {
121
+ for (const dim of isometric ? [] : (scene.dimensions_deprecated?.dimensions ?? [])) {
124
122
  const d = dimensionConvertToTypeMesh(dim, scene.rotation_deprecated ?? vec3Zero, scene.dimensions_deprecated?.material);
125
123
  if (isViewVisible(d.views[0], visibleViews)) {
126
124
  const pos = vec3TransRot(d.pos, unitCenterFlipped, unitRot);
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The classes the svg renderer puts on what it draws, for the page's CSS: `root` on the svg, e.g. sized to fill its box, `stroke` on each stroked
3
+ * element, e.g. as thick at any size by `vector-effect: non-scaling-stroke`, `image` on an embedded picture or svg. Colors and strokes are
4
+ * attributes, which a class overrides
5
+ */
6
+ export const classNames = {
7
+ root: "a3d-root",
8
+ stroke: "a3d-stroke",
9
+ image: "a3d-image",
10
+ } as const;
@@ -0,0 +1,4 @@
1
+ export * from "./svg.js";
2
+ export * from "./class-names.js";
3
+ export { type EmbededImage } from "./svg-encoding.js";
4
+ export type { ImageDataUri, SvgOptions, SvgView } from "./svg-geometries/shared.js";
@@ -0,0 +1,190 @@
1
+ import type { Hole, Vec2, Vec3 } from "../../abstract-3d.js";
2
+ import { bounds2FromVec2Array, vec2, vec2Add, vec2Scale, vec2Sub, vec3, vec3Rot, vec3Zero } from "../../abstract-3d.js";
3
+ import { classNames } from "./class-names.js";
4
+ import { svgTrsMatrix } from "./svg-geometries/shared.js";
5
+
6
+ export const svg = (min: Vec2, size: Vec2, children: string, className: string): string => {
7
+ return `<svg xmlns="http://www.w3.org/2000/svg" class="${className ? `${classNames.root} ${className}` : classNames.root}" viewBox="${min.x.toFixed(0)} ${min.y.toFixed(0)} ${size.x.toFixed(
8
+ 0
9
+ )} ${size.y.toFixed(0)}" width="${size.x.toFixed(0)}px" height="${size.y.toFixed(0)}px">${children}</svg>`;
10
+ };
11
+ export const svgLine = (p1: Vec2, p2: Vec2, stroke: string, strokeWidth: number): string =>
12
+ `<line class="${classNames.stroke}" x1="${p1.x.toFixed(0)}" y1="${p1.y.toFixed(0)}" x2="${p2.x.toFixed(0)}" y2="${p2.y.toFixed(
13
+ 0
14
+ )}" stroke="${stroke}" stroke-width="${strokeWidth}" />`;
15
+
16
+ export function svgPolygon(
17
+ rot: Vec3,
18
+ points: ReadonlyArray<Vec2>,
19
+ fill: string,
20
+ opacity: number,
21
+ stroke: string,
22
+ strokeWidth: number,
23
+ holes?: ReadonlyArray<Hole>,
24
+ _parentPos?: Vec3
25
+ ): string {
26
+ const bounds = bounds2FromVec2Array(points);
27
+ const size = vec2Sub(bounds.max, bounds.min);
28
+ const pos = vec2Scale(vec2Add(bounds.max, bounds.min), 0.5);
29
+ const [mask, maskAttribute] = svgHoleMask(rot, size, holes ?? []);
30
+ const pol = `<polygon class="${classNames.stroke}" points="${points
31
+ .reduce((a, c) => (a += `${c.x.toFixed(0)},${c.y.toFixed(0)} `), "")
32
+ .slice(0, -1)}" fill="${fill}" fill-opacity="${opacity.toFixed(1)}" stroke="${stroke}" stroke-width="${strokeWidth}" ${maskAttribute}/>`;
33
+ return mask + pol + svgStrokedHoles(pos, rot, holes ?? [], stroke, strokeWidth);
34
+ }
35
+
36
+ export function svgCircle(
37
+ radius: number,
38
+ rot: Vec3,
39
+ pos: Vec2,
40
+ fill: string,
41
+ opacity: number,
42
+ stroke: string,
43
+ strokeWidth: number,
44
+ holes?: ReadonlyArray<Hole>
45
+ ): string {
46
+ const size = vec2Scale(vec2(radius, radius), 2);
47
+ const [mask, maskAttribute] = svgHoleMask(rot, size, holes ?? []);
48
+ const cir = `<circle class="${classNames.stroke}" r="${radius.toFixed(0)}" cx="${pos.x.toFixed(0)}" cy="${pos.y.toFixed(0)}" fill="${fill}" fill-opacity="${opacity.toFixed(
49
+ 1
50
+ )}" stroke="${stroke}" stroke-width="${strokeWidth}" ${maskAttribute}/>`;
51
+ return mask + cir + svgStrokedHoles(pos, rot, holes ?? [], stroke, strokeWidth);
52
+ }
53
+
54
+ function svgStrokedHoles(pos: Vec2, rot: Vec3, holes: ReadonlyArray<Hole>, strokeColor: string, strokeThickness: number): string {
55
+ if (strokeThickness <= Number.EPSILON) {
56
+ return "";
57
+ }
58
+
59
+ let svgHoles = "";
60
+ for (const hole of holes) {
61
+ const localSvg = vec3(hole.pos.x, -hole.pos.y, 0);
62
+ const rotated = vec3Rot(localSvg, vec3Zero, rot);
63
+ const holeCenter = vec2Add(vec2(pos.x, pos.y), vec2(rotated.x, rotated.y));
64
+ switch (hole.type) {
65
+ case "RoundHole": {
66
+ const matrix = svgTrsMatrix(holeCenter, vec3Zero);
67
+ svgHoles += `<circle
68
+ class="${classNames.stroke}"
69
+ r="${hole.radius.toFixed(0)}"
70
+ transform="${matrix}"
71
+ fill="none"
72
+ stroke="${strokeColor}"
73
+ stroke-width="${strokeThickness}"
74
+ />`;
75
+ break;
76
+ }
77
+
78
+ case "SquareHole": {
79
+ const half = vec2Scale(hole.size, 0.5);
80
+ const points = [vec2(-half.x, -half.y), vec2(half.x, -half.y), vec2(half.x, half.y), vec2(-half.x, half.y)]
81
+ .map((p) => `${p.x.toFixed(0)},${p.y.toFixed(0)}`)
82
+ .join(" ");
83
+
84
+ const matrix = svgTrsMatrix(holeCenter, rot);
85
+
86
+ svgHoles += `<polygon
87
+ class="${classNames.stroke}"
88
+ points="${points}"
89
+ transform="${matrix}"
90
+ fill="none"
91
+ stroke="${strokeColor}"
92
+ stroke-width="${strokeThickness}"
93
+ />`;
94
+ break;
95
+ }
96
+ default:
97
+ break;
98
+ }
99
+ }
100
+ return svgHoles;
101
+ }
102
+
103
+ /** A polygon's edges that are flagged, the edge from each point to the next, as one path, without a fill */
104
+ export function svgEdges(points: ReadonlyArray<Vec2>, edges: ReadonlyArray<boolean>, stroke: string, strokeWidth: number): string {
105
+ // From after an unflagged edge, so a run of edges isn't broken where the points wrap around
106
+ const start = edges.indexOf(false) + 1;
107
+ const d = points.reduce((path, _, k) => {
108
+ const i = (start + k) % points.length;
109
+ const [p, q] = [points[i]!, points[(i + 1) % points.length]!];
110
+ const move = k > 0 && edges[(i + points.length - 1) % points.length] ? "" : `M${p.x.toFixed(0)},${p.y.toFixed(0)}`;
111
+ return edges[i] ? `${path}${move}L${q.x.toFixed(0)},${q.y.toFixed(0)}` : path;
112
+ }, "");
113
+ return `<path class="${classNames.stroke}" d="${d}" fill="none" stroke="${stroke}" stroke-width="${strokeWidth}" />`;
114
+ }
115
+
116
+ export const svgCircle2 = (radius: number, pos: Vec2, fill: string, stroke: string, strokeWidth: number, _holes?: ReadonlyArray<Hole>): string =>
117
+ `<circle class="${classNames.stroke}" r="${radius.toFixed(0)}" cx="${pos.x.toFixed(0)}" cy="${pos.y.toFixed(
118
+ 0
119
+ )}" fill="${fill}" stroke="${stroke}" stroke-width="${strokeWidth}" />`;
120
+
121
+ function svgHoleMask(rot: Vec3, size: Vec2, holes: ReadonlyArray<Hole>): [string, string] {
122
+ const id = `mask_${counter()}`;
123
+ return holes.length > 0
124
+ ? [
125
+ `<mask id="${id}" mask-type="luminance" maskContentUnits="objectBoundingBox">
126
+ <rect x="0" y="0" width="1" height="1" fill="white" />
127
+ ${svgMaskHoles(rot, size, holes)}
128
+ </mask>\n`,
129
+ `mask="url(#${id}) "`,
130
+ ]
131
+ : ["", ""];
132
+ }
133
+
134
+ function svgMaskHoles(rot: Vec3, size: Vec2, holes: ReadonlyArray<Hole>): string {
135
+ const holeMasks: Array<string> = [];
136
+ for (const hole of holes) {
137
+ const holePosRotated = vec3Rot(vec3(hole.pos.x, hole.pos.y, 0), vec3Zero, rot);
138
+ const holePos = vec2Add(holePosRotated, vec2Scale(size, 0.5));
139
+ switch (hole.type) {
140
+ case "RoundHole": {
141
+ holeMasks.push(
142
+ `<ellipse cx="${holePos.x / size.x}" cy="${holePos.y / size.y}" rx="${hole.radius / size.x}" ry="${hole.radius / size.y}" fill="black" />`
143
+ );
144
+ break;
145
+ }
146
+ case "SquareHole": {
147
+ const holeSizeRotated = vec3Rot(vec3(hole.size.x, hole.size.y, 0), vec3Zero, rot);
148
+ const holeSize = vec2(Math.abs(holeSizeRotated.x), Math.abs(holeSizeRotated.y));
149
+ const halfSize = vec2Scale(holeSize, 0.5);
150
+ holeMasks.push(
151
+ `<rect x="${(holePos.x - halfSize.x) / size.x}" y="${(holePos.y - halfSize.y) / size.y}" width="${
152
+ holeSize.x / size.x
153
+ }" height="${holeSize.y / size.y}" fill="black" />`
154
+ );
155
+ break;
156
+ }
157
+ default:
158
+ break;
159
+ }
160
+ }
161
+ return holeMasks.join("\n");
162
+ }
163
+
164
+ export const svgText = (text: string, matrix: string, color: string, font: string, fontSize: number): string =>
165
+ `<text font-family="${font}" font-size="${fontSize}px" text-anchor="middle" alignment-baseline="middle" fill="${color}" transform="${matrix}">${text}</text>`;
166
+
167
+ export type EmbededImage = { readonly type: "url"; readonly url: string } | { readonly type: "svg"; readonly svg: string };
168
+
169
+ export const rawSvgPrefix = "data:image/svg+xml,";
170
+
171
+ export const svgImage = (p: Vec2, size: Vec2, rot: Vec3, data: EmbededImage, background?: string, scale?: Vec2): string => {
172
+ const matrix = svgTrsMatrix(p, rot, scale);
173
+
174
+ if (data.type === "url") {
175
+ const bg = background ? `<rect width="${size.x.toFixed(0)}" height="${size.y.toFixed(0)}" fill="${background}"/>` : "";
176
+ return `<g transform="${matrix}">${bg}<image class="${classNames.image}" width="${size.x.toFixed(0)}" height="${size.y.toFixed(0)}" href="${data.url}"/></g>`;
177
+ }
178
+
179
+ // scale maps image-space coords → world coords, so the wrapper SVG and rect
180
+ // must use image-space dimensions: imgW * scale.x = size.x (the desired world size).
181
+ const imgW = scale ? size.x / scale.x : size.x;
182
+ const imgH = scale ? size.y / scale.y : size.y;
183
+ const bg = background ? `<rect width="${imgW.toFixed(0)}" height="${imgH.toFixed(0)}" fill="${background}"/>` : "";
184
+ return `<g transform="${matrix}"><svg class="${classNames.image}" width="${imgW.toFixed(0)}" height="${imgH.toFixed(0)}">${bg}${data.svg}</svg></g>`;
185
+ };
186
+
187
+ const counter = (() => {
188
+ let counter = 0;
189
+ return () => counter++;
190
+ })();
@@ -0,0 +1,88 @@
1
+ import type { Vec2, Vec3, View } from "../../../abstract-3d.js";
2
+ import { vec3TransRot, vec3Zero } from "../../../abstract-3d.js";
3
+
4
+ /**
5
+ * An element and its mean depth, and the points it covers in view space (x right, y up, z toward the viewer), by which it's ordered when given,
6
+ * whether it hides what's behind it, and the style of a plain polygon, without holes, by which its pieces are drawn when it's split
7
+ */
8
+ export type zOrderElement = {
9
+ readonly element: string;
10
+ readonly zOrder: number;
11
+ readonly polygon?: ReadonlyArray<Vec3>;
12
+ readonly opaque?: boolean;
13
+ readonly style?: PolygonStyle;
14
+ };
15
+ export type PolygonStyle = { readonly fill: string; readonly opacity: number; readonly stroke: string; readonly strokeWidth: number };
16
+ export const zElem = (element: string, zOrder: number, polygon?: ReadonlyArray<Vec3>, opaque?: boolean, style?: PolygonStyle): zOrderElement => ({
17
+ element,
18
+ zOrder,
19
+ ...(polygon && { polygon }),
20
+ ...(opaque && { opaque }),
21
+ ...(style && { style }),
22
+ });
23
+
24
+ /** Whether a fill hides what's behind it: fully opaque, and not transparent, e.g. a sketch's default background */
25
+ export const isOpaque = (fill: string, opacity: number): boolean => opacity >= 1 && !/^(transparent|none|rgba\(.*,\s*0(\.0*)?\s*\))$/.test(fill);
26
+
27
+ /** Twice the signed area of a polygon on screen */
28
+ export function getArea(points: ReadonlyArray<Vec2>): number {
29
+ return points.reduce((sum, p, i) => {
30
+ const q = points[(i + 1) % points.length]!;
31
+ return sum + p.x * q.y - q.x * p.y;
32
+ }, 0);
33
+ }
34
+
35
+ export const stBW = 0.7;
36
+ export const stN = 0.1;
37
+
38
+ export const gray = "rgb(85, 85, 85)";
39
+ export const white = "rgb(255, 255, 255)";
40
+ export const transparent = "rgba(255, 255, 255, 0)";
41
+ export const black = "rgb(15, 15, 15)";
42
+
43
+ export type ImageDataUri = `data:image/png;base64,${string}` | `data:image/jpeg;base64,${string}` | `data:image/svg+xml,${string}`;
44
+
45
+ /** A view, or a three-quarter one from the front's top left or top right, which shows the front foremost, the top and that side */
46
+ export type SvgView = View | "TopLeft" | "TopRight";
47
+
48
+ export type SvgOptions = {
49
+ readonly view: SvgView;
50
+ readonly stroke_thickness: number;
51
+ readonly only_stroke: boolean;
52
+ readonly gray_scale: boolean;
53
+ readonly background: string;
54
+ readonly rotation: number;
55
+ readonly imageBg: boolean;
56
+
57
+ readonly font: string;
58
+ readonly imageDataByUrl: Record<string, ImageDataUri>;
59
+ /** Its svg's class besides `classNames.root`, e.g. sized to fill its box */
60
+ readonly className: string;
61
+ };
62
+
63
+ export function svgTrsMatrix(translation: Vec2, rotation: Vec3, scale?: Vec2): string {
64
+ const xRot = vec3TransRot({ x: 1, y: 0, z: 0 }, vec3Zero, rotation);
65
+ const yRot = vec3TransRot({ x: 0, y: -1, z: 0 }, vec3Zero, rotation);
66
+
67
+ const x2D = { x: xRot.x, y: -xRot.y };
68
+ const y2D = { x: yRot.x, y: -yRot.y };
69
+
70
+ const sx = scale?.x ?? 1;
71
+ const sy = scale?.y ?? 1;
72
+
73
+ const a = x2D.x * sx;
74
+ const b = x2D.y * sx;
75
+ const c = y2D.x * sy;
76
+ const d = y2D.y * sy;
77
+ const e = translation.x;
78
+ const f = translation.y;
79
+
80
+ return `matrix(${svgNum(a)} ${svgNum(b)} ${svgNum(c)} ${svgNum(d)} ${svgNum(e)} ${svgNum(f)})`;
81
+ }
82
+
83
+ const svgNum = (num: number): string => {
84
+ if (Math.abs(num) < 1e-9) {
85
+ return "0";
86
+ }
87
+ return num.toFixed(6);
88
+ };
@@ -0,0 +1,76 @@
1
+ import type { Box, Vec2, Vec3, Material } from "../../../abstract-3d.js";
2
+ import { vec3Scale, vec3TransRot, vec3RotCombine, vec3Zero, vec3, vec3ZMean } from "../../../abstract-3d.js";
3
+ import { rgbGrayScale } from "../../../utils.js";
4
+ import { svgPolygon } from "../svg-encoding.js";
5
+ import type { zOrderElement, SvgOptions } from "./shared.js";
6
+ import { gray, black, zElem, getArea, isOpaque } from "./shared.js";
7
+
8
+ export function box(
9
+ b: Box,
10
+ point: (x: number, y: number) => Vec2,
11
+ material: Material,
12
+ opts: SvgOptions,
13
+ parentPos: Vec3,
14
+ parentRot: Vec3
15
+ ): ReadonlyArray<zOrderElement> {
16
+ const half = vec3Scale(b.size, 0.5);
17
+ const pos = vec3TransRot(b.pos, parentPos, parentRot);
18
+ const rot = vec3RotCombine(parentRot, b.rot ?? vec3Zero);
19
+ const corners = unitCorners.map((c) => vec3TransRot(vec3(c.x * half.x, c.y * half.y, c.z * half.z), pos, rot));
20
+
21
+ const color = material.normal;
22
+ const opacity = material.opacity ?? 1.0;
23
+ const [strokeColor, fill, strokeUse] = opts.only_stroke
24
+ ? [opts.gray_scale ? gray : color, opts.background, opts.stroke_thickness]
25
+ : [black, opts.gray_scale ? rgbGrayScale(color) : color, 0];
26
+ const opaque = isOpaque(fill, opacity);
27
+
28
+ return faces.flatMap(([one, other], i) => {
29
+ const polygon = (one.reduce((z, c) => z + corners[c]!.z, 0) > other.reduce((z, c) => z + corners[c]!.z, 0) ? one : other).map((c) => corners[c]!);
30
+ const points = polygon.map((c) => point(c.x, c.y));
31
+ // Seen edge on, a face is only a line along the edges of the box's other faces
32
+ if (Math.abs(getArea(points)) < minArea) {
33
+ return [];
34
+ }
35
+ // Only the front and back have holes, which show what's behind them
36
+ const holes = i === 0 ? b.holes : undefined;
37
+ const plain = !holes?.length;
38
+ return [
39
+ zElem(
40
+ svgPolygon(rot, points, fill, opacity, strokeColor, strokeUse, holes),
41
+ vec3ZMean(...polygon),
42
+ polygon,
43
+ opaque && plain,
44
+ plain ? { fill, opacity, stroke: strokeColor, strokeWidth: strokeUse } : undefined
45
+ ),
46
+ ];
47
+ });
48
+ }
49
+
50
+ const unitCorners = [
51
+ vec3(-1, -1, 1),
52
+ vec3(1, -1, 1),
53
+ vec3(1, 1, 1),
54
+ vec3(-1, 1, 1),
55
+ vec3(-1, -1, -1),
56
+ vec3(1, -1, -1),
57
+ vec3(1, 1, -1),
58
+ vec3(-1, 1, -1),
59
+ ];
60
+ /** Pairs of opposite faces by their corners, front and back, top and bottom, right and left, of which the one toward the viewer is drawn */
61
+ const faces: ReadonlyArray<readonly [ReadonlyArray<number>, ReadonlyArray<number>]> = [
62
+ [
63
+ [0, 1, 2, 3],
64
+ [4, 5, 6, 7],
65
+ ],
66
+ [
67
+ [7, 6, 2, 3],
68
+ [4, 5, 1, 0],
69
+ ],
70
+ [
71
+ [5, 1, 2, 6],
72
+ [4, 0, 3, 7],
73
+ ],
74
+ ];
75
+ /** Of a face on screen, in the scene's units squared, below which it's seen edge on */
76
+ const minArea = 1e-6;
@@ -0,0 +1,68 @@
1
+ import type { Cone, Vec2, Vec3, Material } from "../../../abstract-3d.js";
2
+ import { vec3, vec3TransRot, vec3RotCombine, vec3Zero, vec3ZMean, equals } from "../../../abstract-3d.js";
3
+ import { rgbGrayScale } from "../../../utils.js";
4
+ import { svgCircle, svgPolygon } from "../svg-encoding.js";
5
+ import type { zOrderElement, SvgOptions } from "./shared.js";
6
+ import { gray, isOpaque, stBW, zElem, transparent } from "./shared.js";
7
+
8
+ export function cone(
9
+ c: Cone,
10
+ point: (x: number, y: number) => Vec2,
11
+ material: Material,
12
+ opts: SvgOptions,
13
+ parentPos: Vec3,
14
+ parentRot: Vec3
15
+ ): ReadonlyArray<zOrderElement> {
16
+ const half = vec3(c.radius, c.length / 2, c.radius);
17
+ const pos = vec3TransRot(c.pos, parentPos, parentRot);
18
+ const rot = vec3RotCombine(parentRot, c.rot ?? vec3Zero);
19
+ const vec3tr = (p: Vec3): Vec3 => vec3TransRot(p, pos, rot);
20
+
21
+ const color = material.normal;
22
+ const opacity = material.opacity ?? 1.0;
23
+ const [stroke, fill] = opts.only_stroke
24
+ ? [opts.gray_scale ? gray : color, opts.background]
25
+ : [transparent, opts.gray_scale ? rgbGrayScale(color) : color];
26
+ const zOrderComponents = Array<zOrderElement>();
27
+
28
+ const sides = 8;
29
+ const angleStep = (2 * Math.PI) / sides;
30
+ let currentAngle = 0;
31
+
32
+ const topPos = vec3tr(vec3(0, half.y, 0));
33
+ const botVec3Array = Array<Vec3>();
34
+ for (let i = 0; i <= sides; i++) {
35
+ const currBot = vec3tr(vec3(0 + Math.sin(currentAngle) * c.radius, -half.y, 0 + Math.cos(currentAngle) * c.radius));
36
+ botVec3Array.push(currBot);
37
+ if (i !== 0) {
38
+ const prevBot = botVec3Array[i - 1]!;
39
+ const points = [point(currBot.x, currBot.y), point(prevBot.x, prevBot.y), point(topPos.x, topPos.y), point(topPos.x, topPos.y)];
40
+ zOrderComponents.push(
41
+ zElem(
42
+ svgPolygon(rot, points, fill, opacity, stroke, stBW),
43
+ vec3ZMean(currBot, prevBot, topPos),
44
+ [currBot, prevBot, topPos],
45
+ isOpaque(fill, opacity),
46
+ { fill, opacity, stroke, strokeWidth: stBW }
47
+ )
48
+ );
49
+ }
50
+ currentAngle += angleStep;
51
+ }
52
+
53
+ // Add circle if direcly facing camera
54
+ const cylTop = vec3tr(vec3(0, half.y, 0));
55
+ const cylBottom = vec3tr(vec3(0, -half.y, 0));
56
+ if (equals(cylTop.x, cylBottom.x, 0.1) && equals(cylTop.y, cylBottom.y, 0.1)) {
57
+ zOrderComponents.push(
58
+ zElem(
59
+ svgCircle(c.radius, rot, point(cylBottom.x, cylBottom.y), fill, opacity, stroke, stBW),
60
+ cylBottom.z,
61
+ botVec3Array,
62
+ isOpaque(fill, opacity)
63
+ )
64
+ );
65
+ }
66
+
67
+ return zOrderComponents;
68
+ }
@@ -0,0 +1,82 @@
1
+ import type { Cylinder, Vec2, Vec3, Material } from "../../../abstract-3d.js";
2
+ import { vec3, vec3TransRot, vec3RotCombine, vec3Zero, vec3ZMean, equals } from "../../../abstract-3d.js";
3
+ import { rgbGrayScale } from "../../../utils.js";
4
+ import { svgCircle, svgPolygon } from "../svg-encoding.js";
5
+ import type { SvgOptions, zOrderElement } from "./shared.js";
6
+ import { getArea, gray, isOpaque, stBW, transparent, zElem } from "./shared.js";
7
+
8
+ export function cylinder(
9
+ c: Cylinder,
10
+ point: (x: number, y: number) => Vec2,
11
+ material: Material,
12
+ opts: SvgOptions,
13
+ parentPos: Vec3,
14
+ parentRot: Vec3
15
+ ): ReadonlyArray<zOrderElement> {
16
+ const half = c.length / 2;
17
+ const radiusEnd = c.radiusEnd ?? c.radius;
18
+ const pos = vec3TransRot(c.pos, parentPos, parentRot);
19
+ const rot = vec3RotCombine(parentRot, c.rot ?? vec3Zero);
20
+ const vec3tr = (p: Vec3): Vec3 => vec3TransRot(p, pos, rot);
21
+
22
+ const color = material.normal;
23
+ const opacity = material.opacity ?? 1.0;
24
+ const [stroke, fill] = opts.only_stroke
25
+ ? [opts.gray_scale ? gray : color, opts.background]
26
+ : [transparent, opts.gray_scale ? rgbGrayScale(color) : color];
27
+ const opaque = isOpaque(fill, opacity);
28
+ const style = { fill, opacity, stroke, strokeWidth: stBW };
29
+ // A closed opaque cylinder's facets turned away are hidden by those toward the viewer and its near end
30
+ const cullBack = opaque && !c.open;
31
+ const zOrderComponents = Array<zOrderElement>();
32
+
33
+ const sides = 8;
34
+ const angleStep = (2 * Math.PI) / sides;
35
+ let currentAngle = 0;
36
+
37
+ const botVec3Array = Array<Vec3>();
38
+ const topVec3Array = Array<Vec3>();
39
+ for (let i = 0; i <= sides; i++) {
40
+ const sin = Math.sin(currentAngle);
41
+ const cos = Math.cos(currentAngle);
42
+ const currBot = vec3tr(vec3(sin * c.radius, -half, cos * c.radius));
43
+ const currTop = vec3tr(vec3(sin * radiusEnd, half, cos * radiusEnd));
44
+ botVec3Array.push(currBot);
45
+ topVec3Array.push(currTop);
46
+ const mid = currentAngle - angleStep / 2;
47
+ if (i !== 0 && !(cullBack && vec3TransRot(vec3(Math.sin(mid), 0, Math.cos(mid)), vec3Zero, rot).z < -1e-6)) {
48
+ const prevBot = botVec3Array[i - 1]!;
49
+ const prevTop = topVec3Array[i - 1]!;
50
+
51
+ const points = [point(currBot.x, currBot.y), point(prevBot.x, prevBot.y), point(prevTop.x, prevTop.y), point(currTop.x, currTop.y)];
52
+ const polygon = [currBot, prevBot, prevTop, currTop];
53
+ zOrderComponents.push(zElem(svgPolygon(rot, points, fill, opacity, stroke, stBW), vec3ZMean(...polygon), polygon, opaque, style));
54
+ }
55
+ currentAngle += angleStep;
56
+ }
57
+ if (!c.open) {
58
+ // The near end: a circle when directly facing the camera, else seen at an angle, its ring of facet corners
59
+ const circleTop = vec3tr(vec3(0, half, 0));
60
+ const circleBottom = vec3tr(vec3(0, -half, 0));
61
+ const [circlePos, circleRadius, ring] =
62
+ circleTop.z > circleBottom.z ? [circleTop, radiusEnd, topVec3Array] : [circleBottom, c.radius, botVec3Array];
63
+ if (equals(circleTop.x, circleBottom.x, 0.1) && equals(circleTop.y, circleBottom.y, 0.1)) {
64
+ const holes = radiusEnd === c.radius ? c.holes : undefined;
65
+ zOrderComponents.push(
66
+ zElem(
67
+ svgCircle(circleRadius, rot, point(circlePos.x, circlePos.y), fill, opacity, stroke, stBW, holes),
68
+ circlePos.z,
69
+ ring,
70
+ opaque && !holes?.length
71
+ )
72
+ );
73
+ } else {
74
+ const points = ring.map((p) => point(p.x, p.y));
75
+ // Not seen edge on, when it's only a line along the facets' edges
76
+ if (Math.abs(getArea(points)) >= 1e-6) {
77
+ zOrderComponents.push(zElem(svgPolygon(rot, points, fill, opacity, stroke, stBW), circlePos.z, ring, opaque && !c.holes?.length, style));
78
+ }
79
+ }
80
+ }
81
+ return zOrderComponents;
82
+ }