@adecore/drawing 0.23.1 → 0.24.1

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.
@@ -15,12 +15,12 @@ export declare function boundsOf(element: Pick<DrawingElement, 'x' | 'y' | 'w' |
15
15
  export declare function centerOf(rect: Rect): Point;
16
16
  export declare function roundPoint(point: Point): Point;
17
17
  export declare function rotatePoint(point: Point, around: Point, angle: number): Point;
18
- export declare function toLocal(element: DrawingElement, point: Point): Point;
19
18
  export declare function unionOf(rects: readonly Rect[]): Rect | null;
20
19
  export declare function boundsOfElements(elements: readonly DrawingElement[]): Rect | null;
20
+ export declare function drawnBoundsOf(element: DrawingElement): Rect;
21
+ export declare function drawnBoundsOfElements(elements: readonly DrawingElement[]): Rect | null;
21
22
  export declare function intersects(a: Rect, b: Rect): boolean;
22
23
  export declare function rectFromPoints(from: Point, to: Point): Rect;
23
- export declare function distanceToSegment(point: Point, a: Point, b: Point): number;
24
24
  export declare function absolutePoints(element: DrawingElement): Point[];
25
25
  export declare function hitsElement(element: DrawingElement, point: Point, tolerance: number): boolean;
26
26
  export declare function elementAt(elements: readonly DrawingElement[], point: Point, tolerance: number): DrawingElement | undefined;
package/dist/geometry.js CHANGED
@@ -20,7 +20,7 @@ export function rotatePoint(point, around, angle) {
20
20
  return { x: around.x + dx * cos - dy * sin, y: around.y + dx * sin + dy * cos };
21
21
  }
22
22
  /* A point in the element's own frame, so every hit test can pretend nothing is turned. */
23
- export function toLocal(element, point) {
23
+ function toLocal(element, point) {
24
24
  return rotatePoint(point, centerOf(boundsOf(element)), -(element.angle ?? 0));
25
25
  }
26
26
  export function unionOf(rects) {
@@ -42,6 +42,29 @@ export function unionOf(rects) {
42
42
  export function boundsOfElements(elements) {
43
43
  return unionOf(elements.map(boundsOf));
44
44
  }
45
+ /* The box around an element as it is drawn: the stored box, turned by its angle. */
46
+ export function drawnBoundsOf(element) {
47
+ const rect = boundsOf(element);
48
+ const angle = element.angle ?? 0;
49
+ if (angle === 0) {
50
+ return rect;
51
+ }
52
+ const center = centerOf(rect);
53
+ const corners = [
54
+ { x: rect.x, y: rect.y },
55
+ { x: rect.x + rect.w, y: rect.y },
56
+ { x: rect.x + rect.w, y: rect.y + rect.h },
57
+ { x: rect.x, y: rect.y + rect.h }
58
+ ].map((corner) => rotatePoint(corner, center, angle));
59
+ const xs = corners.map((corner) => corner.x);
60
+ const ys = corners.map((corner) => corner.y);
61
+ const left = Math.min(...xs);
62
+ const top = Math.min(...ys);
63
+ return { x: left, y: top, w: Math.max(...xs) - left, h: Math.max(...ys) - top };
64
+ }
65
+ export function drawnBoundsOfElements(elements) {
66
+ return unionOf(elements.map(drawnBoundsOf));
67
+ }
45
68
  export function intersects(a, b) {
46
69
  return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
47
70
  }
@@ -53,7 +76,7 @@ export function rectFromPoints(from, to) {
53
76
  h: Math.abs(to.y - from.y)
54
77
  };
55
78
  }
56
- export function distanceToSegment(point, a, b) {
79
+ function distanceToSegment(point, a, b) {
57
80
  const dx = b.x - a.x;
58
81
  const dy = b.y - a.y;
59
82
  const length = dx * dx + dy * dy;
@@ -154,7 +177,7 @@ export function elementAt(elements, point, tolerance) {
154
177
  return undefined;
155
178
  }
156
179
  export function elementsIn(elements, rect) {
157
- return elements.filter((element) => !element.locked && intersects(boundsOf(element), rect));
180
+ return elements.filter((element) => !element.locked && intersects(drawnBoundsOf(element), rect));
158
181
  }
159
182
  export function handlePoint(rect, handle) {
160
183
  const x = handle.includes('w') ? rect.x : handle.includes('e') ? rect.x + rect.w : rect.x + rect.w / 2;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { type Point, type Rect, RESIZE_HANDLES, type ResizeHandle, boundsOf, centerOf, roundPoint, rotatePoint, toLocal, unionOf, boundsOfElements, intersects, rectFromPoints, distanceToSegment, absolutePoints, hitsElement, elementAt, elementsIn, handlePoint, resizeRect, scaleElement, arrowHead, arrowHeadSize } from './geometry.ts';
2
- export { type ElementPath, outlineToPath, freehandOutline, pathsOfElement } from './paths.ts';
1
+ export { type Point, type Rect, RESIZE_HANDLES, type ResizeHandle, boundsOf, centerOf, roundPoint, rotatePoint, unionOf, boundsOfElements, drawnBoundsOf, drawnBoundsOfElements, intersects, rectFromPoints, absolutePoints, elementAt, elementsIn, handlePoint, resizeRect, scaleElement } from './geometry.ts';
2
+ export { type ElementPath, pathsOfElement } from './paths.ts';
3
3
  export { readingOrder } from './reading-order.ts';
4
- export { type SvgOptions, DEFAULT_SVG_MARGIN, DEFAULT_PALETTE, DEFAULT_PAPER, DEFAULT_EDGE, toSvg } from './svg.ts';
5
- export { LINE_HEIGHT, DEFAULT_FONT_STACKS, type WrittenElement, NOTE_PADDING, NOTE_RADIUS, writingFrameOf, textLines, type MeasureLine, approximateMeasure, wrapLines, linesOf, fontOf } from './text.ts';
4
+ export { type SvgOptions, DEFAULT_SVG_MARGIN, DEFAULT_PALETTE, DEFAULT_PAPER, DEFAULT_EDGE, escapeXml, toSvg } from './svg.ts';
5
+ export { LINE_HEIGHT, DEFAULT_FONT_STACKS, type WrittenElement, NOTE_PADDING, writingFrameOf, type MeasureLine, approximateMeasure, linesOf, fontOf } from './text.ts';
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- export { RESIZE_HANDLES, boundsOf, centerOf, roundPoint, rotatePoint, toLocal, unionOf, boundsOfElements, intersects, rectFromPoints, distanceToSegment, absolutePoints, hitsElement, elementAt, elementsIn, handlePoint, resizeRect, scaleElement, arrowHead, arrowHeadSize } from './geometry.js';
2
- export { outlineToPath, freehandOutline, pathsOfElement } from './paths.js';
1
+ export { RESIZE_HANDLES, boundsOf, centerOf, roundPoint, rotatePoint, unionOf, boundsOfElements, drawnBoundsOf, drawnBoundsOfElements, intersects, rectFromPoints, absolutePoints, elementAt, elementsIn, handlePoint, resizeRect, scaleElement } from './geometry.js';
2
+ export { pathsOfElement } from './paths.js';
3
3
  export { readingOrder } from './reading-order.js';
4
- export { DEFAULT_SVG_MARGIN, DEFAULT_PALETTE, DEFAULT_PAPER, DEFAULT_EDGE, toSvg } from './svg.js';
5
- export { LINE_HEIGHT, DEFAULT_FONT_STACKS, NOTE_PADDING, NOTE_RADIUS, writingFrameOf, textLines, approximateMeasure, wrapLines, linesOf, fontOf } from './text.js';
4
+ export { DEFAULT_SVG_MARGIN, DEFAULT_PALETTE, DEFAULT_PAPER, DEFAULT_EDGE, escapeXml, toSvg } from './svg.js';
5
+ export { LINE_HEIGHT, DEFAULT_FONT_STACKS, NOTE_PADDING, writingFrameOf, approximateMeasure, linesOf, fontOf } from './text.js';
package/dist/paths.d.ts CHANGED
@@ -5,8 +5,4 @@ export interface ElementPath {
5
5
  strokeWidth: number;
6
6
  dash: number[] | null;
7
7
  }
8
- export declare function outlineToPath(outline: readonly (readonly number[])[]): string;
9
- export declare function freehandOutline(element: DrawingElement & {
10
- kind: 'freehand';
11
- }): string;
12
8
  export declare function pathsOfElement(element: DrawingElement): ElementPath[];
package/dist/paths.js CHANGED
@@ -46,14 +46,14 @@ function roundedRectPath(w, h, radius) {
46
46
  const corner = Math.min(radius, w / 2, h / 2);
47
47
  return `M ${corner} 0 L ${w - corner} 0 Q ${w} 0 ${w} ${corner} L ${w} ${h - corner} Q ${w} ${h} ${w - corner} ${h} L ${corner} ${h} Q 0 ${h} 0 ${h - corner} L 0 ${corner} Q 0 0 ${corner} 0 Z`;
48
48
  }
49
- export function outlineToPath(outline) {
49
+ function outlineToPath(outline) {
50
50
  if (outline.length === 0) {
51
51
  return '';
52
52
  }
53
53
  const [first, ...rest] = outline;
54
54
  return `M ${first[0]} ${first[1]} ${rest.map((point) => `L ${point[0]} ${point[1]}`).join(' ')} Z`;
55
55
  }
56
- export function freehandOutline(element) {
56
+ function freehandOutline(element) {
57
57
  const points = element.points.map(([x, y, pressure]) => [x, y, pressure ?? 0.5]);
58
58
  const outline = getStroke(points, {
59
59
  size: element.strokeWidth * 4 + 2,
package/dist/svg.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { DrawingColor, DrawingElement, DrawingFont } from './protocol.ts';
2
2
  import { type MeasureLine, type WrittenElement } from './text.ts';
3
3
  export interface SvgOptions {
4
- palette: Record<DrawingColor, string>;
4
+ palette?: Record<DrawingColor, string>;
5
5
  background?: string | null;
6
6
  margin?: number;
7
7
  fonts?: Partial<Record<DrawingFont, string>>;
@@ -13,4 +13,5 @@ export declare const DEFAULT_SVG_MARGIN = 32;
13
13
  export declare const DEFAULT_PALETTE: Record<DrawingColor, string>;
14
14
  export declare const DEFAULT_PAPER: Record<DrawingColor, string>;
15
15
  export declare const DEFAULT_EDGE: Record<DrawingColor, string>;
16
- export declare function toSvg(elements: readonly DrawingElement[], options: SvgOptions): string;
16
+ export declare function escapeXml(value: string): string;
17
+ export declare function toSvg(elements: readonly DrawingElement[], options?: SvgOptions): string;
package/dist/svg.js CHANGED
@@ -1,4 +1,4 @@
1
- import { boundsOfElements } from './geometry.js';
1
+ import { drawnBoundsOfElements } from './geometry.js';
2
2
  import { pathsOfElement } from './paths.js';
3
3
  import { DEFAULT_FONT_STACKS, LINE_HEIGHT, approximateMeasure, fontOf, linesOf, writingFrameOf } from './text.js';
4
4
  export const DEFAULT_SVG_MARGIN = 32;
@@ -44,7 +44,7 @@ export const DEFAULT_EDGE = {
44
44
  // XML has no way to write a control character other than tab and the line breaks, not even escaped.
45
45
  // oxlint-disable-next-line no-control-regex
46
46
  const NOT_XML = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]/g;
47
- function escapeXml(value) {
47
+ export function escapeXml(value) {
48
48
  return value.replace(NOT_XML, '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;');
49
49
  }
50
50
  function round(value) {
@@ -95,14 +95,15 @@ function elementSvg(element, options) {
95
95
  * The whole drawing as one SVG, in the colors of the theme it is exported from. Fonts are named,
96
96
  * never embedded: a file that opens outside the app falls back to a system face.
97
97
  */
98
- export function toSvg(elements, options) {
98
+ export function toSvg(elements, options = {}) {
99
+ const paint = { ...options, palette: options.palette ?? DEFAULT_PALETTE };
99
100
  const margin = options.margin ?? DEFAULT_SVG_MARGIN;
100
- const bounds = boundsOfElements(elements) ?? { x: 0, y: 0, w: 1, h: 1 };
101
+ const bounds = drawnBoundsOfElements(elements) ?? { x: 0, y: 0, w: 1, h: 1 };
101
102
  const x = round(bounds.x - margin);
102
103
  const y = round(bounds.y - margin);
103
104
  const w = round(bounds.w + margin * 2);
104
105
  const h = round(bounds.h + margin * 2);
105
106
  const paper = options.background ? `<rect x="${x}" y="${y}" width="${w}" height="${h}" fill="${options.background}"/>` : '';
106
- const body = elements.map((element) => elementSvg(element, options)).join('');
107
+ const body = elements.map((element) => elementSvg(element, paint)).join('');
107
108
  return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="${x} ${y} ${w} ${h}">${paper}${body}</svg>`;
108
109
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adecore/drawing",
3
- "version": "0.23.1",
3
+ "version": "0.24.1",
4
4
  "description": "Drawing schemas, geometry, deterministic paths, SVG export and reading order.",
5
5
  "license": "FSL-1.1-MIT",
6
6
  "author": "Bas Milius",
@@ -19,6 +19,7 @@
19
19
  "dist",
20
20
  "src",
21
21
  "!src/**/*.test.ts",
22
+ "!src/**/__snapshots__",
22
23
  "!src/fixtures"
23
24
  ],
24
25
  "exports": {
package/src/geometry.ts CHANGED
@@ -40,7 +40,7 @@ export function rotatePoint(point: Point, around: Point, angle: number): Point {
40
40
  }
41
41
 
42
42
  /* A point in the element's own frame, so every hit test can pretend nothing is turned. */
43
- export function toLocal(element: DrawingElement, point: Point): Point {
43
+ function toLocal(element: DrawingElement, point: Point): Point {
44
44
  return rotatePoint(point, centerOf(boundsOf(element)), -(element.angle ?? 0));
45
45
  }
46
46
 
@@ -65,6 +65,31 @@ export function boundsOfElements(elements: readonly DrawingElement[]): Rect | nu
65
65
  return unionOf(elements.map(boundsOf));
66
66
  }
67
67
 
68
+ /* The box around an element as it is drawn: the stored box, turned by its angle. */
69
+ export function drawnBoundsOf(element: DrawingElement): Rect {
70
+ const rect = boundsOf(element);
71
+ const angle = element.angle ?? 0;
72
+ if (angle === 0) {
73
+ return rect;
74
+ }
75
+ const center = centerOf(rect);
76
+ const corners = [
77
+ { x: rect.x, y: rect.y },
78
+ { x: rect.x + rect.w, y: rect.y },
79
+ { x: rect.x + rect.w, y: rect.y + rect.h },
80
+ { x: rect.x, y: rect.y + rect.h }
81
+ ].map((corner) => rotatePoint(corner, center, angle));
82
+ const xs = corners.map((corner) => corner.x);
83
+ const ys = corners.map((corner) => corner.y);
84
+ const left = Math.min(...xs);
85
+ const top = Math.min(...ys);
86
+ return { x: left, y: top, w: Math.max(...xs) - left, h: Math.max(...ys) - top };
87
+ }
88
+
89
+ export function drawnBoundsOfElements(elements: readonly DrawingElement[]): Rect | null {
90
+ return unionOf(elements.map(drawnBoundsOf));
91
+ }
92
+
68
93
  export function intersects(a: Rect, b: Rect): boolean {
69
94
  return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
70
95
  }
@@ -78,7 +103,7 @@ export function rectFromPoints(from: Point, to: Point): Rect {
78
103
  };
79
104
  }
80
105
 
81
- export function distanceToSegment(point: Point, a: Point, b: Point): number {
106
+ function distanceToSegment(point: Point, a: Point, b: Point): number {
82
107
  const dx = b.x - a.x;
83
108
  const dy = b.y - a.y;
84
109
  const length = dx * dx + dy * dy;
@@ -189,7 +214,7 @@ export function elementAt(elements: readonly DrawingElement[], point: Point, tol
189
214
  }
190
215
 
191
216
  export function elementsIn(elements: readonly DrawingElement[], rect: Rect): DrawingElement[] {
192
- return elements.filter((element) => !element.locked && intersects(boundsOf(element), rect));
217
+ return elements.filter((element) => !element.locked && intersects(drawnBoundsOf(element), rect));
193
218
  }
194
219
 
195
220
  export function handlePoint(rect: Rect, handle: ResizeHandle): Point {
package/src/index.ts CHANGED
@@ -7,36 +7,30 @@ export {
7
7
  centerOf,
8
8
  roundPoint,
9
9
  rotatePoint,
10
- toLocal,
11
10
  unionOf,
12
11
  boundsOfElements,
12
+ drawnBoundsOf,
13
+ drawnBoundsOfElements,
13
14
  intersects,
14
15
  rectFromPoints,
15
- distanceToSegment,
16
16
  absolutePoints,
17
- hitsElement,
18
17
  elementAt,
19
18
  elementsIn,
20
19
  handlePoint,
21
20
  resizeRect,
22
- scaleElement,
23
- arrowHead,
24
- arrowHeadSize
21
+ scaleElement
25
22
  } from './geometry.ts';
26
- export { type ElementPath, outlineToPath, freehandOutline, pathsOfElement } from './paths.ts';
23
+ export { type ElementPath, pathsOfElement } from './paths.ts';
27
24
  export { readingOrder } from './reading-order.ts';
28
- export { type SvgOptions, DEFAULT_SVG_MARGIN, DEFAULT_PALETTE, DEFAULT_PAPER, DEFAULT_EDGE, toSvg } from './svg.ts';
25
+ export { type SvgOptions, DEFAULT_SVG_MARGIN, DEFAULT_PALETTE, DEFAULT_PAPER, DEFAULT_EDGE, escapeXml, toSvg } from './svg.ts';
29
26
  export {
30
27
  LINE_HEIGHT,
31
28
  DEFAULT_FONT_STACKS,
32
29
  type WrittenElement,
33
30
  NOTE_PADDING,
34
- NOTE_RADIUS,
35
31
  writingFrameOf,
36
- textLines,
37
32
  type MeasureLine,
38
33
  approximateMeasure,
39
- wrapLines,
40
34
  linesOf,
41
35
  fontOf
42
36
  } from './text.ts';
package/src/paths.ts CHANGED
@@ -70,7 +70,7 @@ function roundedRectPath(w: number, h: number, radius: number): string {
70
70
  return `M ${corner} 0 L ${w - corner} 0 Q ${w} 0 ${w} ${corner} L ${w} ${h - corner} Q ${w} ${h} ${w - corner} ${h} L ${corner} ${h} Q 0 ${h} 0 ${h - corner} L 0 ${corner} Q 0 0 ${corner} 0 Z`;
71
71
  }
72
72
 
73
- export function outlineToPath(outline: readonly (readonly number[])[]): string {
73
+ function outlineToPath(outline: readonly (readonly number[])[]): string {
74
74
  if (outline.length === 0) {
75
75
  return '';
76
76
  }
@@ -78,7 +78,7 @@ export function outlineToPath(outline: readonly (readonly number[])[]): string {
78
78
  return `M ${first![0]} ${first![1]} ${rest.map((point) => `L ${point[0]} ${point[1]}`).join(' ')} Z`;
79
79
  }
80
80
 
81
- export function freehandOutline(element: DrawingElement & { kind: 'freehand' }): string {
81
+ function freehandOutline(element: DrawingElement & { kind: 'freehand' }): string {
82
82
  const points = element.points.map(([x, y, pressure]) => [x, y, pressure ?? 0.5] as [number, number, number]);
83
83
  const outline = getStroke(points, {
84
84
  size: element.strokeWidth * 4 + 2,
package/src/svg.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import type { DrawingColor, DrawingElement, DrawingFont } from './protocol.ts';
2
- import { boundsOfElements, type Rect } from './geometry.ts';
2
+ import { drawnBoundsOfElements, type Rect } from './geometry.ts';
3
3
  import { pathsOfElement, type ElementPath } from './paths.ts';
4
4
  import { DEFAULT_FONT_STACKS, LINE_HEIGHT, approximateMeasure, fontOf, linesOf, writingFrameOf, type MeasureLine, type WrittenElement } from './text.ts';
5
5
 
6
6
  export interface SvgOptions {
7
- /* What every palette name is in the theme this export is made in. */
8
- palette: Record<DrawingColor, string>;
7
+ /* What every palette name is in the theme this export is made in; without it the light theme's colors are used. */
8
+ palette?: Record<DrawingColor, string>;
9
9
  /* The paper behind the drawing, or null for a transparent one. */
10
10
  background?: string | null;
11
11
  /* World units around the drawing, so nothing touches the edge. */
@@ -67,10 +67,12 @@ export const DEFAULT_EDGE: Record<DrawingColor, string> = {
67
67
  // oxlint-disable-next-line no-control-regex
68
68
  const NOT_XML = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]/g;
69
69
 
70
- function escapeXml(value: string): string {
70
+ export function escapeXml(value: string): string {
71
71
  return value.replace(NOT_XML, '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;');
72
72
  }
73
73
 
74
+ type PaintOptions = SvgOptions & Required<Pick<SvgOptions, 'palette'>>;
75
+
74
76
  function round(value: number): number {
75
77
  return Math.round(value * 100) / 100;
76
78
  }
@@ -81,7 +83,7 @@ function transformOf(element: DrawingElement): string {
81
83
  return `translate(${round(element.x)} ${round(element.y)})${turn}`;
82
84
  }
83
85
 
84
- function textSvg(element: WrittenElement, options: SvgOptions): string {
86
+ function textSvg(element: WrittenElement, options: PaintOptions): string {
85
87
  const family = options.fonts?.[fontOf(element.font)] ?? DEFAULT_FONT_STACKS[fontOf(element.font)];
86
88
  const anchor = element.align === 'center' ? 'middle' : element.align === 'right' ? 'end' : 'start';
87
89
  const frame = writingFrameOf(element);
@@ -93,7 +95,7 @@ function textSvg(element: WrittenElement, options: SvgOptions): string {
93
95
  return `<text font-family="${escapeXml(family)}" font-size="${element.size}" fill="${options.palette[element.stroke]}" text-anchor="${anchor}">${lines}</text>`;
94
96
  }
95
97
 
96
- function pathSvg(path: ElementPath, element: DrawingElement, options: SvgOptions): string {
98
+ function pathSvg(path: ElementPath, element: DrawingElement, options: PaintOptions): string {
97
99
  const note = element.kind === 'note';
98
100
  if (path.role === 'stroke') {
99
101
  const dash = path.dash ? ` stroke-dasharray="${path.dash.join(' ')}"` : '';
@@ -109,7 +111,7 @@ function pathSvg(path: ElementPath, element: DrawingElement, options: SvgOptions
109
111
  : `<path d="${path.d}" fill="${color}" stroke="none"/>`;
110
112
  }
111
113
 
112
- function elementSvg(element: DrawingElement, options: SvgOptions): string {
114
+ function elementSvg(element: DrawingElement, options: PaintOptions): string {
113
115
  const body =
114
116
  element.kind === 'text'
115
117
  ? textSvg(element, options)
@@ -125,14 +127,15 @@ function elementSvg(element: DrawingElement, options: SvgOptions): string {
125
127
  * The whole drawing as one SVG, in the colors of the theme it is exported from. Fonts are named,
126
128
  * never embedded: a file that opens outside the app falls back to a system face.
127
129
  */
128
- export function toSvg(elements: readonly DrawingElement[], options: SvgOptions): string {
130
+ export function toSvg(elements: readonly DrawingElement[], options: SvgOptions = {}): string {
131
+ const paint: PaintOptions = { ...options, palette: options.palette ?? DEFAULT_PALETTE };
129
132
  const margin = options.margin ?? DEFAULT_SVG_MARGIN;
130
- const bounds: Rect = boundsOfElements(elements) ?? { x: 0, y: 0, w: 1, h: 1 };
133
+ const bounds: Rect = drawnBoundsOfElements(elements) ?? { x: 0, y: 0, w: 1, h: 1 };
131
134
  const x = round(bounds.x - margin);
132
135
  const y = round(bounds.y - margin);
133
136
  const w = round(bounds.w + margin * 2);
134
137
  const h = round(bounds.h + margin * 2);
135
138
  const paper = options.background ? `<rect x="${x}" y="${y}" width="${w}" height="${h}" fill="${options.background}"/>` : '';
136
- const body = elements.map((element) => elementSvg(element, options)).join('');
139
+ const body = elements.map((element) => elementSvg(element, paint)).join('');
137
140
  return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="${x} ${y} ${w} ${h}">${paper}${body}</svg>`;
138
141
  }