js-draw 0.0.10 → 0.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.
- package/CHANGELOG.md +11 -0
- package/dist/bundle.js +1 -1
- package/dist/src/Editor.d.ts +2 -2
- package/dist/src/Editor.js +17 -7
- package/dist/src/EditorImage.d.ts +15 -7
- package/dist/src/EditorImage.js +46 -37
- package/dist/src/Pointer.d.ts +3 -2
- package/dist/src/Pointer.js +12 -3
- package/dist/src/SVGLoader.d.ts +6 -2
- package/dist/src/SVGLoader.js +20 -8
- package/dist/src/Viewport.d.ts +4 -0
- package/dist/src/Viewport.js +51 -0
- package/dist/src/components/AbstractComponent.d.ts +9 -2
- package/dist/src/components/AbstractComponent.js +14 -0
- package/dist/src/components/SVGGlobalAttributesObject.d.ts +1 -1
- package/dist/src/components/SVGGlobalAttributesObject.js +1 -1
- package/dist/src/components/Stroke.d.ts +1 -1
- package/dist/src/components/Stroke.js +1 -1
- package/dist/src/components/UnknownSVGObject.d.ts +1 -1
- package/dist/src/components/UnknownSVGObject.js +1 -1
- package/dist/src/components/builders/ArrowBuilder.d.ts +1 -1
- package/dist/src/components/builders/FreehandLineBuilder.d.ts +1 -1
- package/dist/src/components/builders/FreehandLineBuilder.js +1 -1
- package/dist/src/components/builders/LineBuilder.d.ts +1 -1
- package/dist/src/components/builders/RectangleBuilder.d.ts +1 -1
- package/dist/src/components/builders/types.d.ts +1 -1
- package/dist/src/geometry/Mat33.js +3 -0
- package/dist/src/geometry/Path.d.ts +1 -1
- package/dist/src/geometry/Path.js +102 -69
- package/dist/src/geometry/Rect2.d.ts +1 -0
- package/dist/src/geometry/Rect2.js +47 -9
- package/dist/src/{Display.d.ts → rendering/Display.d.ts} +5 -2
- package/dist/src/{Display.js → rendering/Display.js} +34 -4
- package/dist/src/rendering/caching/CacheRecord.d.ts +19 -0
- package/dist/src/rendering/caching/CacheRecord.js +52 -0
- package/dist/src/rendering/caching/CacheRecordManager.d.ts +11 -0
- package/dist/src/rendering/caching/CacheRecordManager.js +31 -0
- package/dist/src/rendering/caching/RenderingCache.d.ts +12 -0
- package/dist/src/rendering/caching/RenderingCache.js +42 -0
- package/dist/src/rendering/caching/RenderingCacheNode.d.ts +28 -0
- package/dist/src/rendering/caching/RenderingCacheNode.js +301 -0
- package/dist/src/rendering/caching/testUtils.d.ts +9 -0
- package/dist/src/rendering/caching/testUtils.js +20 -0
- package/dist/src/rendering/caching/types.d.ts +21 -0
- package/dist/src/rendering/caching/types.js +1 -0
- package/dist/src/rendering/{AbstractRenderer.d.ts → renderers/AbstractRenderer.d.ts} +20 -9
- package/dist/src/rendering/{AbstractRenderer.js → renderers/AbstractRenderer.js} +37 -3
- package/dist/src/rendering/{CanvasRenderer.d.ts → renderers/CanvasRenderer.d.ts} +10 -5
- package/dist/src/rendering/{CanvasRenderer.js → renderers/CanvasRenderer.js} +60 -20
- package/dist/src/rendering/{DummyRenderer.d.ts → renderers/DummyRenderer.d.ts} +9 -5
- package/dist/src/rendering/{DummyRenderer.js → renderers/DummyRenderer.js} +35 -4
- package/dist/src/rendering/{SVGRenderer.d.ts → renderers/SVGRenderer.d.ts} +7 -5
- package/dist/src/rendering/{SVGRenderer.js → renderers/SVGRenderer.js} +35 -18
- package/dist/src/testing/createEditor.js +1 -1
- package/dist/src/toolbar/HTMLToolbar.d.ts +2 -1
- package/dist/src/toolbar/HTMLToolbar.js +165 -154
- package/dist/src/toolbar/icons.d.ts +10 -0
- package/dist/src/toolbar/icons.js +180 -0
- package/dist/src/toolbar/localization.d.ts +4 -1
- package/dist/src/toolbar/localization.js +4 -1
- package/dist/src/toolbar/types.d.ts +4 -0
- package/dist/src/tools/PanZoom.d.ts +9 -6
- package/dist/src/tools/PanZoom.js +30 -21
- package/dist/src/tools/Pen.js +8 -3
- package/dist/src/tools/SelectionTool.js +9 -24
- package/dist/src/tools/ToolController.d.ts +5 -6
- package/dist/src/tools/ToolController.js +8 -10
- package/dist/src/tools/localization.d.ts +1 -0
- package/dist/src/tools/localization.js +1 -0
- package/dist/src/types.d.ts +2 -1
- package/package.json +1 -1
- package/src/Editor.ts +19 -8
- package/src/EditorImage.test.ts +2 -2
- package/src/EditorImage.ts +58 -42
- package/src/Pointer.ts +13 -4
- package/src/SVGLoader.ts +36 -10
- package/src/Viewport.ts +68 -0
- package/src/components/AbstractComponent.ts +21 -2
- package/src/components/SVGGlobalAttributesObject.ts +2 -2
- package/src/components/Stroke.ts +2 -2
- package/src/components/UnknownSVGObject.ts +2 -2
- package/src/components/builders/ArrowBuilder.ts +1 -1
- package/src/components/builders/FreehandLineBuilder.ts +2 -2
- package/src/components/builders/LineBuilder.ts +1 -1
- package/src/components/builders/RectangleBuilder.ts +1 -1
- package/src/components/builders/types.ts +1 -1
- package/src/geometry/Mat33.ts +3 -0
- package/src/geometry/Path.fromString.test.ts +94 -4
- package/src/geometry/Path.toString.test.ts +12 -2
- package/src/geometry/Path.ts +107 -71
- package/src/geometry/Rect2.test.ts +47 -8
- package/src/geometry/Rect2.ts +57 -9
- package/src/{Display.ts → rendering/Display.ts} +39 -6
- package/src/rendering/caching/CacheRecord.test.ts +49 -0
- package/src/rendering/caching/CacheRecord.ts +73 -0
- package/src/rendering/caching/CacheRecordManager.ts +45 -0
- package/src/rendering/caching/RenderingCache.test.ts +44 -0
- package/src/rendering/caching/RenderingCache.ts +63 -0
- package/src/rendering/caching/RenderingCacheNode.ts +378 -0
- package/src/rendering/caching/testUtils.ts +35 -0
- package/src/rendering/caching/types.ts +39 -0
- package/src/rendering/{AbstractRenderer.ts → renderers/AbstractRenderer.ts} +57 -9
- package/src/rendering/{CanvasRenderer.ts → renderers/CanvasRenderer.ts} +74 -25
- package/src/rendering/renderers/DummyRenderer.test.ts +43 -0
- package/src/rendering/{DummyRenderer.ts → renderers/DummyRenderer.ts} +50 -7
- package/src/rendering/{SVGRenderer.ts → renderers/SVGRenderer.ts} +39 -23
- package/src/testing/createEditor.ts +1 -1
- package/src/toolbar/HTMLToolbar.ts +199 -170
- package/src/toolbar/icons.ts +203 -0
- package/src/toolbar/localization.ts +9 -2
- package/src/toolbar/toolbar.css +21 -8
- package/src/toolbar/types.ts +5 -0
- package/src/tools/PanZoom.ts +37 -27
- package/src/tools/Pen.ts +7 -3
- package/src/tools/SelectionTool.test.ts +1 -1
- package/src/tools/SelectionTool.ts +12 -33
- package/src/tools/ToolController.ts +3 -5
- package/src/tools/localization.ts +2 -0
- package/src/types.ts +10 -3
- package/tsconfig.json +1 -0
- package/dist/__mocks__/coloris.d.ts +0 -2
- package/dist/__mocks__/coloris.js +0 -5
@@ -1,8 +1,9 @@
|
|
1
|
-
import Color4 from '
|
2
|
-
import
|
3
|
-
import
|
4
|
-
import
|
5
|
-
import
|
1
|
+
import Color4 from '../../Color4';
|
2
|
+
import Mat33 from '../../geometry/Mat33';
|
3
|
+
import Rect2 from '../../geometry/Rect2';
|
4
|
+
import { Point2, Vec2 } from '../../geometry/Vec2';
|
5
|
+
import Vec3 from '../../geometry/Vec3';
|
6
|
+
import Viewport from '../../Viewport';
|
6
7
|
import AbstractRenderer, { RenderablePathSpec, RenderingStyle } from './AbstractRenderer';
|
7
8
|
|
8
9
|
export default class CanvasRenderer extends AbstractRenderer {
|
@@ -25,6 +26,30 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
25
26
|
this.setDraftMode(false);
|
26
27
|
}
|
27
28
|
|
29
|
+
public canRenderFromWithoutDataLoss(other: AbstractRenderer) {
|
30
|
+
return other instanceof CanvasRenderer;
|
31
|
+
}
|
32
|
+
|
33
|
+
public renderFromOtherOfSameType(transformBy: Mat33, other: AbstractRenderer): void {
|
34
|
+
if (!(other instanceof CanvasRenderer)) {
|
35
|
+
throw new Error(`${other} cannot be rendered onto ${this}`);
|
36
|
+
}
|
37
|
+
transformBy = this.getCanvasToScreenTransform().rightMul(transformBy);
|
38
|
+
this.ctx.save();
|
39
|
+
// From MDN, transform(a,b,c,d,e,f)
|
40
|
+
// takes input such that
|
41
|
+
// ⎡ a c e ⎤
|
42
|
+
// ⎢ b d f ⎥ transforms content drawn to [ctx].
|
43
|
+
// ⎣ 0 0 1 ⎦
|
44
|
+
this.ctx.transform(
|
45
|
+
transformBy.a1, transformBy.b1, // a, b
|
46
|
+
transformBy.a2, transformBy.b2, // c, d
|
47
|
+
transformBy.a3, transformBy.b3, // e, f
|
48
|
+
);
|
49
|
+
this.ctx.drawImage(other.ctx.canvas, 0, 0);
|
50
|
+
this.ctx.restore();
|
51
|
+
}
|
52
|
+
|
28
53
|
// Set parameters for lower/higher quality rendering
|
29
54
|
public setDraftMode(draftMode: boolean) {
|
30
55
|
if (draftMode) {
|
@@ -50,7 +75,7 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
50
75
|
}
|
51
76
|
|
52
77
|
protected beginPath(startPoint: Point2) {
|
53
|
-
startPoint = this.
|
78
|
+
startPoint = this.canvasToScreen(startPoint);
|
54
79
|
|
55
80
|
this.ctx.beginPath();
|
56
81
|
this.ctx.moveTo(startPoint.x, startPoint.y);
|
@@ -62,7 +87,7 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
62
87
|
|
63
88
|
if (style.stroke) {
|
64
89
|
this.ctx.strokeStyle = style.stroke.color.toHexString();
|
65
|
-
this.ctx.lineWidth = this.
|
90
|
+
this.ctx.lineWidth = this.getSizeOfCanvasPixelOnScreen() * style.stroke.width;
|
66
91
|
this.ctx.stroke();
|
67
92
|
}
|
68
93
|
|
@@ -70,19 +95,19 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
70
95
|
}
|
71
96
|
|
72
97
|
protected lineTo(point: Point2) {
|
73
|
-
point = this.
|
98
|
+
point = this.canvasToScreen(point);
|
74
99
|
this.ctx.lineTo(point.x, point.y);
|
75
100
|
}
|
76
101
|
|
77
102
|
protected moveTo(point: Point2) {
|
78
|
-
point = this.
|
103
|
+
point = this.canvasToScreen(point);
|
79
104
|
this.ctx.moveTo(point.x, point.y);
|
80
105
|
}
|
81
106
|
|
82
107
|
protected traceCubicBezierCurve(p1: Point2, p2: Point2, p3: Point2) {
|
83
|
-
p1 = this.
|
84
|
-
p2 = this.
|
85
|
-
p3 = this.
|
108
|
+
p1 = this.canvasToScreen(p1);
|
109
|
+
p2 = this.canvasToScreen(p2);
|
110
|
+
p3 = this.canvasToScreen(p3);
|
86
111
|
|
87
112
|
// Approximate the curve if small enough.
|
88
113
|
const delta1 = p2.minus(p1);
|
@@ -96,8 +121,8 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
96
121
|
}
|
97
122
|
|
98
123
|
protected traceQuadraticBezierCurve(controlPoint: Vec3, endPoint: Vec3) {
|
99
|
-
controlPoint = this.
|
100
|
-
endPoint = this.
|
124
|
+
controlPoint = this.canvasToScreen(controlPoint);
|
125
|
+
endPoint = this.canvasToScreen(endPoint);
|
101
126
|
|
102
127
|
// Approximate the curve with a line if small enough
|
103
128
|
const delta = controlPoint.minus(endPoint);
|
@@ -118,23 +143,35 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
118
143
|
super.drawPath(path);
|
119
144
|
}
|
120
145
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
const bothDimenMinSize = this.minRenderSizeBothDimens;
|
126
|
-
const bothTooSmall = Math.abs(diagonal.x) < bothDimenMinSize && Math.abs(diagonal.y) < bothDimenMinSize;
|
127
|
-
const anyDimenMinSize = this.minRenderSizeAnyDimen;
|
128
|
-
const anyTooSmall = Math.abs(diagonal.x) < anyDimenMinSize || Math.abs(diagonal.y) < anyDimenMinSize;
|
129
|
-
|
130
|
-
if (bothTooSmall || anyTooSmall) {
|
146
|
+
private clipLevels: number[] = [];
|
147
|
+
public startObject(boundingBox: Rect2, clip: boolean) {
|
148
|
+
if (this.isTooSmallToRender(boundingBox)) {
|
131
149
|
this.ignoreObjectsAboveLevel = this.getNestingLevel();
|
132
150
|
this.ignoringObject = true;
|
133
151
|
}
|
134
152
|
|
135
153
|
super.startObject(boundingBox);
|
154
|
+
|
155
|
+
if (!this.ignoringObject && clip) {
|
156
|
+
this.clipLevels.push(this.objectLevel);
|
157
|
+
this.ctx.save();
|
158
|
+
this.ctx.beginPath();
|
159
|
+
for (const corner of boundingBox.corners) {
|
160
|
+
const screenCorner = this.canvasToScreen(corner);
|
161
|
+
this.ctx.lineTo(screenCorner.x, screenCorner.y);
|
162
|
+
}
|
163
|
+
this.ctx.clip();
|
164
|
+
}
|
136
165
|
}
|
166
|
+
|
137
167
|
public endObject() {
|
168
|
+
if (!this.ignoringObject && this.clipLevels.length > 0) {
|
169
|
+
if (this.clipLevels[this.clipLevels.length - 1] === this.objectLevel) {
|
170
|
+
this.ctx.restore();
|
171
|
+
this.clipLevels.pop();
|
172
|
+
}
|
173
|
+
}
|
174
|
+
|
138
175
|
super.endObject();
|
139
176
|
|
140
177
|
// If exiting an object with a too-small-to-draw bounding box,
|
@@ -148,7 +185,7 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
148
185
|
const pointRadius = 10;
|
149
186
|
|
150
187
|
for (let i = 0; i < points.length; i++) {
|
151
|
-
const point = this.
|
188
|
+
const point = this.canvasToScreen(points[i]);
|
152
189
|
|
153
190
|
this.ctx.beginPath();
|
154
191
|
this.ctx.arc(point.x, point.y, pointRadius, 0, Math.PI * 2);
|
@@ -167,4 +204,16 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
167
204
|
this.ctx.fillText(`${i}`, point.x, point.y, pointRadius * 2);
|
168
205
|
}
|
169
206
|
}
|
207
|
+
|
208
|
+
public isTooSmallToRender(rect: Rect2): boolean {
|
209
|
+
// Should we ignore all objects within this object's bbox?
|
210
|
+
const diagonal = this.getCanvasToScreenTransform().transformVec3(rect.size);
|
211
|
+
|
212
|
+
const bothDimenMinSize = this.minRenderSizeBothDimens;
|
213
|
+
const bothTooSmall = Math.abs(diagonal.x) < bothDimenMinSize && Math.abs(diagonal.y) < bothDimenMinSize;
|
214
|
+
const anyDimenMinSize = this.minRenderSizeAnyDimen;
|
215
|
+
const anyTooSmall = Math.abs(diagonal.x) < anyDimenMinSize || Math.abs(diagonal.y) < anyDimenMinSize;
|
216
|
+
|
217
|
+
return bothTooSmall || anyTooSmall;
|
218
|
+
}
|
170
219
|
}
|
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
import EventDispatcher from '../../EventDispatcher';
|
3
|
+
import Mat33 from '../../geometry/Mat33';
|
4
|
+
import { Vec2 } from '../../geometry/Vec2';
|
5
|
+
import Viewport from '../../Viewport';
|
6
|
+
import DummyRenderer from './DummyRenderer';
|
7
|
+
|
8
|
+
const makeRenderer = (): [DummyRenderer, Viewport] => {
|
9
|
+
const viewport = new Viewport(new EventDispatcher());
|
10
|
+
return [ new DummyRenderer(viewport), viewport ];
|
11
|
+
};
|
12
|
+
|
13
|
+
describe('DummyRenderer', () => {
|
14
|
+
it('should correctly calculate the size of a pixel on the screen', () => {
|
15
|
+
const [ renderer, viewport ] = makeRenderer();
|
16
|
+
viewport.updateScreenSize(Vec2.of(100, 100));
|
17
|
+
viewport.resetTransform(Mat33.identity);
|
18
|
+
|
19
|
+
expect(1/viewport.getScaleFactor()).toBe(1);
|
20
|
+
expect(renderer.getSizeOfCanvasPixelOnScreen()).toBe(1);
|
21
|
+
|
22
|
+
// Updating the translation matrix shouldn't affect the size of a pixel on the
|
23
|
+
// screen.
|
24
|
+
renderer.setTransform(Mat33.translation(Vec2.of(-1, -2)));
|
25
|
+
expect(renderer.getSizeOfCanvasPixelOnScreen()).toBe(1);
|
26
|
+
viewport.resetTransform(Mat33.translation(Vec2.of(3, 4)));
|
27
|
+
expect(renderer.getSizeOfCanvasPixelOnScreen()).toBe(1);
|
28
|
+
|
29
|
+
// Scale objects by a factor of 2 when drawing
|
30
|
+
renderer.setTransform(Mat33.scaling2D(2));
|
31
|
+
expect(renderer.getSizeOfCanvasPixelOnScreen()).toBe(2);
|
32
|
+
viewport.resetTransform(Mat33.scaling2D(0.5));
|
33
|
+
|
34
|
+
// When a renderer transform is set, **only** the renderer transform should be used.
|
35
|
+
expect(renderer.getSizeOfCanvasPixelOnScreen()).toBe(2);
|
36
|
+
renderer.setTransform(null);
|
37
|
+
expect(renderer.getSizeOfCanvasPixelOnScreen()).toBe(0.5);
|
38
|
+
|
39
|
+
// Rotating should not affect the size of a pixel
|
40
|
+
renderer.setTransform(Mat33.zRotation(Math.PI / 4).rightMul(Mat33.scaling2D(4)));
|
41
|
+
expect(renderer.getSizeOfCanvasPixelOnScreen()).toBe(4);
|
42
|
+
});
|
43
|
+
});
|
@@ -1,9 +1,10 @@
|
|
1
1
|
// Renderer that outputs nothing. Useful for automated tests.
|
2
2
|
|
3
|
-
import
|
4
|
-
import
|
5
|
-
import
|
6
|
-
import
|
3
|
+
import Mat33 from '../../geometry/Mat33';
|
4
|
+
import Rect2 from '../../geometry/Rect2';
|
5
|
+
import { Point2, Vec2 } from '../../geometry/Vec2';
|
6
|
+
import Vec3 from '../../geometry/Vec3';
|
7
|
+
import Viewport from '../../Viewport';
|
7
8
|
import AbstractRenderer, { RenderingStyle } from './AbstractRenderer';
|
8
9
|
|
9
10
|
export default class DummyRenderer extends AbstractRenderer {
|
@@ -22,8 +23,17 @@ export default class DummyRenderer extends AbstractRenderer {
|
|
22
23
|
}
|
23
24
|
|
24
25
|
public displaySize(): Vec2 {
|
25
|
-
//
|
26
|
-
|
26
|
+
// Do we have a stored viewport size?
|
27
|
+
const viewportSize = this.getViewport().getResolution();
|
28
|
+
|
29
|
+
// Don't use a 0x0 viewport — DummyRenderer is often used
|
30
|
+
// for tests that run without a display, so pretend we have a
|
31
|
+
// reasonable-sized display.
|
32
|
+
if (viewportSize.x === 0 || viewportSize.y === 0) {
|
33
|
+
return Vec2.of(640, 480);
|
34
|
+
}
|
35
|
+
|
36
|
+
return viewportSize;
|
27
37
|
}
|
28
38
|
|
29
39
|
public clear() {
|
@@ -47,18 +57,29 @@ export default class DummyRenderer extends AbstractRenderer {
|
|
47
57
|
this.lastFillStyle = style;
|
48
58
|
}
|
49
59
|
protected lineTo(point: Vec3) {
|
60
|
+
point = this.canvasToScreen(point);
|
61
|
+
|
50
62
|
this.lastPoint = point;
|
51
63
|
this.pointBuffer.push(point);
|
52
64
|
}
|
53
65
|
protected moveTo(point: Point2) {
|
66
|
+
point = this.canvasToScreen(point);
|
67
|
+
|
54
68
|
this.lastPoint = point;
|
55
69
|
this.pointBuffer.push(point);
|
56
70
|
}
|
57
71
|
protected traceCubicBezierCurve(p1: Vec3, p2: Vec3, p3: Vec3) {
|
72
|
+
p1 = this.canvasToScreen(p1);
|
73
|
+
p2 = this.canvasToScreen(p2);
|
74
|
+
p3 = this.canvasToScreen(p3);
|
75
|
+
|
58
76
|
this.lastPoint = p3;
|
59
77
|
this.pointBuffer.push(p1, p2, p3);
|
60
78
|
}
|
61
79
|
protected traceQuadraticBezierCurve(controlPoint: Vec3, endPoint: Vec3) {
|
80
|
+
controlPoint = this.canvasToScreen(controlPoint);
|
81
|
+
endPoint = this.canvasToScreen(endPoint);
|
82
|
+
|
62
83
|
this.lastPoint = endPoint;
|
63
84
|
this.pointBuffer.push(controlPoint, endPoint);
|
64
85
|
}
|
@@ -67,7 +88,7 @@ export default class DummyRenderer extends AbstractRenderer {
|
|
67
88
|
// As such, it is unlikely to be the target of automated tests.
|
68
89
|
}
|
69
90
|
|
70
|
-
public startObject(boundingBox: Rect2) {
|
91
|
+
public startObject(boundingBox: Rect2, _clip: boolean) {
|
71
92
|
super.startObject(boundingBox);
|
72
93
|
|
73
94
|
this.objectNestingLevel += 1;
|
@@ -77,4 +98,26 @@ export default class DummyRenderer extends AbstractRenderer {
|
|
77
98
|
|
78
99
|
this.objectNestingLevel -= 1;
|
79
100
|
}
|
101
|
+
|
102
|
+
public isTooSmallToRender(_rect: Rect2): boolean {
|
103
|
+
return false;
|
104
|
+
}
|
105
|
+
|
106
|
+
|
107
|
+
public canRenderFromWithoutDataLoss(other: AbstractRenderer) {
|
108
|
+
return other instanceof DummyRenderer;
|
109
|
+
}
|
110
|
+
|
111
|
+
public renderFromOtherOfSameType(transform: Mat33, other: AbstractRenderer): void {
|
112
|
+
if (!(other instanceof DummyRenderer)) {
|
113
|
+
throw new Error(`${other} cannot be rendered onto ${this}`);
|
114
|
+
}
|
115
|
+
|
116
|
+
this.renderedPathCount += other.renderedPathCount;
|
117
|
+
this.lastFillStyle = other.lastFillStyle;
|
118
|
+
this.lastPoint = other.lastPoint;
|
119
|
+
this.pointBuffer.push(...other.pointBuffer.map(point => {
|
120
|
+
return transform.transformVec2(point);
|
121
|
+
}));
|
122
|
+
}
|
80
123
|
}
|
@@ -1,8 +1,10 @@
|
|
1
1
|
|
2
|
-
import
|
3
|
-
import
|
4
|
-
import
|
5
|
-
import
|
2
|
+
import { LoadSaveDataTable } from '../../components/AbstractComponent';
|
3
|
+
import Path, { PathCommand, PathCommandType } from '../../geometry/Path';
|
4
|
+
import Rect2 from '../../geometry/Rect2';
|
5
|
+
import { Point2, Vec2 } from '../../geometry/Vec2';
|
6
|
+
import { svgAttributesDataKey, SVGLoaderUnknownAttribute } from '../../SVGLoader';
|
7
|
+
import Viewport from '../../Viewport';
|
6
8
|
import AbstractRenderer, { RenderingStyle } from './AbstractRenderer';
|
7
9
|
|
8
10
|
const svgNameSpace = 'http://www.w3.org/2000/svg';
|
@@ -13,8 +15,8 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
13
15
|
private lastPathStyle: RenderingStyle|null;
|
14
16
|
private lastPath: PathCommand[]|null;
|
15
17
|
private lastPathStart: Point2|null;
|
18
|
+
private objectElems: SVGElement[]|null = null;
|
16
19
|
|
17
|
-
private mainGroup: SVGGElement;
|
18
20
|
private overwrittenAttrs: Record<string, string|null> = {};
|
19
21
|
|
20
22
|
public constructor(private elem: SVGSVGElement, viewport: Viewport) {
|
@@ -41,8 +43,6 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
41
43
|
}
|
42
44
|
|
43
45
|
public clear() {
|
44
|
-
this.mainGroup = document.createElementNS(svgNameSpace, 'g');
|
45
|
-
|
46
46
|
// Restore all alltributes
|
47
47
|
for (const attrName in this.overwrittenAttrs) {
|
48
48
|
const value = this.overwrittenAttrs[attrName];
|
@@ -54,14 +54,11 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
54
54
|
}
|
55
55
|
}
|
56
56
|
this.overwrittenAttrs = {};
|
57
|
-
|
58
|
-
// Remove all children
|
59
|
-
this.elem.replaceChildren(this.mainGroup);
|
60
57
|
}
|
61
58
|
|
62
59
|
protected beginPath(startPoint: Point2) {
|
63
60
|
this.currentPath = [];
|
64
|
-
this.pathStart = this.
|
61
|
+
this.pathStart = this.canvasToScreen(startPoint);
|
65
62
|
this.lastPathStart ??= this.pathStart;
|
66
63
|
}
|
67
64
|
|
@@ -106,7 +103,8 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
106
103
|
pathElem.setAttribute('stroke-width', style.stroke.width.toString());
|
107
104
|
}
|
108
105
|
|
109
|
-
this.
|
106
|
+
this.elem.appendChild(pathElem);
|
107
|
+
this.objectElems?.push(pathElem);
|
110
108
|
}
|
111
109
|
|
112
110
|
public startObject(boundingBox: Rect2) {
|
@@ -116,17 +114,31 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
116
114
|
this.lastPath = null;
|
117
115
|
this.lastPathStart = null;
|
118
116
|
this.lastPathStyle = null;
|
117
|
+
this.objectElems = [];
|
119
118
|
}
|
120
119
|
|
121
|
-
public endObject() {
|
122
|
-
super.endObject();
|
120
|
+
public endObject(loaderData?: LoadSaveDataTable) {
|
121
|
+
super.endObject(loaderData);
|
123
122
|
|
124
123
|
// Don't extend paths across objects
|
125
124
|
this.addPathToSVG();
|
125
|
+
|
126
|
+
if (loaderData) {
|
127
|
+
// Restore any attributes unsupported by the app.
|
128
|
+
for (const elem of this.objectElems ?? []) {
|
129
|
+
const attrs = loaderData[svgAttributesDataKey] as SVGLoaderUnknownAttribute[]|undefined;
|
130
|
+
|
131
|
+
if (attrs) {
|
132
|
+
for (const [ attr, value ] of attrs) {
|
133
|
+
elem.setAttribute(attr, value);
|
134
|
+
}
|
135
|
+
}
|
136
|
+
}
|
137
|
+
}
|
126
138
|
}
|
127
139
|
|
128
140
|
protected lineTo(point: Point2) {
|
129
|
-
point = this.
|
141
|
+
point = this.canvasToScreen(point);
|
130
142
|
|
131
143
|
this.currentPath!.push({
|
132
144
|
kind: PathCommandType.LineTo,
|
@@ -135,7 +147,7 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
135
147
|
}
|
136
148
|
|
137
149
|
protected moveTo(point: Point2) {
|
138
|
-
point = this.
|
150
|
+
point = this.canvasToScreen(point);
|
139
151
|
|
140
152
|
this.currentPath!.push({
|
141
153
|
kind: PathCommandType.MoveTo,
|
@@ -146,9 +158,9 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
146
158
|
protected traceCubicBezierCurve(
|
147
159
|
controlPoint1: Point2, controlPoint2: Point2, endPoint: Point2
|
148
160
|
) {
|
149
|
-
controlPoint1 = this.
|
150
|
-
controlPoint2 = this.
|
151
|
-
endPoint = this.
|
161
|
+
controlPoint1 = this.canvasToScreen(controlPoint1);
|
162
|
+
controlPoint2 = this.canvasToScreen(controlPoint2);
|
163
|
+
endPoint = this.canvasToScreen(endPoint);
|
152
164
|
|
153
165
|
this.currentPath!.push({
|
154
166
|
kind: PathCommandType.CubicBezierTo,
|
@@ -159,8 +171,8 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
159
171
|
}
|
160
172
|
|
161
173
|
protected traceQuadraticBezierCurve(controlPoint: Point2, endPoint: Point2) {
|
162
|
-
controlPoint = this.
|
163
|
-
endPoint = this.
|
174
|
+
controlPoint = this.canvasToScreen(controlPoint);
|
175
|
+
endPoint = this.canvasToScreen(endPoint);
|
164
176
|
|
165
177
|
this.currentPath!.push({
|
166
178
|
kind: PathCommandType.QuadraticBezierTo,
|
@@ -175,12 +187,16 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
175
187
|
elem.setAttribute('cx', `${point.x}`);
|
176
188
|
elem.setAttribute('cy', `${point.y}`);
|
177
189
|
elem.setAttribute('r', '15');
|
178
|
-
this.
|
190
|
+
this.elem.appendChild(elem);
|
179
191
|
});
|
180
192
|
}
|
181
193
|
|
182
|
-
// Renders a copy of the given element.
|
194
|
+
// Renders a **copy** of the given element.
|
183
195
|
public drawSVGElem(elem: SVGElement) {
|
184
196
|
this.elem.appendChild(elem.cloneNode(true));
|
185
197
|
}
|
198
|
+
|
199
|
+
public isTooSmallToRender(_rect: Rect2): boolean {
|
200
|
+
return false;
|
201
|
+
}
|
186
202
|
}
|