js-draw 0.1.1 → 0.1.4
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 +13 -0
- package/README.md +21 -12
- package/dist/bundle.js +1 -1
- package/dist/src/Editor.d.ts +2 -1
- package/dist/src/Editor.js +24 -6
- package/dist/src/EditorImage.js +3 -0
- package/dist/src/Pointer.d.ts +3 -2
- package/dist/src/Pointer.js +12 -3
- package/dist/src/SVGLoader.d.ts +11 -0
- package/dist/src/SVGLoader.js +113 -4
- package/dist/src/Viewport.d.ts +1 -1
- package/dist/src/Viewport.js +12 -2
- package/dist/src/components/AbstractComponent.d.ts +6 -0
- package/dist/src/components/AbstractComponent.js +11 -0
- package/dist/src/components/SVGGlobalAttributesObject.js +0 -1
- package/dist/src/components/Stroke.js +1 -1
- package/dist/src/components/Text.d.ts +30 -0
- package/dist/src/components/Text.js +111 -0
- package/dist/src/components/localization.d.ts +1 -0
- package/dist/src/components/localization.js +1 -0
- package/dist/src/geometry/Mat33.d.ts +1 -0
- package/dist/src/geometry/Mat33.js +30 -0
- package/dist/src/geometry/Path.js +105 -67
- package/dist/src/geometry/Rect2.d.ts +2 -0
- package/dist/src/geometry/Rect2.js +6 -0
- package/dist/src/rendering/renderers/AbstractRenderer.d.ts +7 -1
- package/dist/src/rendering/renderers/AbstractRenderer.js +13 -1
- package/dist/src/rendering/renderers/CanvasRenderer.d.ts +3 -0
- package/dist/src/rendering/renderers/CanvasRenderer.js +28 -8
- package/dist/src/rendering/renderers/DummyRenderer.d.ts +3 -0
- package/dist/src/rendering/renderers/DummyRenderer.js +5 -0
- package/dist/src/rendering/renderers/SVGRenderer.d.ts +6 -2
- package/dist/src/rendering/renderers/SVGRenderer.js +50 -7
- package/dist/src/testing/loadExpectExtensions.js +1 -4
- package/dist/src/toolbar/HTMLToolbar.d.ts +2 -1
- package/dist/src/toolbar/HTMLToolbar.js +242 -154
- package/dist/src/toolbar/icons.d.ts +12 -0
- package/dist/src/toolbar/icons.js +198 -0
- package/dist/src/toolbar/localization.d.ts +5 -1
- package/dist/src/toolbar/localization.js +5 -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 +1 -1
- package/dist/src/tools/TextTool.d.ts +30 -0
- package/dist/src/tools/TextTool.js +173 -0
- package/dist/src/tools/ToolController.d.ts +5 -5
- package/dist/src/tools/ToolController.js +10 -9
- package/dist/src/tools/localization.d.ts +3 -0
- package/dist/src/tools/localization.js +3 -0
- package/dist-test/test-dist-bundle.html +8 -1
- package/package.json +1 -1
- package/src/Editor.css +2 -0
- package/src/Editor.ts +26 -7
- package/src/EditorImage.ts +4 -0
- package/src/Pointer.ts +13 -4
- package/src/SVGLoader.ts +146 -5
- package/src/Viewport.ts +15 -3
- package/src/components/AbstractComponent.ts +16 -1
- package/src/components/SVGGlobalAttributesObject.ts +0 -1
- package/src/components/Stroke.ts +1 -1
- package/src/components/Text.ts +140 -0
- package/src/components/localization.ts +2 -0
- package/src/geometry/Mat33.test.ts +44 -0
- package/src/geometry/Mat33.ts +41 -0
- package/src/geometry/Path.fromString.test.ts +94 -4
- package/src/geometry/Path.toString.test.ts +7 -3
- package/src/geometry/Path.ts +110 -68
- package/src/geometry/Rect2.ts +8 -0
- package/src/rendering/renderers/AbstractRenderer.ts +18 -1
- package/src/rendering/renderers/CanvasRenderer.ts +34 -10
- package/src/rendering/renderers/DummyRenderer.ts +8 -0
- package/src/rendering/renderers/SVGRenderer.ts +57 -10
- package/src/testing/loadExpectExtensions.ts +1 -4
- package/src/toolbar/HTMLToolbar.ts +294 -170
- package/src/toolbar/icons.ts +227 -0
- package/src/toolbar/localization.ts +11 -2
- package/src/toolbar/toolbar.css +27 -11
- 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.ts +1 -1
- package/src/tools/TextTool.ts +225 -0
- package/src/tools/ToolController.ts +7 -5
- package/src/tools/localization.ts +7 -0
@@ -1,4 +1,5 @@
|
|
1
1
|
import Color4 from '../../Color4';
|
2
|
+
import Text, { TextStyle } from '../../components/Text';
|
2
3
|
import Mat33 from '../../geometry/Mat33';
|
3
4
|
import Rect2 from '../../geometry/Rect2';
|
4
5
|
import { Point2, Vec2 } from '../../geometry/Vec2';
|
@@ -26,16 +27,7 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
26
27
|
this.setDraftMode(false);
|
27
28
|
}
|
28
29
|
|
29
|
-
|
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();
|
30
|
+
private transformBy(transformBy: Mat33) {
|
39
31
|
// From MDN, transform(a,b,c,d,e,f)
|
40
32
|
// takes input such that
|
41
33
|
// ⎡ a c e ⎤
|
@@ -46,6 +38,19 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
46
38
|
transformBy.a2, transformBy.b2, // c, d
|
47
39
|
transformBy.a3, transformBy.b3, // e, f
|
48
40
|
);
|
41
|
+
}
|
42
|
+
|
43
|
+
public canRenderFromWithoutDataLoss(other: AbstractRenderer) {
|
44
|
+
return other instanceof CanvasRenderer;
|
45
|
+
}
|
46
|
+
|
47
|
+
public renderFromOtherOfSameType(transformBy: Mat33, other: AbstractRenderer): void {
|
48
|
+
if (!(other instanceof CanvasRenderer)) {
|
49
|
+
throw new Error(`${other} cannot be rendered onto ${this}`);
|
50
|
+
}
|
51
|
+
transformBy = this.getCanvasToScreenTransform().rightMul(transformBy);
|
52
|
+
this.ctx.save();
|
53
|
+
this.transformBy(transformBy);
|
49
54
|
this.ctx.drawImage(other.ctx.canvas, 0, 0);
|
50
55
|
this.ctx.restore();
|
51
56
|
}
|
@@ -143,6 +148,25 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
143
148
|
super.drawPath(path);
|
144
149
|
}
|
145
150
|
|
151
|
+
public drawText(text: string, transform: Mat33, style: TextStyle): void {
|
152
|
+
this.ctx.save();
|
153
|
+
transform = this.getCanvasToScreenTransform().rightMul(transform);
|
154
|
+
this.transformBy(transform);
|
155
|
+
Text.applyTextStyles(this.ctx, style);
|
156
|
+
|
157
|
+
if (style.renderingStyle.fill.a !== 0) {
|
158
|
+
this.ctx.fillStyle = style.renderingStyle.fill.toHexString();
|
159
|
+
this.ctx.fillText(text, 0, 0);
|
160
|
+
}
|
161
|
+
if (style.renderingStyle.stroke) {
|
162
|
+
this.ctx.strokeStyle = style.renderingStyle.stroke.color.toHexString();
|
163
|
+
this.ctx.lineWidth = style.renderingStyle.stroke.width;
|
164
|
+
this.ctx.strokeText(text, 0, 0);
|
165
|
+
}
|
166
|
+
|
167
|
+
this.ctx.restore();
|
168
|
+
}
|
169
|
+
|
146
170
|
private clipLevels: number[] = [];
|
147
171
|
public startObject(boundingBox: Rect2, clip: boolean) {
|
148
172
|
if (this.isTooSmallToRender(boundingBox)) {
|
@@ -1,5 +1,6 @@
|
|
1
1
|
// Renderer that outputs nothing. Useful for automated tests.
|
2
2
|
|
3
|
+
import { TextStyle } from '../../components/Text';
|
3
4
|
import Mat33 from '../../geometry/Mat33';
|
4
5
|
import Rect2 from '../../geometry/Rect2';
|
5
6
|
import { Point2, Vec2 } from '../../geometry/Vec2';
|
@@ -14,6 +15,7 @@ export default class DummyRenderer extends AbstractRenderer {
|
|
14
15
|
public lastFillStyle: RenderingStyle|null = null;
|
15
16
|
public lastPoint: Point2|null = null;
|
16
17
|
public objectNestingLevel: number = 0;
|
18
|
+
public lastText: string|null = null;
|
17
19
|
|
18
20
|
// List of points drawn since the last clear.
|
19
21
|
public pointBuffer: Point2[] = [];
|
@@ -40,6 +42,7 @@ export default class DummyRenderer extends AbstractRenderer {
|
|
40
42
|
this.clearedCount ++;
|
41
43
|
this.renderedPathCount = 0;
|
42
44
|
this.pointBuffer = [];
|
45
|
+
this.lastText = null;
|
43
46
|
|
44
47
|
// Ensure all objects finished rendering
|
45
48
|
if (this.objectNestingLevel > 0) {
|
@@ -88,6 +91,11 @@ export default class DummyRenderer extends AbstractRenderer {
|
|
88
91
|
// As such, it is unlikely to be the target of automated tests.
|
89
92
|
}
|
90
93
|
|
94
|
+
|
95
|
+
public drawText(text: string, _transform: Mat33, _style: TextStyle): void {
|
96
|
+
this.lastText = text;
|
97
|
+
}
|
98
|
+
|
91
99
|
public startObject(boundingBox: Rect2, _clip: boolean) {
|
92
100
|
super.startObject(boundingBox);
|
93
101
|
|
@@ -1,7 +1,11 @@
|
|
1
1
|
|
2
|
+
import { LoadSaveDataTable } from '../../components/AbstractComponent';
|
3
|
+
import { TextStyle } from '../../components/Text';
|
4
|
+
import Mat33 from '../../geometry/Mat33';
|
2
5
|
import Path, { PathCommand, PathCommandType } from '../../geometry/Path';
|
3
6
|
import Rect2 from '../../geometry/Rect2';
|
4
7
|
import { Point2, Vec2 } from '../../geometry/Vec2';
|
8
|
+
import { svgAttributesDataKey, SVGLoaderUnknownAttribute, SVGLoaderUnknownStyleAttribute, svgStyleAttributesDataKey } from '../../SVGLoader';
|
5
9
|
import Viewport from '../../Viewport';
|
6
10
|
import AbstractRenderer, { RenderingStyle } from './AbstractRenderer';
|
7
11
|
|
@@ -13,8 +17,8 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
13
17
|
private lastPathStyle: RenderingStyle|null;
|
14
18
|
private lastPath: PathCommand[]|null;
|
15
19
|
private lastPathStart: Point2|null;
|
20
|
+
private objectElems: SVGElement[]|null = null;
|
16
21
|
|
17
|
-
private mainGroup: SVGGElement;
|
18
22
|
private overwrittenAttrs: Record<string, string|null> = {};
|
19
23
|
|
20
24
|
public constructor(private elem: SVGSVGElement, viewport: Viewport) {
|
@@ -41,8 +45,6 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
41
45
|
}
|
42
46
|
|
43
47
|
public clear() {
|
44
|
-
this.mainGroup = document.createElementNS(svgNameSpace, 'g');
|
45
|
-
|
46
48
|
// Restore all alltributes
|
47
49
|
for (const attrName in this.overwrittenAttrs) {
|
48
50
|
const value = this.overwrittenAttrs[attrName];
|
@@ -54,9 +56,6 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
54
56
|
}
|
55
57
|
}
|
56
58
|
this.overwrittenAttrs = {};
|
57
|
-
|
58
|
-
// Remove all children
|
59
|
-
this.elem.replaceChildren(this.mainGroup);
|
60
59
|
}
|
61
60
|
|
62
61
|
protected beginPath(startPoint: Point2) {
|
@@ -106,7 +105,34 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
106
105
|
pathElem.setAttribute('stroke-width', style.stroke.width.toString());
|
107
106
|
}
|
108
107
|
|
109
|
-
this.
|
108
|
+
this.elem.appendChild(pathElem);
|
109
|
+
this.objectElems?.push(pathElem);
|
110
|
+
}
|
111
|
+
|
112
|
+
public drawText(text: string, transform: Mat33, style: TextStyle): void {
|
113
|
+
transform = this.getCanvasToScreenTransform().rightMul(transform);
|
114
|
+
|
115
|
+
const textElem = document.createElementNS(svgNameSpace, 'text');
|
116
|
+
textElem.appendChild(document.createTextNode(text));
|
117
|
+
textElem.style.transform = `matrix(
|
118
|
+
${transform.a1}, ${transform.b1},
|
119
|
+
${transform.a2}, ${transform.b2},
|
120
|
+
${transform.a3}, ${transform.b3}
|
121
|
+
)`;
|
122
|
+
textElem.style.fontFamily = style.fontFamily;
|
123
|
+
textElem.style.fontVariant = style.fontVariant ?? '';
|
124
|
+
textElem.style.fontWeight = style.fontWeight ?? '';
|
125
|
+
textElem.style.fontSize = style.size + 'px';
|
126
|
+
textElem.style.fill = style.renderingStyle.fill.toHexString();
|
127
|
+
|
128
|
+
if (style.renderingStyle.stroke) {
|
129
|
+
const strokeStyle = style.renderingStyle.stroke;
|
130
|
+
textElem.style.stroke = strokeStyle.color.toHexString();
|
131
|
+
textElem.style.strokeWidth = strokeStyle.width + 'px';
|
132
|
+
}
|
133
|
+
|
134
|
+
this.elem.appendChild(textElem);
|
135
|
+
this.objectElems?.push(textElem);
|
110
136
|
}
|
111
137
|
|
112
138
|
public startObject(boundingBox: Rect2) {
|
@@ -116,13 +142,34 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
116
142
|
this.lastPath = null;
|
117
143
|
this.lastPathStart = null;
|
118
144
|
this.lastPathStyle = null;
|
145
|
+
this.objectElems = [];
|
119
146
|
}
|
120
147
|
|
121
|
-
public endObject() {
|
122
|
-
super.endObject();
|
148
|
+
public endObject(loaderData?: LoadSaveDataTable) {
|
149
|
+
super.endObject(loaderData);
|
123
150
|
|
124
151
|
// Don't extend paths across objects
|
125
152
|
this.addPathToSVG();
|
153
|
+
|
154
|
+
if (loaderData) {
|
155
|
+
// Restore any attributes unsupported by the app.
|
156
|
+
for (const elem of this.objectElems ?? []) {
|
157
|
+
const attrs = loaderData[svgAttributesDataKey] as SVGLoaderUnknownAttribute[]|undefined;
|
158
|
+
const styleAttrs = loaderData[svgStyleAttributesDataKey] as SVGLoaderUnknownStyleAttribute[]|undefined;
|
159
|
+
|
160
|
+
if (attrs) {
|
161
|
+
for (const [ attr, value ] of attrs) {
|
162
|
+
elem.setAttribute(attr, value);
|
163
|
+
}
|
164
|
+
}
|
165
|
+
|
166
|
+
if (styleAttrs) {
|
167
|
+
for (const attr of styleAttrs) {
|
168
|
+
elem.style.setProperty(attr.key, attr.value, attr.priority);
|
169
|
+
}
|
170
|
+
}
|
171
|
+
}
|
172
|
+
}
|
126
173
|
}
|
127
174
|
|
128
175
|
protected lineTo(point: Point2) {
|
@@ -175,7 +222,7 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
175
222
|
elem.setAttribute('cx', `${point.x}`);
|
176
223
|
elem.setAttribute('cy', `${point.y}`);
|
177
224
|
elem.setAttribute('r', '15');
|
178
|
-
this.
|
225
|
+
this.elem.appendChild(elem);
|
179
226
|
});
|
180
227
|
}
|
181
228
|
|
@@ -15,10 +15,7 @@ export const loadExpectExtensions = () => {
|
|
15
15
|
return {
|
16
16
|
pass,
|
17
17
|
message: () => {
|
18
|
-
|
19
|
-
return `Expected ${expected} not to .eq ${actual}. Options(${eqArgs})`;
|
20
|
-
}
|
21
|
-
return `Expected ${expected} to .eq ${actual}. Options(${eqArgs})`;
|
18
|
+
return `Expected ${pass ? '!' : ''}(${actual}).eq(${expected}). Options(${eqArgs})`;
|
22
19
|
},
|
23
20
|
};
|
24
21
|
},
|