js-draw 0.0.10 → 0.1.0
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 +4 -0
- package/dist/bundle.js +1 -1
- package/dist/src/Editor.d.ts +2 -2
- package/dist/src/Editor.js +13 -7
- package/dist/src/EditorImage.d.ts +15 -7
- package/dist/src/EditorImage.js +41 -35
- package/dist/src/SVGLoader.d.ts +3 -2
- package/dist/src/SVGLoader.js +9 -7
- package/dist/src/Viewport.d.ts +4 -0
- package/dist/src/Viewport.js +41 -0
- package/dist/src/components/AbstractComponent.d.ts +3 -2
- package/dist/src/components/AbstractComponent.js +3 -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/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/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 +5 -3
- package/dist/src/geometry/Rect2.d.ts +1 -0
- package/dist/src/geometry/Rect2.js +28 -1
- package/dist/src/{Display.d.ts → rendering/Display.d.ts} +5 -2
- package/dist/src/{Display.js → rendering/Display.js} +33 -4
- package/dist/src/rendering/caching/CacheRecord.d.ts +19 -0
- package/dist/src/rendering/caching/CacheRecord.js +51 -0
- package/dist/src/rendering/caching/CacheRecordManager.d.ts +11 -0
- package/dist/src/rendering/caching/CacheRecordManager.js +39 -0
- package/dist/src/rendering/caching/RenderingCache.d.ts +12 -0
- package/dist/src/rendering/caching/RenderingCache.js +36 -0
- package/dist/src/rendering/caching/RenderingCacheNode.d.ts +28 -0
- package/dist/src/rendering/caching/RenderingCacheNode.js +294 -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 +20 -0
- package/dist/src/rendering/caching/types.js +1 -0
- package/dist/src/rendering/{AbstractRenderer.d.ts → renderers/AbstractRenderer.d.ts} +18 -8
- package/dist/src/rendering/{AbstractRenderer.js → renderers/AbstractRenderer.js} +36 -2
- 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} +4 -3
- package/dist/src/rendering/{SVGRenderer.js → renderers/SVGRenderer.js} +14 -11
- package/dist/src/testing/createEditor.js +1 -1
- package/dist/src/toolbar/HTMLToolbar.js +1 -1
- package/dist/src/tools/SelectionTool.js +9 -24
- package/dist/src/types.d.ts +2 -1
- package/package.json +1 -1
- package/src/Editor.ts +15 -8
- package/src/EditorImage.test.ts +2 -2
- package/src/EditorImage.ts +53 -41
- package/src/SVGLoader.ts +11 -8
- package/src/Viewport.ts +56 -0
- package/src/components/AbstractComponent.ts +6 -2
- package/src/components/SVGGlobalAttributesObject.ts +2 -2
- package/src/components/Stroke.ts +1 -1
- package/src/components/UnknownSVGObject.ts +2 -2
- package/src/components/builders/ArrowBuilder.ts +1 -1
- package/src/components/builders/FreehandLineBuilder.ts +1 -1
- 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.toString.test.ts +12 -2
- package/src/geometry/Path.ts +8 -4
- package/src/geometry/Rect2.test.ts +38 -8
- package/src/geometry/Rect2.ts +32 -1
- package/src/{Display.ts → rendering/Display.ts} +38 -6
- package/src/rendering/caching/CacheRecord.test.ts +49 -0
- package/src/rendering/caching/CacheRecord.ts +72 -0
- package/src/rendering/caching/CacheRecordManager.ts +55 -0
- package/src/rendering/caching/RenderingCache.test.ts +44 -0
- package/src/rendering/caching/RenderingCache.ts +56 -0
- package/src/rendering/caching/RenderingCacheNode.ts +365 -0
- package/src/rendering/caching/testUtils.ts +34 -0
- package/src/rendering/caching/types.ts +35 -0
- package/src/rendering/{AbstractRenderer.ts → renderers/AbstractRenderer.ts} +55 -8
- 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} +17 -13
- package/src/testing/createEditor.ts +1 -1
- package/src/toolbar/HTMLToolbar.ts +1 -1
- package/src/tools/SelectionTool.test.ts +1 -1
- package/src/tools/SelectionTool.ts +12 -33
- 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
package/dist/src/Editor.d.ts
CHANGED
@@ -6,8 +6,8 @@ import UndoRedoHistory from './UndoRedoHistory';
|
|
6
6
|
import Viewport from './Viewport';
|
7
7
|
import { Point2 } from './geometry/Vec2';
|
8
8
|
import HTMLToolbar from './toolbar/HTMLToolbar';
|
9
|
-
import { RenderablePathSpec } from './rendering/AbstractRenderer';
|
10
|
-
import Display, { RenderingMode } from './Display';
|
9
|
+
import { RenderablePathSpec } from './rendering/renderers/AbstractRenderer';
|
10
|
+
import Display, { RenderingMode } from './rendering/Display';
|
11
11
|
import Pointer from './Pointer';
|
12
12
|
import Rect2 from './geometry/Rect2';
|
13
13
|
import { EditorLocalization } from './localization';
|
package/dist/src/Editor.js
CHANGED
@@ -16,8 +16,8 @@ import EventDispatcher from './EventDispatcher';
|
|
16
16
|
import { Vec2 } from './geometry/Vec2';
|
17
17
|
import Vec3 from './geometry/Vec3';
|
18
18
|
import HTMLToolbar from './toolbar/HTMLToolbar';
|
19
|
-
import Display, { RenderingMode } from './Display';
|
20
|
-
import SVGRenderer from './rendering/SVGRenderer';
|
19
|
+
import Display, { RenderingMode } from './rendering/Display';
|
20
|
+
import SVGRenderer from './rendering/renderers/SVGRenderer';
|
21
21
|
import Color4 from './Color4';
|
22
22
|
import SVGLoader from './SVGLoader';
|
23
23
|
import Pointer from './Pointer';
|
@@ -282,7 +282,8 @@ export class Editor {
|
|
282
282
|
const exportRectStrokeWidth = 12;
|
283
283
|
renderer.drawRect(this.importExportViewport.visibleRect, exportRectStrokeWidth, exportRectFill);
|
284
284
|
}
|
285
|
-
this.image.render(renderer, this.viewport);
|
285
|
+
//this.image.render(renderer, this.viewport);
|
286
|
+
this.image.renderWithCache(renderer, this.display.getCache(), this.viewport);
|
286
287
|
this.rerenderQueued = false;
|
287
288
|
}
|
288
289
|
drawWetInk(...path) {
|
@@ -344,20 +345,25 @@ export class Editor {
|
|
344
345
|
loadFrom(loader) {
|
345
346
|
return __awaiter(this, void 0, void 0, function* () {
|
346
347
|
this.showLoadingWarning(0);
|
347
|
-
|
348
|
+
this.display.setDraftMode(true);
|
349
|
+
yield loader.start((component) => {
|
348
350
|
(new EditorImage.AddElementCommand(component)).apply(this);
|
349
351
|
}, (countProcessed, totalToProcess) => {
|
350
|
-
if (countProcessed %
|
352
|
+
if (countProcessed % 500 === 0) {
|
351
353
|
this.showLoadingWarning(countProcessed / totalToProcess);
|
352
|
-
this.rerender(
|
354
|
+
this.rerender();
|
353
355
|
return new Promise(resolve => {
|
354
356
|
requestAnimationFrame(() => resolve());
|
355
357
|
});
|
356
358
|
}
|
357
359
|
return null;
|
360
|
+
}, (importExportRect) => {
|
361
|
+
this.setImportExportRect(importExportRect).apply(this);
|
362
|
+
this.viewport.zoomTo(importExportRect).apply(this);
|
358
363
|
});
|
359
364
|
this.hideLoadingWarning();
|
360
|
-
this.
|
365
|
+
this.display.setDraftMode(false);
|
366
|
+
this.queueRerender();
|
361
367
|
});
|
362
368
|
}
|
363
369
|
// Returns the size of the visible region of the output SVG
|
@@ -1,16 +1,18 @@
|
|
1
1
|
import Editor from './Editor';
|
2
|
-
import AbstractRenderer from './rendering/AbstractRenderer';
|
2
|
+
import AbstractRenderer from './rendering/renderers/AbstractRenderer';
|
3
3
|
import Viewport from './Viewport';
|
4
4
|
import AbstractComponent from './components/AbstractComponent';
|
5
5
|
import Rect2 from './geometry/Rect2';
|
6
6
|
import { EditorLocalization } from './localization';
|
7
|
+
import RenderingCache from './rendering/caching/RenderingCache';
|
8
|
+
export declare const sortLeavesByZIndex: (leaves: Array<ImageNode>) => void;
|
7
9
|
export default class EditorImage {
|
8
10
|
private root;
|
9
11
|
constructor();
|
10
12
|
private addElement;
|
11
13
|
findParent(elem: AbstractComponent): ImageNode | null;
|
12
|
-
|
13
|
-
render(renderer: AbstractRenderer, viewport: Viewport
|
14
|
+
renderWithCache(screenRenderer: AbstractRenderer, cache: RenderingCache, viewport: Viewport): void;
|
15
|
+
render(renderer: AbstractRenderer, viewport: Viewport): void;
|
14
16
|
renderAll(renderer: AbstractRenderer): void;
|
15
17
|
getElementsIntersectingRegion(region: Rect2): AbstractComponent[];
|
16
18
|
static AddElementCommand: {
|
@@ -24,23 +26,29 @@ export default class EditorImage {
|
|
24
26
|
};
|
25
27
|
}
|
26
28
|
export declare type AddElementCommand = typeof EditorImage.AddElementCommand.prototype;
|
29
|
+
declare type TooSmallToRenderCheck = (rect: Rect2) => boolean;
|
27
30
|
export declare class ImageNode {
|
28
31
|
private parent;
|
29
32
|
private content;
|
30
33
|
private bbox;
|
31
34
|
private children;
|
32
35
|
private targetChildCount;
|
33
|
-
private
|
34
|
-
private
|
36
|
+
private id;
|
37
|
+
private static idCounter;
|
35
38
|
constructor(parent?: ImageNode | null);
|
39
|
+
getId(): number;
|
40
|
+
onContentChange(): void;
|
36
41
|
getContent(): AbstractComponent | null;
|
37
42
|
getParent(): ImageNode | null;
|
38
|
-
|
39
|
-
|
43
|
+
getChildrenInRegion(region: Rect2): ImageNode[];
|
44
|
+
getChildrenOrSelfIntersectingRegion(region: Rect2): ImageNode[];
|
45
|
+
getLeavesIntersectingRegion(region: Rect2, isTooSmall?: TooSmallToRenderCheck): ImageNode[];
|
40
46
|
getLeaves(): ImageNode[];
|
41
47
|
addLeaf(leaf: AbstractComponent): ImageNode;
|
42
48
|
getBBox(): Rect2;
|
43
49
|
recomputeBBox(bubbleUp: boolean): void;
|
44
50
|
private rebalance;
|
45
51
|
remove(): void;
|
52
|
+
render(renderer: AbstractRenderer, visibleRect: Rect2): void;
|
46
53
|
}
|
54
|
+
export {};
|
package/dist/src/EditorImage.js
CHANGED
@@ -11,6 +11,9 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
11
11
|
};
|
12
12
|
var _element, _applyByFlattening, _a;
|
13
13
|
import Rect2 from './geometry/Rect2';
|
14
|
+
export const sortLeavesByZIndex = (leaves) => {
|
15
|
+
leaves.sort((a, b) => a.getContent().getZIndex() - b.getContent().getZIndex());
|
16
|
+
};
|
14
17
|
// Handles lookup/storage of elements in the image
|
15
18
|
export default class EditorImage {
|
16
19
|
constructor() {
|
@@ -21,7 +24,7 @@ export default class EditorImage {
|
|
21
24
|
}
|
22
25
|
// Returns the parent of the given element, if it exists.
|
23
26
|
findParent(elem) {
|
24
|
-
const candidates = this.root.
|
27
|
+
const candidates = this.root.getLeavesIntersectingRegion(elem.getBBox());
|
25
28
|
for (const candidate of candidates) {
|
26
29
|
if (candidate.getContent() === elem) {
|
27
30
|
return candidate;
|
@@ -29,29 +32,23 @@ export default class EditorImage {
|
|
29
32
|
}
|
30
33
|
return null;
|
31
34
|
}
|
32
|
-
|
33
|
-
|
35
|
+
renderWithCache(screenRenderer, cache, viewport) {
|
36
|
+
cache.render(screenRenderer, this.root, viewport);
|
34
37
|
}
|
35
|
-
render(renderer, viewport
|
36
|
-
|
37
|
-
const leaves = this.root.getLeavesInRegion(viewport.visibleRect, minFraction);
|
38
|
-
this.sortLeaves(leaves);
|
39
|
-
for (const leaf of leaves) {
|
40
|
-
// Leaves by definition have content
|
41
|
-
leaf.getContent().render(renderer, viewport.visibleRect);
|
42
|
-
}
|
38
|
+
render(renderer, viewport) {
|
39
|
+
this.root.render(renderer, viewport.visibleRect);
|
43
40
|
}
|
44
41
|
// Renders all nodes, even ones not within the viewport
|
45
42
|
renderAll(renderer) {
|
46
43
|
const leaves = this.root.getLeaves();
|
47
|
-
|
44
|
+
sortLeavesByZIndex(leaves);
|
48
45
|
for (const leaf of leaves) {
|
49
46
|
leaf.getContent().render(renderer, leaf.getBBox());
|
50
47
|
}
|
51
48
|
}
|
52
49
|
getElementsIntersectingRegion(region) {
|
53
|
-
const leaves = this.root.
|
54
|
-
|
50
|
+
const leaves = this.root.getLeavesIntersectingRegion(region);
|
51
|
+
sortLeavesByZIndex(leaves);
|
55
52
|
return leaves.map(leaf => leaf.getContent());
|
56
53
|
}
|
57
54
|
}
|
@@ -88,6 +85,7 @@ EditorImage.AddElementCommand = (_a = class {
|
|
88
85
|
_element = new WeakMap(),
|
89
86
|
_applyByFlattening = new WeakMap(),
|
90
87
|
_a);
|
88
|
+
// TODO: Assign leaf nodes to CacheNodes. When leaf nodes are modified, the corresponding CacheNodes can be updated.
|
91
89
|
export class ImageNode {
|
92
90
|
constructor(parent = null) {
|
93
91
|
this.parent = parent;
|
@@ -95,8 +93,13 @@ export class ImageNode {
|
|
95
93
|
this.children = [];
|
96
94
|
this.bbox = Rect2.empty;
|
97
95
|
this.content = null;
|
98
|
-
this.
|
99
|
-
|
96
|
+
this.id = ImageNode.idCounter++;
|
97
|
+
}
|
98
|
+
getId() {
|
99
|
+
return this.id;
|
100
|
+
}
|
101
|
+
onContentChange() {
|
102
|
+
this.id = ImageNode.idCounter++;
|
100
103
|
}
|
101
104
|
getContent() {
|
102
105
|
return this.content;
|
@@ -109,11 +112,17 @@ export class ImageNode {
|
|
109
112
|
return child.getBBox().intersects(region);
|
110
113
|
});
|
111
114
|
}
|
115
|
+
getChildrenOrSelfIntersectingRegion(region) {
|
116
|
+
if (this.content) {
|
117
|
+
return [this];
|
118
|
+
}
|
119
|
+
return this.getChildrenInRegion(region);
|
120
|
+
}
|
112
121
|
// Returns a list of `ImageNode`s with content (and thus no children).
|
113
|
-
|
122
|
+
getLeavesIntersectingRegion(region, isTooSmall) {
|
114
123
|
const result = [];
|
115
124
|
// Don't render if too small
|
116
|
-
if (
|
125
|
+
if (isTooSmall === null || isTooSmall === void 0 ? void 0 : isTooSmall(this.bbox)) {
|
117
126
|
return [];
|
118
127
|
}
|
119
128
|
if (this.content !== null && this.getBBox().intersects(region)) {
|
@@ -121,7 +130,7 @@ export class ImageNode {
|
|
121
130
|
}
|
122
131
|
const children = this.getChildrenInRegion(region);
|
123
132
|
for (const child of children) {
|
124
|
-
result.push(...child.
|
133
|
+
result.push(...child.getLeavesIntersectingRegion(region, isTooSmall));
|
125
134
|
}
|
126
135
|
return result;
|
127
136
|
}
|
@@ -138,6 +147,7 @@ export class ImageNode {
|
|
138
147
|
return result;
|
139
148
|
}
|
140
149
|
addLeaf(leaf) {
|
150
|
+
this.onContentChange();
|
141
151
|
if (this.content === null && this.children.length === 0) {
|
142
152
|
this.content = leaf;
|
143
153
|
this.recomputeBBox(true);
|
@@ -186,17 +196,13 @@ export class ImageNode {
|
|
186
196
|
// this' ancestors bounding boxes. This also re-computes this' bounding box
|
187
197
|
// in the z-direction (z-indicies).
|
188
198
|
recomputeBBox(bubbleUp) {
|
189
|
-
var _a
|
199
|
+
var _a;
|
190
200
|
const oldBBox = this.bbox;
|
191
201
|
if (this.content !== null) {
|
192
202
|
this.bbox = this.content.getBBox();
|
193
|
-
this.minZIndex = this.content.zIndex;
|
194
|
-
this.maxZIndex = this.content.zIndex;
|
195
203
|
}
|
196
204
|
else {
|
197
205
|
this.bbox = Rect2.empty;
|
198
|
-
this.minZIndex = null;
|
199
|
-
this.maxZIndex = null;
|
200
206
|
let isFirst = true;
|
201
207
|
for (const child of this.children) {
|
202
208
|
if (isFirst) {
|
@@ -206,18 +212,10 @@ export class ImageNode {
|
|
206
212
|
else {
|
207
213
|
this.bbox = this.bbox.union(child.getBBox());
|
208
214
|
}
|
209
|
-
(_a = this.minZIndex) !== null && _a !== void 0 ? _a : (this.minZIndex = child.minZIndex);
|
210
|
-
(_b = this.maxZIndex) !== null && _b !== void 0 ? _b : (this.maxZIndex = child.maxZIndex);
|
211
|
-
if (child.minZIndex !== null && this.minZIndex !== null) {
|
212
|
-
this.minZIndex = Math.min(child.minZIndex, this.minZIndex);
|
213
|
-
}
|
214
|
-
if (child.maxZIndex !== null && this.maxZIndex !== null) {
|
215
|
-
this.maxZIndex = Math.max(child.maxZIndex, this.maxZIndex);
|
216
|
-
}
|
217
215
|
}
|
218
216
|
}
|
219
217
|
if (bubbleUp && !oldBBox.eq(this.bbox)) {
|
220
|
-
(
|
218
|
+
(_a = this.parent) === null || _a === void 0 ? void 0 : _a.recomputeBBox(true);
|
221
219
|
}
|
222
220
|
}
|
223
221
|
rebalance() {
|
@@ -243,8 +241,6 @@ export class ImageNode {
|
|
243
241
|
}
|
244
242
|
// Remove this node and all of its children
|
245
243
|
remove() {
|
246
|
-
this.minZIndex = null;
|
247
|
-
this.maxZIndex = null;
|
248
244
|
if (!this.parent) {
|
249
245
|
this.content = null;
|
250
246
|
this.children = [];
|
@@ -264,4 +260,14 @@ export class ImageNode {
|
|
264
260
|
this.parent = null;
|
265
261
|
this.children = [];
|
266
262
|
}
|
263
|
+
render(renderer, visibleRect) {
|
264
|
+
// Don't render components that are < 0.1% of the viewport.
|
265
|
+
const leaves = this.getLeavesIntersectingRegion(visibleRect, rect => renderer.isTooSmallToRender(rect));
|
266
|
+
sortLeavesByZIndex(leaves);
|
267
|
+
for (const leaf of leaves) {
|
268
|
+
// Leaves by definition have content
|
269
|
+
leaf.getContent().render(renderer, visibleRect);
|
270
|
+
}
|
271
|
+
}
|
267
272
|
}
|
273
|
+
ImageNode.idCounter = 0;
|
package/dist/src/SVGLoader.d.ts
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
import Rect2 from './geometry/Rect2';
|
2
|
-
import { ComponentAddedListener, ImageLoader, OnProgressListener } from './types';
|
2
|
+
import { ComponentAddedListener, ImageLoader, OnDetermineExportRectListener, OnProgressListener } from './types';
|
3
3
|
export declare const defaultSVGViewRect: Rect2;
|
4
4
|
export default class SVGLoader implements ImageLoader {
|
5
5
|
private source;
|
6
6
|
private onFinish?;
|
7
7
|
private onAddComponent;
|
8
8
|
private onProgress;
|
9
|
+
private onDetermineExportRect;
|
9
10
|
private processedCount;
|
10
11
|
private totalToProcess;
|
11
12
|
private rootViewBox;
|
@@ -18,6 +19,6 @@ export default class SVGLoader implements ImageLoader {
|
|
18
19
|
private updateSVGAttrs;
|
19
20
|
private visit;
|
20
21
|
private getSourceAttrs;
|
21
|
-
start(onAddComponent: ComponentAddedListener, onProgress: OnProgressListener): Promise<
|
22
|
+
start(onAddComponent: ComponentAddedListener, onProgress: OnProgressListener, onDetermineExportRect?: OnDetermineExportRectListener | null): Promise<void>;
|
22
23
|
static fromString(text: string): SVGLoader;
|
23
24
|
}
|
package/dist/src/SVGLoader.js
CHANGED
@@ -21,6 +21,7 @@ export default class SVGLoader {
|
|
21
21
|
this.onFinish = onFinish;
|
22
22
|
this.onAddComponent = null;
|
23
23
|
this.onProgress = null;
|
24
|
+
this.onDetermineExportRect = null;
|
24
25
|
this.processedCount = 0;
|
25
26
|
this.totalToProcess = 0;
|
26
27
|
}
|
@@ -100,6 +101,7 @@ export default class SVGLoader {
|
|
100
101
|
(_a = this.onAddComponent) === null || _a === void 0 ? void 0 : _a.call(this, component);
|
101
102
|
}
|
102
103
|
updateViewBox(node) {
|
104
|
+
var _a;
|
103
105
|
const viewBoxAttr = node.getAttribute('viewBox');
|
104
106
|
if (this.rootViewBox || !viewBoxAttr) {
|
105
107
|
return;
|
@@ -113,6 +115,7 @@ export default class SVGLoader {
|
|
113
115
|
return;
|
114
116
|
}
|
115
117
|
this.rootViewBox = new Rect2(x, y, width, height);
|
118
|
+
(_a = this.onDetermineExportRect) === null || _a === void 0 ? void 0 : _a.call(this, this.rootViewBox);
|
116
119
|
}
|
117
120
|
updateSVGAttrs(node) {
|
118
121
|
var _a;
|
@@ -154,23 +157,22 @@ export default class SVGLoader {
|
|
154
157
|
return [attr, node.getAttribute(attr)];
|
155
158
|
});
|
156
159
|
}
|
157
|
-
start(onAddComponent, onProgress) {
|
158
|
-
var _a;
|
160
|
+
start(onAddComponent, onProgress, onDetermineExportRect = null) {
|
161
|
+
var _a, _b;
|
159
162
|
return __awaiter(this, void 0, void 0, function* () {
|
160
163
|
this.onAddComponent = onAddComponent;
|
161
164
|
this.onProgress = onProgress;
|
165
|
+
this.onDetermineExportRect = onDetermineExportRect;
|
162
166
|
// Estimate the number of tags to process.
|
163
167
|
this.totalToProcess = this.source.childElementCount;
|
164
168
|
this.processedCount = 0;
|
165
169
|
this.rootViewBox = null;
|
166
170
|
yield this.visit(this.source);
|
167
171
|
const viewBox = this.rootViewBox;
|
168
|
-
|
169
|
-
|
170
|
-
result = Rect2.of(viewBox);
|
172
|
+
if (!viewBox) {
|
173
|
+
(_a = this.onDetermineExportRect) === null || _a === void 0 ? void 0 : _a.call(this, defaultSVGViewRect);
|
171
174
|
}
|
172
|
-
(
|
173
|
-
return result;
|
175
|
+
(_b = this.onFinish) === null || _b === void 0 ? void 0 : _b.call(this);
|
174
176
|
});
|
175
177
|
}
|
176
178
|
// TODO: Handling unsafe data! Tripple-check that this is secure!
|
package/dist/src/Viewport.d.ts
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import Command from './commands/Command';
|
1
2
|
import { CommandLocalization } from './commands/localization';
|
2
3
|
import Editor from './Editor';
|
3
4
|
import Mat33 from './geometry/Mat33';
|
@@ -28,10 +29,13 @@ export declare class Viewport {
|
|
28
29
|
resetTransform(newTransform: Mat33): void;
|
29
30
|
get screenToCanvasTransform(): Mat33;
|
30
31
|
get canvasToScreenTransform(): Mat33;
|
32
|
+
getResolution(): Vec2;
|
31
33
|
getScaleFactor(): number;
|
34
|
+
getSizeOfPixelOnCanvas(): number;
|
32
35
|
getRotationAngle(): number;
|
33
36
|
static roundPoint<T extends Point2 | number>(point: T, tolerance: number): PointDataType<T>;
|
34
37
|
roundPoint(point: Point2): Point2;
|
38
|
+
zoomTo(toMakeVisible: Rect2): Command;
|
35
39
|
}
|
36
40
|
export declare namespace Viewport {
|
37
41
|
type ViewportTransform = typeof Viewport.ViewportTransform.prototype;
|
package/dist/src/Viewport.js
CHANGED
@@ -50,11 +50,17 @@ export class Viewport {
|
|
50
50
|
get canvasToScreenTransform() {
|
51
51
|
return this.transform;
|
52
52
|
}
|
53
|
+
getResolution() {
|
54
|
+
return this.screenRect.size;
|
55
|
+
}
|
53
56
|
// Returns the amount a vector on the canvas is scaled to become a vector on the screen.
|
54
57
|
getScaleFactor() {
|
55
58
|
// Use transformVec3 to avoid translating the vector
|
56
59
|
return this.transform.transformVec3(Vec3.unitX).magnitude();
|
57
60
|
}
|
61
|
+
getSizeOfPixelOnCanvas() {
|
62
|
+
return 1 / this.getScaleFactor();
|
63
|
+
}
|
58
64
|
// Returns the angle of the canvas in radians
|
59
65
|
getRotationAngle() {
|
60
66
|
return this.transform.transformVec3(Vec3.unitX).angle();
|
@@ -76,6 +82,41 @@ export class Viewport {
|
|
76
82
|
roundPoint(point) {
|
77
83
|
return Viewport.roundPoint(point, 1 / this.getScaleFactor());
|
78
84
|
}
|
85
|
+
// Returns a Command that transforms the view such that [rect] is visible, and perhaps
|
86
|
+
// centered in the viewport.
|
87
|
+
// Returns null if no transformation is necessary
|
88
|
+
zoomTo(toMakeVisible) {
|
89
|
+
let transform = Mat33.identity;
|
90
|
+
// Try to move the selection within the center 2/3rds of the viewport.
|
91
|
+
const recomputeTargetRect = () => {
|
92
|
+
// transform transforms objects on the canvas. As such, we need to invert it
|
93
|
+
// to transform the viewport.
|
94
|
+
const visibleRect = this.visibleRect.transformedBoundingBox(transform.inverse());
|
95
|
+
return visibleRect.transformedBoundingBox(Mat33.scaling2D(2 / 3, visibleRect.center));
|
96
|
+
};
|
97
|
+
let targetRect = recomputeTargetRect();
|
98
|
+
const largerThanTarget = targetRect.w < toMakeVisible.w || targetRect.h < toMakeVisible.h;
|
99
|
+
// Ensure that toMakeVisible is at least 1/8th of the visible region.
|
100
|
+
const muchSmallerThanTarget = toMakeVisible.maxDimension / targetRect.maxDimension < 0.125;
|
101
|
+
if (largerThanTarget || muchSmallerThanTarget) {
|
102
|
+
// If larger than the target, ensure that the longest axis is visible.
|
103
|
+
// If smaller, shrink the visible rectangle as much as possible
|
104
|
+
const multiplier = (largerThanTarget ? Math.max : Math.min)(toMakeVisible.w / targetRect.w, toMakeVisible.h / targetRect.h);
|
105
|
+
const visibleRectTransform = Mat33.scaling2D(multiplier, targetRect.topLeft);
|
106
|
+
const viewportContentTransform = visibleRectTransform.inverse();
|
107
|
+
transform = transform.rightMul(viewportContentTransform);
|
108
|
+
}
|
109
|
+
targetRect = recomputeTargetRect();
|
110
|
+
// Ensure that the center of the region is visible
|
111
|
+
if (!targetRect.containsRect(toMakeVisible)) {
|
112
|
+
// target position - current position
|
113
|
+
const translation = toMakeVisible.center.minus(targetRect.center);
|
114
|
+
const visibleRectTransform = Mat33.translation(translation);
|
115
|
+
const viewportContentTransform = visibleRectTransform.inverse();
|
116
|
+
transform = transform.rightMul(viewportContentTransform);
|
117
|
+
}
|
118
|
+
return new Viewport.ViewportTransform(transform);
|
119
|
+
}
|
79
120
|
}
|
80
121
|
// Command that translates/scales the viewport.
|
81
122
|
Viewport.ViewportTransform = (_a = class {
|
@@ -2,14 +2,15 @@ import Command from '../commands/Command';
|
|
2
2
|
import LineSegment2 from '../geometry/LineSegment2';
|
3
3
|
import Mat33 from '../geometry/Mat33';
|
4
4
|
import Rect2 from '../geometry/Rect2';
|
5
|
-
import AbstractRenderer from '../rendering/AbstractRenderer';
|
5
|
+
import AbstractRenderer from '../rendering/renderers/AbstractRenderer';
|
6
6
|
import { ImageComponentLocalization } from './localization';
|
7
7
|
export default abstract class AbstractComponent {
|
8
8
|
protected lastChangedTime: number;
|
9
9
|
protected abstract contentBBox: Rect2;
|
10
|
-
zIndex
|
10
|
+
private zIndex;
|
11
11
|
private static zIndexCounter;
|
12
12
|
protected constructor();
|
13
|
+
getZIndex(): number;
|
13
14
|
getBBox(): Rect2;
|
14
15
|
abstract render(canvas: AbstractRenderer, visibleRect?: Rect2): void;
|
15
16
|
abstract intersects(lineSegment: LineSegment2): boolean;
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import LineSegment2 from '../geometry/LineSegment2';
|
2
2
|
import Mat33 from '../geometry/Mat33';
|
3
3
|
import Rect2 from '../geometry/Rect2';
|
4
|
-
import AbstractRenderer from '../rendering/AbstractRenderer';
|
4
|
+
import AbstractRenderer from '../rendering/renderers/AbstractRenderer';
|
5
5
|
import AbstractComponent from './AbstractComponent';
|
6
6
|
import { ImageComponentLocalization } from './localization';
|
7
7
|
export default class SVGGlobalAttributesObject extends AbstractComponent {
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import Rect2 from '../geometry/Rect2';
|
2
|
-
import SVGRenderer from '../rendering/SVGRenderer';
|
2
|
+
import SVGRenderer from '../rendering/renderers/SVGRenderer';
|
3
3
|
import AbstractComponent from './AbstractComponent';
|
4
4
|
// Stores global SVG attributes (e.g. namespace identifiers.)
|
5
5
|
export default class SVGGlobalAttributesObject extends AbstractComponent {
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import LineSegment2 from '../geometry/LineSegment2';
|
2
2
|
import Mat33 from '../geometry/Mat33';
|
3
3
|
import Rect2 from '../geometry/Rect2';
|
4
|
-
import AbstractRenderer, { RenderablePathSpec } from '../rendering/AbstractRenderer';
|
4
|
+
import AbstractRenderer, { RenderablePathSpec } from '../rendering/renderers/AbstractRenderer';
|
5
5
|
import AbstractComponent from './AbstractComponent';
|
6
6
|
import { ImageComponentLocalization } from './localization';
|
7
7
|
export default class Stroke extends AbstractComponent {
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import LineSegment2 from '../geometry/LineSegment2';
|
2
2
|
import Mat33 from '../geometry/Mat33';
|
3
3
|
import Rect2 from '../geometry/Rect2';
|
4
|
-
import AbstractRenderer from '../rendering/AbstractRenderer';
|
4
|
+
import AbstractRenderer from '../rendering/renderers/AbstractRenderer';
|
5
5
|
import AbstractComponent from './AbstractComponent';
|
6
6
|
import { ImageComponentLocalization } from './localization';
|
7
7
|
export default class UnknownSVGObject extends AbstractComponent {
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import Rect2 from '../geometry/Rect2';
|
2
|
-
import SVGRenderer from '../rendering/SVGRenderer';
|
2
|
+
import SVGRenderer from '../rendering/renderers/SVGRenderer';
|
3
3
|
import AbstractComponent from './AbstractComponent';
|
4
4
|
export default class UnknownSVGObject extends AbstractComponent {
|
5
5
|
constructor(svgObject) {
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import Rect2 from '../../geometry/Rect2';
|
2
|
-
import AbstractRenderer from '../../rendering/AbstractRenderer';
|
2
|
+
import AbstractRenderer from '../../rendering/renderers/AbstractRenderer';
|
3
3
|
import { StrokeDataPoint } from '../../types';
|
4
4
|
import AbstractComponent from '../AbstractComponent';
|
5
5
|
import { ComponentBuilder, ComponentBuilderFactory } from './types';
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import Rect2 from '../../geometry/Rect2';
|
2
|
-
import AbstractRenderer from '../../rendering/AbstractRenderer';
|
2
|
+
import AbstractRenderer from '../../rendering/renderers/AbstractRenderer';
|
3
3
|
import { StrokeDataPoint } from '../../types';
|
4
4
|
import AbstractComponent from '../AbstractComponent';
|
5
5
|
import { ComponentBuilder, ComponentBuilderFactory } from './types';
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import Rect2 from '../../geometry/Rect2';
|
2
|
-
import AbstractRenderer from '../../rendering/AbstractRenderer';
|
2
|
+
import AbstractRenderer from '../../rendering/renderers/AbstractRenderer';
|
3
3
|
import { StrokeDataPoint } from '../../types';
|
4
4
|
import AbstractComponent from '../AbstractComponent';
|
5
5
|
import { ComponentBuilder, ComponentBuilderFactory } from './types';
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import Rect2 from '../../geometry/Rect2';
|
2
|
-
import AbstractRenderer from '../../rendering/AbstractRenderer';
|
2
|
+
import AbstractRenderer from '../../rendering/renderers/AbstractRenderer';
|
3
3
|
import { StrokeDataPoint } from '../../types';
|
4
4
|
import Viewport from '../../Viewport';
|
5
5
|
import AbstractComponent from '../AbstractComponent';
|
@@ -4,6 +4,9 @@ import Vec3 from './Vec3';
|
|
4
4
|
// a two-dimensional affine transformation. (An affine transformation scales/rotates/shears
|
5
5
|
// **and** translates while a linear transformation just scales/rotates/shears).
|
6
6
|
export default class Mat33 {
|
7
|
+
// ⎡ a1 a2 a3 ⎤
|
8
|
+
// ⎢ b1 b2 b3 ⎥
|
9
|
+
// ⎣ c1 c2 c3 ⎦
|
7
10
|
constructor(a1, a2, a3, b1, b2, b3, c1, c2, c3) {
|
8
11
|
this.a1 = a1;
|
9
12
|
this.a2 = a2;
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { Bezier } from 'bezier-js';
|
2
|
-
import { RenderingStyle, RenderablePathSpec } from '../rendering/AbstractRenderer';
|
2
|
+
import { RenderingStyle, RenderablePathSpec } from '../rendering/renderers/AbstractRenderer';
|
3
3
|
import LineSegment2 from './LineSegment2';
|
4
4
|
import Mat33 from './Mat33';
|
5
5
|
import Rect2 from './Rect2';
|
@@ -208,8 +208,8 @@ export default class Path {
|
|
208
208
|
const toRoundedString = (num) => {
|
209
209
|
// Try to remove rounding errors. If the number ends in at least three/four zeroes
|
210
210
|
// (or nines) just one or two digits, it's probably a rounding error.
|
211
|
-
const fixRoundingUpExp = /^([-]?\d
|
212
|
-
const hasRoundingDownExp = /^([-]?)(\d*)\.(\d
|
211
|
+
const fixRoundingUpExp = /^([-]?\d*\.\d{3,})0{4,}\d$/;
|
212
|
+
const hasRoundingDownExp = /^([-]?)(\d*)\.(\d{3,}9{4,}\d)$/;
|
213
213
|
let text = num.toString();
|
214
214
|
if (text.indexOf('.') === -1) {
|
215
215
|
return text;
|
@@ -230,7 +230,9 @@ export default class Path {
|
|
230
230
|
text = `${negativeSign + (preDecimal + carry).toString()}.${newPostDecimal}`;
|
231
231
|
}
|
232
232
|
text = text.replace(fixRoundingUpExp, '$1');
|
233
|
-
// Remove trailing
|
233
|
+
// Remove trailing zeroes
|
234
|
+
text = text.replace(/([.][^0]*)0+$/, '$1');
|
235
|
+
// Remove trailing period
|
234
236
|
return text.replace(/[.]$/, '');
|
235
237
|
};
|
236
238
|
const addCommand = (command, ...points) => {
|
@@ -27,6 +27,7 @@ export default class Rect2 {
|
|
27
27
|
intersects(other: Rect2): boolean;
|
28
28
|
intersection(other: Rect2): Rect2 | null;
|
29
29
|
union(other: Rect2): Rect2;
|
30
|
+
divideIntoGrid(columns: number, rows: number): Rect2[];
|
30
31
|
grownToPoint(point: Point2, margin?: number): Rect2;
|
31
32
|
grownBy(margin: number): Rect2;
|
32
33
|
get corners(): Point2[];
|