@skillpet/circuit 0.5.0 → 0.5.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 (333) hide show
  1. package/LICENSE +43 -0
  2. package/README.md +24 -30
  3. package/dist/circuit.bundle.js +0 -1
  4. package/dist/circuit.bundle.min.js +0 -1
  5. package/dist/circuit.esm.js +0 -1
  6. package/dist/config.d.ts +0 -1
  7. package/dist/container.d.ts +0 -1
  8. package/dist/drawing-gradient.d.ts +0 -1
  9. package/dist/drawing-stack.d.ts +0 -1
  10. package/dist/drawing-transition.d.ts +0 -1
  11. package/dist/drawing.d.ts +0 -1
  12. package/dist/element-label.d.ts +0 -1
  13. package/dist/element-style.d.ts +0 -1
  14. package/dist/element.d.ts +0 -1
  15. package/dist/element2term.d.ts +0 -1
  16. package/dist/elements/cables.d.ts +0 -1
  17. package/dist/elements/capacitor.d.ts +0 -1
  18. package/dist/elements/compound.d.ts +0 -1
  19. package/dist/elements/connectors.d.ts +0 -1
  20. package/dist/elements/crystal.d.ts +0 -1
  21. package/dist/elements/diode-more.d.ts +0 -1
  22. package/dist/elements/diode.d.ts +0 -1
  23. package/dist/elements/element-compound.d.ts +0 -1
  24. package/dist/elements/element-drawing.d.ts +0 -1
  25. package/dist/elements/ground.d.ts +0 -1
  26. package/dist/elements/ic.d.ts +0 -1
  27. package/dist/elements/image.d.ts +0 -1
  28. package/dist/elements/inductor.d.ts +0 -1
  29. package/dist/elements/line.d.ts +0 -1
  30. package/dist/elements/lines-arc-labels.d.ts +0 -1
  31. package/dist/elements/lines-elements.d.ts +0 -1
  32. package/dist/elements/misc-elements.d.ts +0 -1
  33. package/dist/elements/oneterm.d.ts +0 -1
  34. package/dist/elements/opamp.d.ts +0 -1
  35. package/dist/elements/outlets.d.ts +0 -1
  36. package/dist/elements/resistor-more.d.ts +0 -1
  37. package/dist/elements/resistor.d.ts +0 -1
  38. package/dist/elements/source-v.d.ts +0 -1
  39. package/dist/elements/sources.d.ts +0 -1
  40. package/dist/elements/switches.d.ts +0 -1
  41. package/dist/elements/transformer.d.ts +0 -1
  42. package/dist/elements/transistors.d.ts +0 -1
  43. package/dist/elements/tubes.d.ts +0 -1
  44. package/dist/elements/twoports.d.ts +0 -1
  45. package/dist/elements/twoterm-extras.d.ts +0 -1
  46. package/dist/elements/twoterm.d.ts +0 -1
  47. package/dist/geometry/point.d.ts +0 -1
  48. package/dist/geometry/roundcorners.d.ts +0 -1
  49. package/dist/geometry/transform.d.ts +0 -1
  50. package/dist/geometry/util.d.ts +0 -1
  51. package/dist/index.cjs +0 -1
  52. package/dist/index.d.ts +0 -1
  53. package/dist/json/anchor-ref.d.ts +0 -1
  54. package/dist/json/mount.d.ts +0 -1
  55. package/dist/json/registry.d.ts +0 -1
  56. package/dist/json/render.d.ts +0 -1
  57. package/dist/json/theme.d.ts +0 -1
  58. package/dist/json/types.d.ts +0 -1
  59. package/dist/json/validate.d.ts +0 -1
  60. package/dist/label-hint.d.ts +0 -1
  61. package/dist/logic/and.d.ts +0 -1
  62. package/dist/logic/bitfield.d.ts +0 -1
  63. package/dist/logic/buffer.d.ts +0 -1
  64. package/dist/logic/extras.d.ts +0 -1
  65. package/dist/logic/kmap.d.ts +0 -1
  66. package/dist/logic/or.d.ts +0 -1
  67. package/dist/logic/timing.d.ts +0 -1
  68. package/dist/logic/tristate.d.ts +0 -1
  69. package/dist/math-render.d.ts +0 -1
  70. package/dist/package-api.d.ts +0 -1
  71. package/dist/params.d.ts +0 -1
  72. package/dist/react/index.d.ts +0 -1
  73. package/dist/react/index.js +0 -1
  74. package/dist/segment.d.ts +0 -1
  75. package/dist/style.d.ts +0 -1
  76. package/dist/subsystems/dsp.d.ts +0 -1
  77. package/dist/subsystems/flow.d.ts +0 -1
  78. package/dist/subsystems/index.d.ts +0 -1
  79. package/dist/subsystems/parsing.d.ts +0 -1
  80. package/dist/subsystems/pictorial.d.ts +0 -1
  81. package/dist/svg/constants.d.ts +0 -1
  82. package/dist/svg/figure.d.ts +0 -1
  83. package/dist/svg/fmt.d.ts +0 -1
  84. package/dist/svg/svg-style.d.ts +0 -1
  85. package/dist/types.d.ts +0 -1
  86. package/dist/version.d.ts +0 -1
  87. package/dist/vue/index.d.ts +0 -1
  88. package/dist/vue/index.js +0 -1
  89. package/package.json +22 -9
  90. package/dist/circuit.bundle.js.map +0 -7
  91. package/dist/circuit.bundle.min.js.map +0 -7
  92. package/dist/circuit.esm.js.map +0 -7
  93. package/dist/config.d.ts.map +0 -1
  94. package/dist/config.js +0 -23
  95. package/dist/config.js.map +0 -1
  96. package/dist/container.d.ts.map +0 -1
  97. package/dist/container.js +0 -101
  98. package/dist/container.js.map +0 -1
  99. package/dist/drawing-gradient.d.ts.map +0 -1
  100. package/dist/drawing-gradient.js +0 -39
  101. package/dist/drawing-gradient.js.map +0 -1
  102. package/dist/drawing-stack.d.ts.map +0 -1
  103. package/dist/drawing-stack.js +0 -32
  104. package/dist/drawing-stack.js.map +0 -1
  105. package/dist/drawing-transition.d.ts.map +0 -1
  106. package/dist/drawing-transition.js +0 -181
  107. package/dist/drawing-transition.js.map +0 -1
  108. package/dist/drawing.d.ts.map +0 -1
  109. package/dist/drawing.js +0 -337
  110. package/dist/drawing.js.map +0 -1
  111. package/dist/element-label.d.ts.map +0 -1
  112. package/dist/element-label.js +0 -377
  113. package/dist/element-label.js.map +0 -1
  114. package/dist/element-style.d.ts.map +0 -1
  115. package/dist/element-style.js +0 -51
  116. package/dist/element-style.js.map +0 -1
  117. package/dist/element.d.ts.map +0 -1
  118. package/dist/element.js +0 -514
  119. package/dist/element.js.map +0 -1
  120. package/dist/element2term.d.ts.map +0 -1
  121. package/dist/element2term.js +0 -245
  122. package/dist/element2term.js.map +0 -1
  123. package/dist/elements/cables.d.ts.map +0 -1
  124. package/dist/elements/cables.js +0 -100
  125. package/dist/elements/cables.js.map +0 -1
  126. package/dist/elements/capacitor.d.ts.map +0 -1
  127. package/dist/elements/capacitor.js +0 -93
  128. package/dist/elements/capacitor.js.map +0 -1
  129. package/dist/elements/compound.d.ts.map +0 -1
  130. package/dist/elements/compound.js +0 -266
  131. package/dist/elements/compound.js.map +0 -1
  132. package/dist/elements/connectors.d.ts.map +0 -1
  133. package/dist/elements/connectors.js +0 -578
  134. package/dist/elements/connectors.js.map +0 -1
  135. package/dist/elements/crystal.d.ts.map +0 -1
  136. package/dist/elements/crystal.js +0 -31
  137. package/dist/elements/crystal.js.map +0 -1
  138. package/dist/elements/diode-more.d.ts.map +0 -1
  139. package/dist/elements/diode-more.js +0 -233
  140. package/dist/elements/diode-more.js.map +0 -1
  141. package/dist/elements/diode.d.ts.map +0 -1
  142. package/dist/elements/diode.js +0 -58
  143. package/dist/elements/diode.js.map +0 -1
  144. package/dist/elements/element-compound.d.ts.map +0 -1
  145. package/dist/elements/element-compound.js +0 -68
  146. package/dist/elements/element-compound.js.map +0 -1
  147. package/dist/elements/element-drawing.d.ts.map +0 -1
  148. package/dist/elements/element-drawing.js +0 -16
  149. package/dist/elements/element-drawing.js.map +0 -1
  150. package/dist/elements/ground.d.ts.map +0 -1
  151. package/dist/elements/ground.js +0 -30
  152. package/dist/elements/ground.js.map +0 -1
  153. package/dist/elements/ic.d.ts.map +0 -1
  154. package/dist/elements/ic.js +0 -816
  155. package/dist/elements/ic.js.map +0 -1
  156. package/dist/elements/image.d.ts.map +0 -1
  157. package/dist/elements/image.js +0 -27
  158. package/dist/elements/image.js.map +0 -1
  159. package/dist/elements/inductor.d.ts.map +0 -1
  160. package/dist/elements/inductor.js +0 -111
  161. package/dist/elements/inductor.js.map +0 -1
  162. package/dist/elements/line.d.ts.map +0 -1
  163. package/dist/elements/line.js +0 -14
  164. package/dist/elements/line.js.map +0 -1
  165. package/dist/elements/lines-arc-labels.d.ts.map +0 -1
  166. package/dist/elements/lines-arc-labels.js +0 -822
  167. package/dist/elements/lines-arc-labels.js.map +0 -1
  168. package/dist/elements/lines-elements.d.ts.map +0 -1
  169. package/dist/elements/lines-elements.js +0 -271
  170. package/dist/elements/lines-elements.js.map +0 -1
  171. package/dist/elements/misc-elements.d.ts.map +0 -1
  172. package/dist/elements/misc-elements.js +0 -160
  173. package/dist/elements/misc-elements.js.map +0 -1
  174. package/dist/elements/oneterm.d.ts.map +0 -1
  175. package/dist/elements/oneterm.js +0 -163
  176. package/dist/elements/oneterm.js.map +0 -1
  177. package/dist/elements/opamp.d.ts.map +0 -1
  178. package/dist/elements/opamp.js +0 -72
  179. package/dist/elements/opamp.js.map +0 -1
  180. package/dist/elements/outlets.d.ts.map +0 -1
  181. package/dist/elements/outlets.js +0 -285
  182. package/dist/elements/outlets.js.map +0 -1
  183. package/dist/elements/resistor-more.d.ts.map +0 -1
  184. package/dist/elements/resistor-more.js +0 -171
  185. package/dist/elements/resistor-more.js.map +0 -1
  186. package/dist/elements/resistor.d.ts.map +0 -1
  187. package/dist/elements/resistor.js +0 -22
  188. package/dist/elements/resistor.js.map +0 -1
  189. package/dist/elements/source-v.d.ts.map +0 -1
  190. package/dist/elements/source-v.js +0 -3
  191. package/dist/elements/source-v.js.map +0 -1
  192. package/dist/elements/sources.d.ts.map +0 -1
  193. package/dist/elements/sources.js +0 -519
  194. package/dist/elements/sources.js.map +0 -1
  195. package/dist/elements/switches.d.ts.map +0 -1
  196. package/dist/elements/switches.js +0 -328
  197. package/dist/elements/switches.js.map +0 -1
  198. package/dist/elements/transformer.d.ts.map +0 -1
  199. package/dist/elements/transformer.js +0 -184
  200. package/dist/elements/transformer.js.map +0 -1
  201. package/dist/elements/transistors.d.ts.map +0 -1
  202. package/dist/elements/transistors.js +0 -1009
  203. package/dist/elements/transistors.js.map +0 -1
  204. package/dist/elements/tubes.d.ts.map +0 -1
  205. package/dist/elements/tubes.js +0 -386
  206. package/dist/elements/tubes.js.map +0 -1
  207. package/dist/elements/twoports.d.ts.map +0 -1
  208. package/dist/elements/twoports.js +0 -231
  209. package/dist/elements/twoports.js.map +0 -1
  210. package/dist/elements/twoterm-extras.d.ts.map +0 -1
  211. package/dist/elements/twoterm-extras.js +0 -243
  212. package/dist/elements/twoterm-extras.js.map +0 -1
  213. package/dist/elements/twoterm.d.ts.map +0 -1
  214. package/dist/elements/twoterm.js +0 -4
  215. package/dist/elements/twoterm.js.map +0 -1
  216. package/dist/geometry/point.d.ts.map +0 -1
  217. package/dist/geometry/point.js +0 -99
  218. package/dist/geometry/point.js.map +0 -1
  219. package/dist/geometry/roundcorners.d.ts.map +0 -1
  220. package/dist/geometry/roundcorners.js +0 -64
  221. package/dist/geometry/roundcorners.js.map +0 -1
  222. package/dist/geometry/transform.d.ts.map +0 -1
  223. package/dist/geometry/transform.js +0 -33
  224. package/dist/geometry/transform.js.map +0 -1
  225. package/dist/geometry/util.d.ts.map +0 -1
  226. package/dist/geometry/util.js +0 -36
  227. package/dist/geometry/util.js.map +0 -1
  228. package/dist/index.cjs.map +0 -7
  229. package/dist/index.d.ts.map +0 -1
  230. package/dist/index.js +0 -68
  231. package/dist/index.js.map +0 -1
  232. package/dist/json/anchor-ref.d.ts.map +0 -1
  233. package/dist/json/anchor-ref.js +0 -35
  234. package/dist/json/anchor-ref.js.map +0 -1
  235. package/dist/json/mount.d.ts.map +0 -1
  236. package/dist/json/mount.js +0 -369
  237. package/dist/json/mount.js.map +0 -1
  238. package/dist/json/registry.d.ts.map +0 -1
  239. package/dist/json/registry.js +0 -323
  240. package/dist/json/registry.js.map +0 -1
  241. package/dist/json/render.d.ts.map +0 -1
  242. package/dist/json/render.js +0 -113
  243. package/dist/json/render.js.map +0 -1
  244. package/dist/json/theme.d.ts.map +0 -1
  245. package/dist/json/theme.js +0 -57
  246. package/dist/json/theme.js.map +0 -1
  247. package/dist/json/types.d.ts.map +0 -1
  248. package/dist/json/types.js +0 -2
  249. package/dist/json/types.js.map +0 -1
  250. package/dist/json/validate.d.ts.map +0 -1
  251. package/dist/json/validate.js +0 -77
  252. package/dist/json/validate.js.map +0 -1
  253. package/dist/label-hint.d.ts.map +0 -1
  254. package/dist/label-hint.js +0 -2
  255. package/dist/label-hint.js.map +0 -1
  256. package/dist/logic/and.d.ts.map +0 -1
  257. package/dist/logic/and.js +0 -63
  258. package/dist/logic/and.js.map +0 -1
  259. package/dist/logic/bitfield.d.ts.map +0 -1
  260. package/dist/logic/bitfield.js +0 -271
  261. package/dist/logic/bitfield.js.map +0 -1
  262. package/dist/logic/buffer.d.ts.map +0 -1
  263. package/dist/logic/buffer.js +0 -46
  264. package/dist/logic/buffer.js.map +0 -1
  265. package/dist/logic/extras.d.ts.map +0 -1
  266. package/dist/logic/extras.js +0 -211
  267. package/dist/logic/extras.js.map +0 -1
  268. package/dist/logic/kmap.d.ts.map +0 -1
  269. package/dist/logic/kmap.js +0 -163
  270. package/dist/logic/kmap.js.map +0 -1
  271. package/dist/logic/or.d.ts.map +0 -1
  272. package/dist/logic/or.js +0 -117
  273. package/dist/logic/or.js.map +0 -1
  274. package/dist/logic/timing.d.ts.map +0 -1
  275. package/dist/logic/timing.js +0 -791
  276. package/dist/logic/timing.js.map +0 -1
  277. package/dist/logic/tristate.d.ts.map +0 -1
  278. package/dist/logic/tristate.js +0 -48
  279. package/dist/logic/tristate.js.map +0 -1
  280. package/dist/math-render.d.ts.map +0 -1
  281. package/dist/math-render.js +0 -53
  282. package/dist/math-render.js.map +0 -1
  283. package/dist/package-api.d.ts.map +0 -1
  284. package/dist/package-api.js +0 -75
  285. package/dist/package-api.js.map +0 -1
  286. package/dist/params.d.ts.map +0 -1
  287. package/dist/params.js +0 -35
  288. package/dist/params.js.map +0 -1
  289. package/dist/react/index.d.ts.map +0 -1
  290. package/dist/react/index.js.map +0 -1
  291. package/dist/schemdraw-ts.bundle.js +0 -30448
  292. package/dist/schemdraw-ts.bundle.js.map +0 -7
  293. package/dist/segment.d.ts.map +0 -1
  294. package/dist/segment.js +0 -844
  295. package/dist/segment.js.map +0 -1
  296. package/dist/style.d.ts.map +0 -1
  297. package/dist/style.js +0 -230
  298. package/dist/style.js.map +0 -1
  299. package/dist/subsystems/dsp.d.ts.map +0 -1
  300. package/dist/subsystems/dsp.js +0 -267
  301. package/dist/subsystems/dsp.js.map +0 -1
  302. package/dist/subsystems/flow.d.ts.map +0 -1
  303. package/dist/subsystems/flow.js +0 -315
  304. package/dist/subsystems/flow.js.map +0 -1
  305. package/dist/subsystems/index.d.ts.map +0 -1
  306. package/dist/subsystems/index.js +0 -22
  307. package/dist/subsystems/index.js.map +0 -1
  308. package/dist/subsystems/parsing.d.ts.map +0 -1
  309. package/dist/subsystems/parsing.js +0 -456
  310. package/dist/subsystems/parsing.js.map +0 -1
  311. package/dist/subsystems/pictorial.d.ts.map +0 -1
  312. package/dist/subsystems/pictorial.js +0 -469
  313. package/dist/subsystems/pictorial.js.map +0 -1
  314. package/dist/svg/constants.d.ts.map +0 -1
  315. package/dist/svg/constants.js +0 -5
  316. package/dist/svg/constants.js.map +0 -1
  317. package/dist/svg/figure.d.ts.map +0 -1
  318. package/dist/svg/figure.js +0 -634
  319. package/dist/svg/figure.js.map +0 -1
  320. package/dist/svg/fmt.d.ts.map +0 -1
  321. package/dist/svg/fmt.js +0 -7
  322. package/dist/svg/fmt.js.map +0 -1
  323. package/dist/svg/svg-style.d.ts.map +0 -1
  324. package/dist/svg/svg-style.js +0 -46
  325. package/dist/svg/svg-style.js.map +0 -1
  326. package/dist/types.d.ts.map +0 -1
  327. package/dist/types.js +0 -21
  328. package/dist/types.js.map +0 -1
  329. package/dist/version.d.ts.map +0 -1
  330. package/dist/version.js +0 -3
  331. package/dist/version.js.map +0 -1
  332. package/dist/vue/index.d.ts.map +0 -1
  333. package/dist/vue/index.js.map +0 -1
@@ -1,634 +0,0 @@
1
- import { LINE_WIDTH, PT_PER_IN } from "./constants.js";
2
- import { fmt } from "./fmt.js";
3
- import { getSvgStyle } from "./svg-style.js";
4
- import { Point } from "../geometry/point.js";
5
- // Character-width tables adapted from Python `svgtext.string_width`.
6
- // Values are in milliinches per character at 12pt reference size.
7
- const SANS_WIDTHS = {};
8
- for (const ch of "lij|' ")
9
- SANS_WIDTHS[ch] = 37;
10
- for (const ch of "![]fI.,:;/\\t")
11
- SANS_WIDTHS[ch] = 50;
12
- for (const ch of '`-(){}r"')
13
- SANS_WIDTHS[ch] = 60;
14
- for (const ch of "*^zcsJkvxy\u03BC\u00B0")
15
- SANS_WIDTHS[ch] = 85;
16
- for (const ch of "aebdhnopqug#$L+<>=?_~FZT\u03B10123456789")
17
- SANS_WIDTHS[ch] = 95;
18
- for (const ch of "BSPEAKVXY&UwNRCHD")
19
- SANS_WIDTHS[ch] = 112;
20
- for (const ch of "QGOMm%@\u03A9")
21
- SANS_WIDTHS[ch] = 140;
22
- for (const ch of "W\u2220")
23
- SANS_WIDTHS[ch] = 155;
24
- const SERIF_WIDTHS = {};
25
- for (const ch of "lij:.,;t")
26
- SERIF_WIDTHS[ch] = 47;
27
- for (const ch of "|")
28
- SERIF_WIDTHS[ch] = 37;
29
- for (const ch of "![]fI/\\")
30
- SERIF_WIDTHS[ch] = 55;
31
- for (const ch of "`-(){}r")
32
- SERIF_WIDTHS[ch] = 60;
33
- for (const ch of "sJ\u00B0")
34
- SERIF_WIDTHS[ch] = 68;
35
- for (const ch of '"zcae?1')
36
- SERIF_WIDTHS[ch] = 74;
37
- for (const ch of "*^kvxy\u03BCbdhnopqug#$_\u03B10123456789")
38
- SERIF_WIDTHS[ch] = 85;
39
- for (const ch of "#$+<>=~FSP")
40
- SERIF_WIDTHS[ch] = 95;
41
- for (const ch of "ELZT")
42
- SERIF_WIDTHS[ch] = 105;
43
- for (const ch of "BRC")
44
- SERIF_WIDTHS[ch] = 112;
45
- for (const ch of "DAwHUKVXYNQGO")
46
- SERIF_WIDTHS[ch] = 122;
47
- for (const ch of "&m\u03A9")
48
- SERIF_WIDTHS[ch] = 130;
49
- for (const ch of "%")
50
- SERIF_WIDTHS[ch] = 140;
51
- for (const ch of "MW@\u2220")
52
- SERIF_WIDTHS[ch] = 155;
53
- function isSerifFont(font) {
54
- if (!font)
55
- return false;
56
- const lc = font.toLowerCase();
57
- return (lc.includes("times") || (lc.includes("serif") && !lc.includes("sans")));
58
- }
59
- function stringWidth(text, fontsize, font) {
60
- const table = isSerifFont(font) ? SERIF_WIDTHS : SANS_WIDTHS;
61
- const fallback = isSerifFont(font) ? 60 : 75;
62
- let mil = 0;
63
- for (const ch of text) {
64
- mil += table[ch] ?? fallback;
65
- }
66
- return (mil * 72) / 1000.0 * (fontsize / 12);
67
- }
68
- /**
69
- * Estimate text dimensions using per-character width lookup (aligned with
70
- * Python `svgtext.text_approx_size`). Returns width and height in points.
71
- */
72
- export function textSize(text, fontsize, font) {
73
- if (text === "")
74
- return { width: 0, height: 0 };
75
- const lines = text.split("\n");
76
- let w = 0;
77
- for (const line of lines) {
78
- w = Math.max(w, stringWidth(line, fontsize, font));
79
- }
80
- return { width: w, height: lines.length * fontsize };
81
- }
82
- function escapeXml(s) {
83
- return s
84
- .replace(/&/g, "&amp;")
85
- .replace(/</g, "&lt;")
86
- .replace(/>/g, "&gt;")
87
- .replace(/"/g, "&quot;");
88
- }
89
- export class SvgFigure {
90
- static totalClips = 0;
91
- bbox;
92
- scale;
93
- pxwidth;
94
- pxheight;
95
- elements = [];
96
- backgroundColor;
97
- showbbox;
98
- extraDefs;
99
- svgDefs = [];
100
- clips = new Map();
101
- _groupStack = [];
102
- _hasGroups = false;
103
- constructor(bbox, opts = {}) {
104
- const inchesPerUnit = opts.inchesPerUnit ?? 0.5;
105
- const margin = (opts.margin ?? 0.1) + LINE_WIDTH / PT_PER_IN;
106
- this.scale = PT_PER_IN * inchesPerUnit;
107
- this.showbbox = opts.showbbox ?? false;
108
- this.bbox = {
109
- xmin: bbox.xmin - margin,
110
- ymin: bbox.ymin - margin,
111
- xmax: bbox.xmax + margin,
112
- ymax: bbox.ymax + margin,
113
- };
114
- const w = this.bbox.xmax - this.bbox.xmin;
115
- const h = this.bbox.ymax - this.bbox.ymin;
116
- this.pxwidth = Math.max(5, w * this.scale);
117
- this.pxheight = Math.max(5, h * this.scale);
118
- this.extraDefs = opts.extraDefs ?? "";
119
- }
120
- setBbox(bbox) {
121
- const margin = 0.1 + LINE_WIDTH / PT_PER_IN;
122
- this.bbox = {
123
- xmin: bbox.xmin - margin,
124
- ymin: bbox.ymin - margin,
125
- xmax: bbox.xmax + margin,
126
- ymax: bbox.ymax + margin,
127
- };
128
- const w = this.bbox.xmax - this.bbox.xmin;
129
- const h = this.bbox.ymax - this.bbox.ymin;
130
- this.pxwidth = Math.max(5, w * this.scale);
131
- this.pxheight = Math.max(5, h * this.scale);
132
- }
133
- /** User coords → SVG px (y-up → y-down). */
134
- xform(x, y) {
135
- return [x * this.scale, -y * this.scale];
136
- }
137
- bgcolor(color) {
138
- this.backgroundColor = color;
139
- }
140
- /** Create an SVG hatch `<pattern>` def, returning the pattern id for `fill="url(#id)"`. */
141
- hatchpattern(name = "hatch", color = "black", linewidth = 0.5) {
142
- const pat = `<pattern id="${name}" patternUnits="userSpaceOnUse" width="4" height="4">`
143
- + `<path d="M-1,1 l2,-2 M0,4 l4,-4 M3,5 l2,-2" style="stroke:${color}; stroke-width:${linewidth}" />`
144
- + `</pattern>`;
145
- this.svgDefs.push(pat);
146
- return name;
147
- }
148
- /** Create an SVG `<clipPath>` def from raw path data. */
149
- addclip(clipId, pathData) {
150
- const clip = `<clipPath id="${clipId}"><path d="${pathData}" /></clipPath>`;
151
- this.svgDefs.push(clip);
152
- }
153
- /** Attach a clip-path to an element by BBox (aligned with Python `Figure.addclip`). */
154
- addclipBbox(bboxClip) {
155
- const key = `${bboxClip.xmin},${bboxClip.ymin},${bboxClip.xmax},${bboxClip.ymax}`;
156
- let clipid;
157
- if (this.clips.has(key)) {
158
- clipid = this.clips.get(key);
159
- }
160
- else {
161
- clipid = SvgFigure.totalClips++;
162
- this.clips.set(key, clipid);
163
- const [x0, y0] = this.xform(bboxClip.xmin, bboxClip.ymin);
164
- const [x1, y1] = this.xform(bboxClip.xmax, bboxClip.ymax);
165
- const rx = Math.min(x0, x1) - 1;
166
- const ry = Math.min(y0, y1) - 1;
167
- const rw = Math.abs(x1 - x0) + 2;
168
- const rh = Math.abs(y1 - y0) + 2;
169
- this.svgDefs.push(`<clipPath id="clip${clipid}"><rect x="${fmt(rx)}" y="${fmt(ry)}" width="${fmt(rw)}" height="${fmt(rh)}" /></clipPath>`);
170
- }
171
- return `url(#clip${clipid})`;
172
- }
173
- plot(xs, ys, style = {}) {
174
- const z = style.zorder ?? 2;
175
- const color = style.color ?? "black";
176
- const ls = style.ls ?? "-";
177
- const lw = style.lw ?? 2;
178
- const cap = (style.capstyle ?? "round");
179
- const join = (style.joinstyle ?? "round");
180
- const fill = typeof style.fill === "string" || style.fill === undefined ? style.fill ?? "none" : "none";
181
- let d = "";
182
- const [mx0, my0] = this.xform(xs[0], ys[0]);
183
- d += `M ${fmt(mx0)} ${fmt(my0)} `;
184
- for (let i = 1; i < xs.length; i++) {
185
- if (Number.isNaN(xs[i]) || Number.isNaN(ys[i])) {
186
- d += "M ";
187
- continue;
188
- }
189
- if (!d.endsWith("M ")) {
190
- d += "L ";
191
- }
192
- const [xx, yy] = this.xform(xs[i], ys[i]);
193
- d += `${fmt(xx)},${fmt(yy)} `;
194
- }
195
- d = d.trim();
196
- const st = getSvgStyle({
197
- color,
198
- ls: ls,
199
- lw,
200
- capstyle: cap,
201
- joinstyle: join,
202
- fill: fill,
203
- });
204
- this.elements.push({ z, xml: `<path d="${d}" style="${escapeXml(st)}"/>` });
205
- }
206
- circle(cx, cy, radius, style = {}) {
207
- const z = style.zorder ?? 1;
208
- const [x, y] = this.xform(cx, cy);
209
- const r = radius * this.scale;
210
- const rawFill = style.fill;
211
- const fillNorm = rawFill === undefined || rawFill === null ? "none" : rawFill;
212
- const st = getSvgStyle({
213
- color: style.color ?? "black",
214
- ls: style.ls ?? "-",
215
- lw: style.lw ?? 2,
216
- fill: fillNorm,
217
- });
218
- this.elements.push({
219
- z,
220
- xml: `<circle cx="${fmt(x)}" cy="${fmt(y)}" r="${fmt(r)}" style="${escapeXml(st)}"/>`,
221
- });
222
- }
223
- /** Arrowhead at user-space `xy`, `thetaDeg` is line direction in degrees (see Python `Figure.arrow`). */
224
- arrow(xy, thetaDeg, opts = {}) {
225
- const arrowwidth = opts.arrowwidth ?? 0.15;
226
- const arrowlength = opts.arrowlength ?? 0.25;
227
- const color = opts.color ?? "black";
228
- const lw = opts.lw ?? 1;
229
- const z = opts.zorder ?? 1;
230
- const [x, y] = this.xform(xy[0], xy[1]);
231
- const dx = (arrowlength / 2) * Math.cos((thetaDeg * Math.PI) / 180) * this.scale;
232
- const dy = (arrowlength / 2) * Math.sin((thetaDeg * Math.PI) / 180) * this.scale;
233
- const aw = arrowwidth * this.scale;
234
- const al = arrowlength * this.scale;
235
- const fullen = Math.hypot(dx, dy);
236
- const thetaRot = (-Math.atan2(dy, dx) * 180) / Math.PI;
237
- const tail = new Point(x - dx, y + dy);
238
- const fin1 = new Point(fullen - al, aw / 2).rotate(thetaRot, [0, 0]).add(tail);
239
- const fin2 = new Point(fullen - al, -aw / 2).rotate(thetaRot, [0, 0]).add(tail);
240
- const headAdj = new Point(x - lw * 2 * Math.cos((thetaRot * Math.PI) / 180), y - lw * 2 * Math.sin((thetaRot * Math.PI) / 180));
241
- const st = getSvgStyle({
242
- color,
243
- lw: 0,
244
- capstyle: "butt",
245
- joinstyle: "miter",
246
- fill: color,
247
- });
248
- const d = `M ${fmt(headAdj.x)} ${fmt(headAdj.y)} L ${fmt(fin1.x)} ${fmt(fin1.y)} L ${fmt(fin2.x)} ${fmt(fin2.y)} Z`;
249
- this.elements.push({ z, xml: `<path d="${d}" style="${escapeXml(st)}"/>` });
250
- }
251
- text(s, x, y, opts = {}) {
252
- if (s === "") {
253
- return;
254
- }
255
- const z = opts.zorder ?? 3;
256
- let [x0, y0] = this.xform(x, y);
257
- const fontsize = opts.fontsize ?? 14;
258
- let font = opts.fontfamily ?? "sans-serif";
259
- if (font.toLowerCase() === "sans-serif" || font.toLowerCase() === "arial") {
260
- font = "sans";
261
- }
262
- const valign = opts.valign ?? "center";
263
- if (valign === "base") {
264
- const nlines = s.split("\n").length;
265
- y0 -= (nlines - 1) * fontsize;
266
- }
267
- const anchor = opts.halign === "left" ? "start" : opts.halign === "right" ? "end" : "middle";
268
- const baseline = valign === "top"
269
- ? "hanging"
270
- : valign === "bottom"
271
- ? "ideographic"
272
- : "central";
273
- const rot = opts.rotation ?? 0;
274
- const transform = rot !== 0 ? ` transform="rotate(${fmt(rot)} ${fmt(x0)} ${fmt(y0)})"` : "";
275
- const color = opts.color ?? "black";
276
- const textY = y0 - fontsize;
277
- let textContent;
278
- if (opts.textDecoration) {
279
- textContent = `<tspan text-decoration="${opts.textDecoration}">${escapeXml(s)}</tspan>`;
280
- }
281
- else {
282
- textContent = escapeXml(s);
283
- }
284
- let inner = `<text x="${fmt(x0)}" y="${fmt(textY)}" dominant-baseline="${baseline}" fill="${escapeXml(color)}" font-size="${fmt(fontsize)}" font-family="${escapeXml(font)}" text-anchor="${anchor}"${transform}><tspan x="${fmt(x0)}" dy="${fmt(fontsize)}" dominant-baseline="${baseline}">${textContent}</tspan></text>`;
285
- if (opts.href) {
286
- inner = `<a href="${escapeXml(opts.href)}">${inner}</a>`;
287
- }
288
- this.elements.push({ z, xml: inner });
289
- }
290
- /** Polygon / polyline — aligned with Python `Figure.poly`. */
291
- poly(verts, opts = {}) {
292
- const closed = opts.closed ?? true;
293
- const z = opts.zorder ?? 1;
294
- const color = opts.color ?? "black";
295
- const lw = opts.lw ?? 2;
296
- const ls = opts.ls ?? "-";
297
- const cap = (opts.capstyle ?? "round");
298
- const join = (opts.joinstyle ?? "round");
299
- let fill = typeof opts.fill === "string" ? opts.fill : "none";
300
- const points = verts
301
- .map((p) => {
302
- const [px, py] = this.xform(p.x, p.y);
303
- return `${fmt(px)},${fmt(py)}`;
304
- })
305
- .join(" ");
306
- const tag = closed ? "polygon" : "polyline";
307
- const st = getSvgStyle({
308
- color,
309
- ls,
310
- lw,
311
- capstyle: cap,
312
- joinstyle: join,
313
- fill: fill,
314
- hatch: opts.hatch,
315
- });
316
- this.elements.push({ z, xml: `<${tag} points="${points}" style="${escapeXml(st)}"/>` });
317
- }
318
- /** Cubic (4 points) or quadratic (3 points) Bézier — aligned with Python `Figure.bezier`. */
319
- bezier(pts, opts = {}) {
320
- if (pts.length !== 3 && pts.length !== 4) {
321
- throw new Error("bezier expects 3 (quadratic) or 4 (cubic) control points");
322
- }
323
- const z = opts.zorder ?? 1;
324
- const color = opts.color ?? "black";
325
- const lw = opts.lw ?? 2;
326
- const ls = opts.ls ?? "-";
327
- const cap = (opts.capstyle ?? "round");
328
- const order = pts.length === 4 ? "C" : "Q";
329
- const arrow = opts.arrow;
330
- const alen = opts.arrowlength ?? 0.25;
331
- const aw = opts.arrowwidth ?? 0.15;
332
- const adjusted = pts.map((p) => ({ x: p.x, y: p.y }));
333
- if (arrow != null) {
334
- if (arrow.includes("<")) {
335
- const th1 = Math.atan2(pts[0].y - pts[1].y, pts[0].x - pts[1].x);
336
- adjusted[0] = {
337
- x: pts[0].x - Math.cos(th1) * alen / 2,
338
- y: pts[0].y - Math.sin(th1) * alen / 2,
339
- };
340
- }
341
- if (arrow.includes(">")) {
342
- const last = pts.length - 1;
343
- const prev = pts.length - 2;
344
- const th2 = Math.atan2(pts[last].y - pts[prev].y, pts[last].x - pts[prev].x);
345
- adjusted[last] = {
346
- x: pts[last].x - Math.cos(th2) * alen / 2,
347
- y: pts[last].y - Math.sin(th2) * alen / 2,
348
- };
349
- }
350
- }
351
- const lpoints = adjusted.map((p) => {
352
- const [px, py] = this.xform(p.x, p.y);
353
- return { x: px, y: py };
354
- });
355
- let d = `M ${fmt(lpoints[0].x)} ${fmt(lpoints[0].y)} ${order}`;
356
- for (let i = 1; i < lpoints.length; i++) {
357
- d += ` ${fmt(lpoints[i].x)} ${fmt(lpoints[i].y)}`;
358
- }
359
- const st = getSvgStyle({
360
- color,
361
- ls: ls,
362
- lw,
363
- capstyle: cap,
364
- fill: "none",
365
- });
366
- this.elements.push({ z, xml: `<path d="${d}" style="${escapeXml(st)}"/>` });
367
- if (arrow != null) {
368
- if (arrow.includes("<")) {
369
- const dx = pts[0].x - pts[1].x;
370
- const dy = pts[0].y - pts[1].y;
371
- const th = (Math.atan2(dy, dx) * 180) / Math.PI;
372
- this.arrow([pts[0].x, pts[0].y], th, { color, lw: 1, zorder: z, arrowlength: alen, arrowwidth: aw });
373
- }
374
- else if (arrow.startsWith("o")) {
375
- this.circle(pts[0].x, pts[0].y, aw / 2, { color, fill: color, lw: 0, zorder: z });
376
- }
377
- if (arrow.includes(">")) {
378
- const a = pts.length - 1;
379
- const b = pts.length - 2;
380
- const dx = pts[a].x - pts[b].x;
381
- const dy = pts[a].y - pts[b].y;
382
- const th = (Math.atan2(dy, dx) * 180) / Math.PI;
383
- this.arrow([pts[a].x, pts[a].y], th, { color, lw: 1, zorder: z, arrowlength: alen, arrowwidth: aw });
384
- }
385
- else if (arrow.endsWith("o")) {
386
- const last = pts.length - 1;
387
- this.circle(pts[last].x, pts[last].y, aw / 2, { color, fill: color, lw: 0, zorder: z });
388
- }
389
- }
390
- }
391
- /** Elliptical arc — aligned with Python `Figure.arc` (subset: no arrow on arc). */
392
- arc(center, width, height, opts = {}) {
393
- const z = opts.zorder ?? 1;
394
- const color = opts.color ?? "black";
395
- const lw = opts.lw ?? 2;
396
- const ls = opts.ls ?? "-";
397
- let fill = opts.fill ?? undefined;
398
- if (fill === null || fill === undefined) {
399
- fill = "none";
400
- }
401
- const theta1 = opts.theta1 ?? 0;
402
- const theta2 = opts.theta2 ?? 90;
403
- let angle = opts.angle ?? 0;
404
- const [centerx, centery] = this.xform(center[0], center[1]);
405
- let w = width * this.scale;
406
- let h = height * this.scale;
407
- angle = -angle;
408
- let t1 = (-theta1 * Math.PI) / 180;
409
- let t2 = (-theta2 * Math.PI) / 180;
410
- const anglerad = (angle * Math.PI) / 180;
411
- t1 = Math.atan2(w * Math.sin(t1), h * Math.cos(t1));
412
- t2 = Math.atan2(w * Math.sin(t2), h * Math.cos(t2));
413
- while (t1 < t2) {
414
- t1 += 2 * Math.PI;
415
- }
416
- const startx = centerx +
417
- (w / 2) * Math.cos(t2) * Math.cos(anglerad) -
418
- (h / 2) * Math.sin(t2) * Math.sin(anglerad);
419
- const starty = centery +
420
- (w / 2) * Math.cos(t2) * Math.sin(anglerad) +
421
- (h / 2) * Math.sin(t2) * Math.cos(anglerad);
422
- const endx = centerx +
423
- (w / 2) * Math.cos(t1) * Math.cos(anglerad) -
424
- (h / 2) * Math.sin(t1) * Math.sin(anglerad);
425
- const endy = centery +
426
- (w / 2) * Math.cos(t1) * Math.sin(anglerad) +
427
- (h / 2) * Math.sin(t1) * Math.cos(anglerad);
428
- const dx = endx - startx;
429
- const dy = endy - starty;
430
- const st = getSvgStyle({
431
- color,
432
- ls: ls,
433
- lw,
434
- fill: fill,
435
- });
436
- if (Math.abs(dx) < 0.1 && Math.abs(dy) < 0.1) {
437
- const rotDeg = -(opts.angle ?? 0);
438
- const tr = rotDeg !== 0
439
- ? ` transform="rotate(${fmt(rotDeg)} ${fmt(centerx)} ${fmt(centery)})"`
440
- : "";
441
- this.elements.push({
442
- z,
443
- xml: `<ellipse cx="${fmt(centerx)}" cy="${fmt(centery)}" rx="${fmt(w / 2)}" ry="${fmt(h / 2)}"${tr} style="${escapeXml(st)}"/>`,
444
- });
445
- }
446
- else {
447
- const flags = Math.abs(t2 - t1) >= Math.PI ? "1 1" : "0 1";
448
- const d = `M ${fmt(startx)} ${fmt(starty)} a ${fmt(w / 2)} ${fmt(h / 2)} ${fmt(angle)} ${flags} ${fmt(dx)} ${fmt(dy)}`;
449
- this.elements.push({ z, xml: `<path d="${d}" style="${escapeXml(st)}"/>` });
450
- }
451
- if (opts.arrow) {
452
- const arrowWidth = opts.arrowwidth ?? 0.15;
453
- const arrowLength = opts.arrowlength ?? 0.25;
454
- const uAngle = opts.angle ?? 0;
455
- const uT1 = opts.theta1 ?? 0;
456
- const uT2 = opts.theta2 ?? 90;
457
- const ar = uAngle * Math.PI / 180;
458
- const xc2 = Math.cos(uT2 * Math.PI / 180);
459
- const yc2 = Math.sin(uT2 * Math.PI / 180);
460
- const th2 = Math.atan2((width / height) * yc2, xc2) * 180 / Math.PI;
461
- const xc1 = Math.cos(uT1 * Math.PI / 180);
462
- const yc1 = Math.sin(uT1 * Math.PI / 180);
463
- const th1 = Math.atan2((width / height) * yc1, xc1) * 180 / Math.PI;
464
- const rotPt = (px, py) => {
465
- if (uAngle === 0)
466
- return [px, py];
467
- const rx = px - center[0], ry = py - center[1];
468
- return [center[0] + rx * Math.cos(ar) - ry * Math.sin(ar),
469
- center[1] + rx * Math.sin(ar) + ry * Math.cos(ar)];
470
- };
471
- const rotVec = (vx, vy) => {
472
- if (uAngle === 0)
473
- return [vx, vy];
474
- return [vx * Math.cos(ar) - vy * Math.sin(ar),
475
- vx * Math.sin(ar) + vy * Math.cos(ar)];
476
- };
477
- const dir = opts.arrow;
478
- if (dir === "ccw" || dir === "end" || dir === "both") {
479
- const adx = Math.cos((th2 + 90) * Math.PI / 180) * arrowLength;
480
- const ady = Math.sin((th2 + 90) * Math.PI / 180) * arrowLength;
481
- const ep = rotPt(center[0] + width / 2 * Math.cos(th2 * Math.PI / 180), center[1] + height / 2 * Math.sin(th2 * Math.PI / 180));
482
- const da = rotVec(adx, ady);
483
- const theta = Math.atan2(da[1], da[0]) * 180 / Math.PI;
484
- this.arrow([ep[0] + da[0], ep[1] + da[1]], theta, {
485
- arrowwidth: arrowWidth, arrowlength: arrowLength, color, lw: 1, zorder: z,
486
- });
487
- }
488
- if (dir === "cw" || dir === "start" || dir === "both") {
489
- const adx = -Math.cos((th1 + 90) * Math.PI / 180) * arrowLength;
490
- const ady = -Math.sin((th1 + 90) * Math.PI / 180) * arrowLength;
491
- const ep = rotPt(center[0] + width / 2 * Math.cos(th1 * Math.PI / 180), center[1] + height / 2 * Math.sin(th1 * Math.PI / 180));
492
- const da = rotVec(adx, ady);
493
- const theta = Math.atan2(da[1], da[0]) * 180 / Math.PI;
494
- this.arrow([ep[0] + da[0], ep[1] + da[1]], theta, {
495
- arrowwidth: arrowWidth, arrowlength: arrowLength, color, lw: 1, zorder: z,
496
- });
497
- }
498
- }
499
- }
500
- /** Raw SVG path `d` fragments — aligned with Python `Figure.path`. */
501
- path(parts, opts = {}) {
502
- const z = opts.zorder ?? 1;
503
- const color = opts.color ?? "black";
504
- const lw = opts.lw ?? 2;
505
- const ls = opts.ls ?? "-";
506
- const cap = (opts.capstyle ?? "round");
507
- const join = (opts.joinstyle ?? "round");
508
- const fill = opts.fill ?? "none";
509
- const dstrs = [];
510
- for (const p of parts) {
511
- if (typeof p === "string") {
512
- dstrs.push(p);
513
- }
514
- else {
515
- const [px, py] = this.xform(p.x, p.y);
516
- dstrs.push(fmt(px), fmt(py));
517
- }
518
- }
519
- const st = getSvgStyle({
520
- color,
521
- ls: ls,
522
- lw,
523
- capstyle: cap,
524
- joinstyle: join,
525
- fill,
526
- });
527
- this.elements.push({ z, xml: `<path d="${dstrs.join(" ")}" style="${escapeXml(st)}"/>` });
528
- }
529
- image(imageData, xy, width, height, opts = {}) {
530
- const z = opts.zorder ?? 1;
531
- const rotate = opts.rotate ?? 0;
532
- const w = width * this.scale;
533
- const h = height * this.scale;
534
- const [x0, y0raw] = this.xform(xy[0], xy[1]);
535
- const y0 = y0raw - h;
536
- const href = imageData.startsWith("data:")
537
- ? imageData
538
- : `data:image/${opts.imgfmt ?? "png"};base64,${imageData}`;
539
- const rotAttr = rotate !== 0
540
- ? ` transform="rotate(${fmt(-rotate)} ${fmt(x0)} ${fmt(y0 + h)})"`
541
- : "";
542
- this.elements.push({
543
- z,
544
- xml: `<image x="${fmt(x0)}" y="${fmt(y0)}" width="${fmt(w)}" height="${fmt(h)}" href="${escapeXml(href)}"${rotAttr}/>`,
545
- });
546
- }
547
- mathText(html, x, y, opts) {
548
- const z = opts.zorder ?? 3;
549
- const [x0, y0] = this.xform(x, y);
550
- const fontsize = opts.fontsize ?? 14;
551
- const color = opts.color ?? "black";
552
- const w = opts.width;
553
- const h = opts.height;
554
- const halign = opts.halign ?? "center";
555
- const valign = opts.valign ?? "center";
556
- let foX = x0;
557
- let foY = y0;
558
- if (halign === "center")
559
- foX -= w / 2;
560
- else if (halign === "right")
561
- foX -= w;
562
- if (valign === "center")
563
- foY -= h / 2;
564
- else if (valign === "bottom")
565
- foY -= h;
566
- const rot = opts.rotation ?? 0;
567
- const transform = rot !== 0 ? ` transform="rotate(${fmt(rot)} ${fmt(x0)} ${fmt(y0)})"` : "";
568
- const justify = halign === "left" ? "flex-start" : halign === "right" ? "flex-end" : "center";
569
- const align = valign === "top" ? "flex-start" : valign === "bottom" ? "flex-end" : "center";
570
- const fo = `<foreignObject x="${fmt(foX)}" y="${fmt(foY)}" width="${fmt(w)}" height="${fmt(h)}"${transform}>` +
571
- `<div xmlns="http://www.w3.org/1999/xhtml" style="color:${escapeXml(color)};font-size:${fontsize}px;` +
572
- `display:flex;align-items:${align};justify-content:${justify};width:100%;height:100%;">` +
573
- html +
574
- `</div></foreignObject>`;
575
- this.elements.push({ z, xml: fo });
576
- }
577
- /** Begin an SVG `<g>` group; elements added after this call will be wrapped on `endGroup()`. */
578
- beginGroup(attrs, title) {
579
- this._hasGroups = true;
580
- this._groupStack.push({ startIndex: this.elements.length, attrs, title });
581
- }
582
- /** End the most recent `<g>` group opened by `beginGroup()`. */
583
- endGroup() {
584
- const frame = this._groupStack.pop();
585
- if (!frame)
586
- throw new Error("endGroup called without matching beginGroup");
587
- const grouped = this.elements.splice(frame.startIndex);
588
- if (grouped.length === 0) {
589
- const attrStr = Object.entries(frame.attrs)
590
- .map(([k, v]) => `${k}="${escapeXml(v)}"`)
591
- .join(" ");
592
- const titleTag = frame.title ? `<title>${escapeXml(frame.title)}</title>` : "";
593
- this.elements.push({ z: 0, xml: `<g ${attrStr}>${titleTag}</g>` });
594
- return;
595
- }
596
- let minZ = grouped[0].z;
597
- for (let i = 1; i < grouped.length; i++) {
598
- if (grouped[i].z < minZ)
599
- minZ = grouped[i].z;
600
- }
601
- const innerXml = grouped.map((e) => e.xml).join("");
602
- const attrStr = Object.entries(frame.attrs)
603
- .map(([k, v]) => `${k}="${escapeXml(v)}"`)
604
- .join(" ");
605
- const titleTag = frame.title ? `<title>${escapeXml(frame.title)}</title>` : "";
606
- this.elements.push({ z: minZ, xml: `<g ${attrStr}>${titleTag}${innerXml}</g>` });
607
- }
608
- /** Sorted SVG children inner XML. */
609
- toSvgInnerXml() {
610
- return [...this.elements]
611
- .sort((a, b) => a.z - b.z)
612
- .map((e) => e.xml)
613
- .join("");
614
- }
615
- toSvgString() {
616
- const x0 = this.bbox.xmin * this.scale;
617
- const y0 = -this.bbox.ymax * this.scale;
618
- const w = this.pxwidth;
619
- const h = this.pxheight;
620
- const bg = this.backgroundColor ? ` style="background-color:${escapeXml(this.backgroundColor)}"` : "";
621
- let allDefs = this.extraDefs + this.svgDefs.join("");
622
- if (this._hasGroups) {
623
- allDefs += '<style>'
624
- + '.sd-el{cursor:pointer;transition:filter .15s ease}'
625
- + '.sd-el:hover path,.sd-el:hover polyline,.sd-el:hover polygon,.sd-el:hover circle,.sd-el:hover ellipse{filter:drop-shadow(0 0 3px rgba(66,133,244,.6))}'
626
- + '.sd-el.sd-selected path,.sd-el.sd-selected polyline,.sd-el.sd-selected polygon,.sd-el.sd-selected circle,.sd-el.sd-selected ellipse{stroke:#4285f4;filter:drop-shadow(0 0 4px rgba(66,133,244,.8))}'
627
- + '</style>';
628
- }
629
- const defs = allDefs ? `<defs>${allDefs}</defs>` : "";
630
- const inner = this.toSvgInnerXml();
631
- return `<?xml version="1.0" encoding="UTF-8"?>\n<svg xmlns="http://www.w3.org/2000/svg" width="${fmt(w)}pt" height="${fmt(h)}pt" viewBox="${fmt(x0)} ${fmt(y0)} ${fmt(w)} ${fmt(h)}"${bg}>${defs}${inner}</svg>`;
632
- }
633
- }
634
- //# sourceMappingURL=figure.js.map