js-draw 1.0.0 → 1.0.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.
- package/README.md +20 -6
- package/dist/bundle.js +1 -1
- package/dist/cjs/Editor.js +1 -1
- package/dist/cjs/Editor.loadFrom.test.d.ts +1 -0
- package/dist/cjs/Editor.test.d.ts +1 -0
- package/dist/cjs/Editor.toSVG.test.d.ts +1 -0
- package/dist/cjs/EditorImage.test.d.ts +1 -0
- package/dist/cjs/EventDispatcher.test.d.ts +1 -0
- package/dist/cjs/SVGLoader.test.d.ts +1 -0
- package/dist/cjs/UndoRedoHistory.test.d.ts +1 -0
- package/dist/cjs/commands/uniteCommands.test.d.ts +1 -0
- package/dist/cjs/components/AbstractComponent.transformBy.test.d.ts +1 -0
- package/dist/cjs/components/BackgroundComponent.test.d.ts +1 -0
- package/dist/cjs/components/Stroke.test.d.ts +1 -0
- package/dist/cjs/components/TextComponent.test.d.ts +1 -0
- package/dist/cjs/components/UnknownSVGObject.test.d.ts +1 -0
- package/dist/cjs/components/builders/FreehandLineBuilder.test.d.ts +1 -0
- package/dist/cjs/localizations/getLocalizationTable.test.d.ts +1 -0
- package/dist/cjs/rendering/RenderingStyle.test.d.ts +1 -0
- package/dist/cjs/rendering/caching/CacheRecord.test.d.ts +1 -0
- package/dist/cjs/rendering/caching/RenderingCache.test.d.ts +1 -0
- package/dist/cjs/rendering/renderers/DummyRenderer.test.d.ts +1 -0
- package/dist/cjs/rendering/renderers/TextOnlyRenderer.test.d.ts +1 -0
- package/dist/cjs/shortcuts/KeyBinding.test.d.ts +1 -0
- package/dist/cjs/shortcuts/KeyboardShortcutManager.test.d.ts +1 -0
- package/dist/cjs/toolbar/EdgeToolbar.test.d.ts +1 -0
- package/dist/cjs/tools/Eraser.test.d.ts +1 -0
- package/dist/cjs/tools/FindTool.test.d.ts +1 -0
- package/dist/cjs/tools/InputFilter/InputPipeline.test.d.ts +1 -0
- package/dist/cjs/tools/PanZoom.test.d.ts +1 -0
- package/dist/cjs/tools/Pen.test.d.ts +1 -0
- package/dist/cjs/tools/SelectionTool/SelectionTool.test.d.ts +1 -0
- package/dist/cjs/tools/UndoRedoShortcut.test.d.ts +1 -0
- package/dist/cjs/util/ReactiveValue.test.d.ts +1 -0
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/version.test.d.ts +1 -0
- package/dist/mjs/Editor.loadFrom.test.d.ts +1 -0
- package/dist/mjs/Editor.mjs +1 -1
- package/dist/mjs/Editor.test.d.ts +1 -0
- package/dist/mjs/Editor.toSVG.test.d.ts +1 -0
- package/dist/mjs/EditorImage.test.d.ts +1 -0
- package/dist/mjs/EventDispatcher.test.d.ts +1 -0
- package/dist/mjs/SVGLoader.test.d.ts +1 -0
- package/dist/mjs/UndoRedoHistory.test.d.ts +1 -0
- package/dist/mjs/commands/uniteCommands.test.d.ts +1 -0
- package/dist/mjs/components/AbstractComponent.transformBy.test.d.ts +1 -0
- package/dist/mjs/components/BackgroundComponent.test.d.ts +1 -0
- package/dist/mjs/components/Stroke.test.d.ts +1 -0
- package/dist/mjs/components/TextComponent.test.d.ts +1 -0
- package/dist/mjs/components/UnknownSVGObject.test.d.ts +1 -0
- package/dist/mjs/components/builders/FreehandLineBuilder.test.d.ts +1 -0
- package/dist/mjs/localizations/getLocalizationTable.test.d.ts +1 -0
- package/dist/mjs/rendering/RenderingStyle.test.d.ts +1 -0
- package/dist/mjs/rendering/caching/CacheRecord.test.d.ts +1 -0
- package/dist/mjs/rendering/caching/RenderingCache.test.d.ts +1 -0
- package/dist/mjs/rendering/renderers/DummyRenderer.test.d.ts +1 -0
- package/dist/mjs/rendering/renderers/TextOnlyRenderer.test.d.ts +1 -0
- package/dist/mjs/shortcuts/KeyBinding.test.d.ts +1 -0
- package/dist/mjs/shortcuts/KeyboardShortcutManager.test.d.ts +1 -0
- package/dist/mjs/toolbar/EdgeToolbar.test.d.ts +1 -0
- package/dist/mjs/tools/Eraser.test.d.ts +1 -0
- package/dist/mjs/tools/FindTool.test.d.ts +1 -0
- package/dist/mjs/tools/InputFilter/InputPipeline.test.d.ts +1 -0
- package/dist/mjs/tools/PanZoom.test.d.ts +1 -0
- package/dist/mjs/tools/Pen.test.d.ts +1 -0
- package/dist/mjs/tools/SelectionTool/SelectionTool.test.d.ts +1 -0
- package/dist/mjs/tools/UndoRedoShortcut.test.d.ts +1 -0
- package/dist/mjs/util/ReactiveValue.test.d.ts +1 -0
- package/dist/mjs/version.mjs +1 -1
- package/dist/mjs/version.test.d.ts +1 -0
- package/dist-test/test_imports/package-lock.json +13 -0
- package/dist-test/test_imports/package.json +12 -0
- package/dist-test/test_imports/test-imports.js +11 -0
- package/dist-test/test_imports/test-require.cjs +14 -0
- package/package.json +2 -2
- package/src/Editor.loadFrom.test.ts +24 -0
- package/src/Editor.test.ts +107 -0
- package/src/Editor.toSVG.test.ts +294 -0
- package/src/Editor.ts +1443 -0
- package/src/EditorImage.test.ts +117 -0
- package/src/EditorImage.ts +609 -0
- package/src/EventDispatcher.test.ts +123 -0
- package/src/EventDispatcher.ts +72 -0
- package/src/Pointer.ts +183 -0
- package/src/SVGLoader.test.ts +114 -0
- package/src/SVGLoader.ts +672 -0
- package/src/UndoRedoHistory.test.ts +34 -0
- package/src/UndoRedoHistory.ts +102 -0
- package/src/Viewport.ts +322 -0
- package/src/bundle/bundled.ts +7 -0
- package/src/commands/Command.ts +45 -0
- package/src/commands/Duplicate.ts +75 -0
- package/src/commands/Erase.ts +95 -0
- package/src/commands/SerializableCommand.ts +49 -0
- package/src/commands/UnresolvedCommand.ts +37 -0
- package/src/commands/invertCommand.ts +58 -0
- package/src/commands/lib.ts +16 -0
- package/src/commands/localization.ts +47 -0
- package/src/commands/uniteCommands.test.ts +23 -0
- package/src/commands/uniteCommands.ts +140 -0
- package/src/components/AbstractComponent.transformBy.test.ts +23 -0
- package/src/components/AbstractComponent.ts +383 -0
- package/src/components/BackgroundComponent.test.ts +44 -0
- package/src/components/BackgroundComponent.ts +348 -0
- package/src/components/ImageComponent.ts +176 -0
- package/src/components/RestylableComponent.ts +161 -0
- package/src/components/SVGGlobalAttributesObject.ts +79 -0
- package/src/components/Stroke.test.ts +137 -0
- package/src/components/Stroke.ts +294 -0
- package/src/components/TextComponent.test.ts +202 -0
- package/src/components/TextComponent.ts +429 -0
- package/src/components/UnknownSVGObject.test.ts +10 -0
- package/src/components/UnknownSVGObject.ts +60 -0
- package/src/components/builders/ArrowBuilder.ts +106 -0
- package/src/components/builders/CircleBuilder.ts +100 -0
- package/src/components/builders/FreehandLineBuilder.test.ts +24 -0
- package/src/components/builders/FreehandLineBuilder.ts +210 -0
- package/src/components/builders/LineBuilder.ts +77 -0
- package/src/components/builders/PressureSensitiveFreehandLineBuilder.ts +453 -0
- package/src/components/builders/RectangleBuilder.ts +73 -0
- package/src/components/builders/types.ts +15 -0
- package/src/components/lib.ts +31 -0
- package/src/components/localization.ts +24 -0
- package/src/components/util/StrokeSmoother.ts +302 -0
- package/src/components/util/describeComponentList.ts +18 -0
- package/src/dialogs/makeAboutDialog.ts +82 -0
- package/src/inputEvents.ts +143 -0
- package/src/lib.ts +91 -0
- package/src/localization.ts +34 -0
- package/src/localizations/de.ts +146 -0
- package/src/localizations/en.ts +8 -0
- package/src/localizations/es.ts +74 -0
- package/src/localizations/getLocalizationTable.test.ts +27 -0
- package/src/localizations/getLocalizationTable.ts +74 -0
- package/src/rendering/Display.ts +247 -0
- package/src/rendering/RenderablePathSpec.ts +88 -0
- package/src/rendering/RenderingStyle.test.ts +68 -0
- package/src/rendering/RenderingStyle.ts +55 -0
- package/src/rendering/TextRenderingStyle.ts +55 -0
- package/src/rendering/caching/CacheRecord.test.ts +48 -0
- package/src/rendering/caching/CacheRecord.ts +76 -0
- package/src/rendering/caching/CacheRecordManager.ts +71 -0
- package/src/rendering/caching/RenderingCache.test.ts +43 -0
- package/src/rendering/caching/RenderingCache.ts +66 -0
- package/src/rendering/caching/RenderingCacheNode.ts +404 -0
- package/src/rendering/caching/testUtils.ts +35 -0
- package/src/rendering/caching/types.ts +34 -0
- package/src/rendering/lib.ts +8 -0
- package/src/rendering/localization.ts +20 -0
- package/src/rendering/renderers/AbstractRenderer.ts +232 -0
- package/src/rendering/renderers/CanvasRenderer.ts +312 -0
- package/src/rendering/renderers/DummyRenderer.test.ts +41 -0
- package/src/rendering/renderers/DummyRenderer.ts +142 -0
- package/src/rendering/renderers/SVGRenderer.ts +434 -0
- package/src/rendering/renderers/TextOnlyRenderer.test.ts +34 -0
- package/src/rendering/renderers/TextOnlyRenderer.ts +68 -0
- package/src/shortcuts/KeyBinding.test.ts +61 -0
- package/src/shortcuts/KeyBinding.ts +257 -0
- package/src/shortcuts/KeyboardShortcutManager.test.ts +95 -0
- package/src/shortcuts/KeyboardShortcutManager.ts +163 -0
- package/src/shortcuts/lib.ts +3 -0
- package/src/testing/createEditor.ts +11 -0
- package/src/testing/getUniquePointerId.ts +18 -0
- package/src/testing/lib.ts +3 -0
- package/src/testing/sendPenEvent.ts +36 -0
- package/src/testing/sendTouchEvent.ts +71 -0
- package/src/toolbar/AbstractToolbar.ts +542 -0
- package/src/toolbar/DropdownToolbar.ts +220 -0
- package/src/toolbar/EdgeToolbar.test.ts +54 -0
- package/src/toolbar/EdgeToolbar.ts +543 -0
- package/src/toolbar/IconProvider.ts +861 -0
- package/src/toolbar/constants.ts +1 -0
- package/src/toolbar/lib.ts +6 -0
- package/src/toolbar/localization.ts +136 -0
- package/src/toolbar/types.ts +13 -0
- package/src/toolbar/widgets/ActionButtonWidget.ts +39 -0
- package/src/toolbar/widgets/BaseToolWidget.ts +81 -0
- package/src/toolbar/widgets/BaseWidget.ts +495 -0
- package/src/toolbar/widgets/DocumentPropertiesWidget.ts +250 -0
- package/src/toolbar/widgets/EraserToolWidget.ts +84 -0
- package/src/toolbar/widgets/HandToolWidget.ts +239 -0
- package/src/toolbar/widgets/InsertImageWidget.ts +248 -0
- package/src/toolbar/widgets/OverflowWidget.ts +92 -0
- package/src/toolbar/widgets/PenToolWidget.ts +369 -0
- package/src/toolbar/widgets/SelectionToolWidget.ts +195 -0
- package/src/toolbar/widgets/TextToolWidget.ts +149 -0
- package/src/toolbar/widgets/components/makeColorInput.ts +184 -0
- package/src/toolbar/widgets/components/makeFileInput.ts +128 -0
- package/src/toolbar/widgets/components/makeGridSelector.ts +179 -0
- package/src/toolbar/widgets/components/makeSeparator.ts +17 -0
- package/src/toolbar/widgets/components/makeThicknessSlider.ts +62 -0
- package/src/toolbar/widgets/keybindings.ts +19 -0
- package/src/toolbar/widgets/layout/DropdownLayoutManager.ts +262 -0
- package/src/toolbar/widgets/layout/EdgeToolbarLayoutManager.ts +71 -0
- package/src/toolbar/widgets/layout/types.ts +74 -0
- package/src/toolbar/widgets/lib.ts +13 -0
- package/src/tools/BaseTool.ts +169 -0
- package/src/tools/Eraser.test.ts +103 -0
- package/src/tools/Eraser.ts +173 -0
- package/src/tools/FindTool.test.ts +67 -0
- package/src/tools/FindTool.ts +153 -0
- package/src/tools/InputFilter/FunctionMapper.ts +17 -0
- package/src/tools/InputFilter/InputMapper.ts +41 -0
- package/src/tools/InputFilter/InputPipeline.test.ts +41 -0
- package/src/tools/InputFilter/InputPipeline.ts +34 -0
- package/src/tools/InputFilter/InputStabilizer.ts +254 -0
- package/src/tools/InputFilter/StrokeKeyboardControl.ts +104 -0
- package/src/tools/PanZoom.test.ts +339 -0
- package/src/tools/PanZoom.ts +525 -0
- package/src/tools/PasteHandler.ts +94 -0
- package/src/tools/Pen.test.ts +260 -0
- package/src/tools/Pen.ts +284 -0
- package/src/tools/PipetteTool.ts +84 -0
- package/src/tools/SelectionTool/SelectAllShortcutHandler.ts +29 -0
- package/src/tools/SelectionTool/Selection.ts +647 -0
- package/src/tools/SelectionTool/SelectionHandle.ts +142 -0
- package/src/tools/SelectionTool/SelectionTool.test.ts +370 -0
- package/src/tools/SelectionTool/SelectionTool.ts +510 -0
- package/src/tools/SelectionTool/TransformMode.ts +112 -0
- package/src/tools/SelectionTool/types.ts +11 -0
- package/src/tools/SoundUITool.ts +221 -0
- package/src/tools/TextTool.ts +339 -0
- package/src/tools/ToolController.ts +224 -0
- package/src/tools/ToolEnabledGroup.ts +14 -0
- package/src/tools/ToolSwitcherShortcut.ts +39 -0
- package/src/tools/ToolbarShortcutHandler.ts +39 -0
- package/src/tools/UndoRedoShortcut.test.ts +62 -0
- package/src/tools/UndoRedoShortcut.ts +24 -0
- package/src/tools/keybindings.ts +85 -0
- package/src/tools/lib.ts +22 -0
- package/src/tools/localization.ts +76 -0
- package/src/types.ts +151 -0
- package/src/util/ReactiveValue.test.ts +168 -0
- package/src/util/ReactiveValue.ts +241 -0
- package/src/util/assertions.ts +55 -0
- package/src/util/fileToBase64.ts +18 -0
- package/src/util/guessKeyCodeFromKey.ts +36 -0
- package/src/util/listPrefixMatch.ts +19 -0
- package/src/util/stopPropagationOfScrollingWheelEvents.ts +20 -0
- package/src/util/untilNextAnimationFrame.ts +9 -0
- package/src/util/waitForAll.ts +18 -0
- package/src/util/waitForTimeout.ts +9 -0
- package/src/version.test.ts +12 -0
- package/src/version.ts +3 -0
@@ -0,0 +1,142 @@
|
|
1
|
+
import { Mat33, Rect2, Point2, Vec2, Vec3 } from '@js-draw/math';
|
2
|
+
import Viewport from '../../Viewport';
|
3
|
+
import RenderingStyle from '../RenderingStyle';
|
4
|
+
import TextRenderingStyle from '../TextRenderingStyle';
|
5
|
+
import AbstractRenderer, { RenderableImage } from './AbstractRenderer';
|
6
|
+
|
7
|
+
// Renderer that outputs almost nothing. Useful for automated tests.
|
8
|
+
export default class DummyRenderer extends AbstractRenderer {
|
9
|
+
// Variables that track the state of what's been rendered
|
10
|
+
public clearedCount: number = 0;
|
11
|
+
public renderedPathCount: number = 0;
|
12
|
+
public lastFillStyle: RenderingStyle|null = null;
|
13
|
+
public lastPoint: Point2|null = null;
|
14
|
+
public objectNestingLevel: number = 0;
|
15
|
+
public lastText: string|null = null;
|
16
|
+
public lastImage: RenderableImage|null = null;
|
17
|
+
|
18
|
+
// List of points drawn since the last clear.
|
19
|
+
public pointBuffer: Point2[] = [];
|
20
|
+
|
21
|
+
public constructor(viewport: Viewport) {
|
22
|
+
super(viewport);
|
23
|
+
}
|
24
|
+
|
25
|
+
public displaySize(): Vec2 {
|
26
|
+
// Do we have a stored viewport size?
|
27
|
+
const viewportSize = this.getViewport().getScreenRectSize();
|
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;
|
37
|
+
}
|
38
|
+
|
39
|
+
public clear() {
|
40
|
+
this.clearedCount ++;
|
41
|
+
this.renderedPathCount = 0;
|
42
|
+
this.pointBuffer = [];
|
43
|
+
this.lastText = null;
|
44
|
+
this.lastImage = null;
|
45
|
+
|
46
|
+
// Ensure all objects finished rendering
|
47
|
+
if (this.objectNestingLevel > 0) {
|
48
|
+
throw new Error(
|
49
|
+
`Within an object while clearing! Nesting level: ${this.objectNestingLevel}`
|
50
|
+
);
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
protected beginPath(startPoint: Vec3) {
|
55
|
+
this.lastPoint = startPoint;
|
56
|
+
this.pointBuffer.push(startPoint);
|
57
|
+
}
|
58
|
+
|
59
|
+
protected endPath(style: RenderingStyle) {
|
60
|
+
this.renderedPathCount++;
|
61
|
+
this.lastFillStyle = style;
|
62
|
+
}
|
63
|
+
|
64
|
+
protected lineTo(point: Vec3) {
|
65
|
+
point = this.canvasToScreen(point);
|
66
|
+
|
67
|
+
this.lastPoint = point;
|
68
|
+
this.pointBuffer.push(point);
|
69
|
+
}
|
70
|
+
|
71
|
+
protected moveTo(point: Point2) {
|
72
|
+
point = this.canvasToScreen(point);
|
73
|
+
|
74
|
+
this.lastPoint = point;
|
75
|
+
this.pointBuffer.push(point);
|
76
|
+
}
|
77
|
+
|
78
|
+
protected traceCubicBezierCurve(p1: Vec3, p2: Vec3, p3: Vec3) {
|
79
|
+
p1 = this.canvasToScreen(p1);
|
80
|
+
p2 = this.canvasToScreen(p2);
|
81
|
+
p3 = this.canvasToScreen(p3);
|
82
|
+
|
83
|
+
this.lastPoint = p3;
|
84
|
+
this.pointBuffer.push(p1, p2, p3);
|
85
|
+
}
|
86
|
+
|
87
|
+
protected traceQuadraticBezierCurve(controlPoint: Vec3, endPoint: Vec3) {
|
88
|
+
controlPoint = this.canvasToScreen(controlPoint);
|
89
|
+
endPoint = this.canvasToScreen(endPoint);
|
90
|
+
|
91
|
+
this.lastPoint = endPoint;
|
92
|
+
this.pointBuffer.push(controlPoint, endPoint);
|
93
|
+
}
|
94
|
+
|
95
|
+
public drawPoints(..._points: Vec3[]) {
|
96
|
+
// drawPoints is intended for debugging.
|
97
|
+
// As such, it is unlikely to be the target of automated tests.
|
98
|
+
}
|
99
|
+
|
100
|
+
|
101
|
+
public drawText(text: string, _transform: Mat33, _style: TextRenderingStyle): void {
|
102
|
+
this.lastText = text;
|
103
|
+
}
|
104
|
+
|
105
|
+
public drawImage(image: RenderableImage) {
|
106
|
+
this.lastImage = image;
|
107
|
+
}
|
108
|
+
|
109
|
+
public override startObject(boundingBox: Rect2, _clip: boolean) {
|
110
|
+
super.startObject(boundingBox);
|
111
|
+
|
112
|
+
this.objectNestingLevel += 1;
|
113
|
+
}
|
114
|
+
|
115
|
+
public override endObject() {
|
116
|
+
super.endObject();
|
117
|
+
|
118
|
+
this.objectNestingLevel -= 1;
|
119
|
+
}
|
120
|
+
|
121
|
+
public isTooSmallToRender(_rect: Rect2): boolean {
|
122
|
+
return false;
|
123
|
+
}
|
124
|
+
|
125
|
+
|
126
|
+
public override canRenderFromWithoutDataLoss(other: AbstractRenderer) {
|
127
|
+
return other instanceof DummyRenderer;
|
128
|
+
}
|
129
|
+
|
130
|
+
public override renderFromOtherOfSameType(transform: Mat33, other: AbstractRenderer): void {
|
131
|
+
if (!(other instanceof DummyRenderer)) {
|
132
|
+
throw new Error(`${other} cannot be rendered onto ${this}`);
|
133
|
+
}
|
134
|
+
|
135
|
+
this.renderedPathCount += other.renderedPathCount;
|
136
|
+
this.lastFillStyle = other.lastFillStyle;
|
137
|
+
this.lastPoint = other.lastPoint;
|
138
|
+
this.pointBuffer.push(...other.pointBuffer.map(point => {
|
139
|
+
return transform.transformVec2(point);
|
140
|
+
}));
|
141
|
+
}
|
142
|
+
}
|
@@ -0,0 +1,434 @@
|
|
1
|
+
import { LoadSaveDataTable } from '../../components/AbstractComponent';
|
2
|
+
import { Mat33, Rect2, Point2, Vec2, toRoundedString } from '@js-draw/math';
|
3
|
+
import { svgAttributesDataKey, svgLoaderAttributeContainerID, SVGLoaderUnknownAttribute, SVGLoaderUnknownStyleAttribute, svgStyleAttributesDataKey } from '../../SVGLoader';
|
4
|
+
import Viewport from '../../Viewport';
|
5
|
+
import RenderingStyle, { stylesEqual } from '../RenderingStyle';
|
6
|
+
import TextRenderingStyle from '../TextRenderingStyle';
|
7
|
+
import AbstractRenderer, { RenderableImage } from './AbstractRenderer';
|
8
|
+
import RenderablePathSpec, { pathFromRenderable } from '../RenderablePathSpec';
|
9
|
+
import listPrefixMatch from '../../util/listPrefixMatch';
|
10
|
+
|
11
|
+
export const renderedStylesheetId = 'js-draw-style-sheet';
|
12
|
+
|
13
|
+
const svgNameSpace = 'http://www.w3.org/2000/svg';
|
14
|
+
|
15
|
+
const defaultTextStyle: Partial<TextRenderingStyle> = {
|
16
|
+
fontWeight: '400',
|
17
|
+
fontStyle: 'normal',
|
18
|
+
};
|
19
|
+
|
20
|
+
/**
|
21
|
+
* Renders onto an `SVGElement`.
|
22
|
+
*
|
23
|
+
* @see {@link Editor.toSVG}
|
24
|
+
*/
|
25
|
+
export default class SVGRenderer extends AbstractRenderer {
|
26
|
+
private lastPathStyle: RenderingStyle|null = null;
|
27
|
+
private lastPathString: string[] = [];
|
28
|
+
private lastContainerIDList: string[] = [];
|
29
|
+
|
30
|
+
// Elements that make up the current object (as created by startObject)
|
31
|
+
// if any.
|
32
|
+
private objectElems: SVGElement[]|null = null;
|
33
|
+
|
34
|
+
private overwrittenAttrs: Record<string, string|null> = {};
|
35
|
+
|
36
|
+
/**
|
37
|
+
* Creates a renderer that renders onto `elem`. If `sanitize`, don't render potentially untrusted data.
|
38
|
+
*
|
39
|
+
* `viewport` is used to determine the translation/rotation/scaling/output size of the rendered
|
40
|
+
* data.
|
41
|
+
*/
|
42
|
+
public constructor(private elem: SVGSVGElement, viewport: Viewport, private sanitize: boolean = false) {
|
43
|
+
super(viewport);
|
44
|
+
this.clear();
|
45
|
+
|
46
|
+
this.addStyleSheet();
|
47
|
+
}
|
48
|
+
|
49
|
+
private addStyleSheet() {
|
50
|
+
if (!this.elem.querySelector(`#${renderedStylesheetId}`)) {
|
51
|
+
// Default to rounded strokes.
|
52
|
+
const styleSheet = document.createElementNS('http://www.w3.org/2000/svg', 'style');
|
53
|
+
styleSheet.innerHTML = `
|
54
|
+
path {
|
55
|
+
stroke-linecap: round;
|
56
|
+
stroke-linejoin: round;
|
57
|
+
}
|
58
|
+
|
59
|
+
text {
|
60
|
+
white-space: pre;
|
61
|
+
}
|
62
|
+
`.replace(/\s+/g, '');
|
63
|
+
styleSheet.setAttribute('id', renderedStylesheetId);
|
64
|
+
this.elem.appendChild(styleSheet);
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
// Sets an attribute on the root SVG element.
|
69
|
+
public setRootSVGAttribute(name: string, value: string|null) {
|
70
|
+
if (this.sanitize) {
|
71
|
+
return;
|
72
|
+
}
|
73
|
+
|
74
|
+
// Make the original value of the attribute restorable on clear
|
75
|
+
if (!(name in this.overwrittenAttrs)) {
|
76
|
+
this.overwrittenAttrs[name] = this.elem.getAttribute(name);
|
77
|
+
}
|
78
|
+
|
79
|
+
if (value !== null) {
|
80
|
+
this.elem.setAttribute(name, value);
|
81
|
+
} else {
|
82
|
+
this.elem.removeAttribute(name);
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
public displaySize(): Vec2 {
|
87
|
+
return Vec2.of(this.elem.clientWidth, this.elem.clientHeight);
|
88
|
+
}
|
89
|
+
|
90
|
+
public clear() {
|
91
|
+
this.lastPathString = [];
|
92
|
+
this.lastContainerIDList = [];
|
93
|
+
|
94
|
+
if (!this.sanitize) {
|
95
|
+
// Restore all all attributes
|
96
|
+
for (const attrName in this.overwrittenAttrs) {
|
97
|
+
const value = this.overwrittenAttrs[attrName];
|
98
|
+
|
99
|
+
if (value) {
|
100
|
+
this.elem.setAttribute(attrName, value);
|
101
|
+
} else {
|
102
|
+
this.elem.removeAttribute(attrName);
|
103
|
+
}
|
104
|
+
}
|
105
|
+
this.overwrittenAttrs = {};
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
// Push `this.fullPath` to the SVG. Returns the path added to the SVG, if any.
|
110
|
+
// @internal
|
111
|
+
protected addPathToSVG() {
|
112
|
+
if (!this.lastPathStyle || this.lastPathString.length === 0) {
|
113
|
+
return null;
|
114
|
+
}
|
115
|
+
|
116
|
+
const pathElem = document.createElementNS(svgNameSpace, 'path');
|
117
|
+
pathElem.setAttribute('d', this.lastPathString.join(' '));
|
118
|
+
|
119
|
+
const style = this.lastPathStyle;
|
120
|
+
if (style.fill.a > 0) {
|
121
|
+
pathElem.setAttribute('fill', style.fill.toHexString());
|
122
|
+
} else {
|
123
|
+
pathElem.setAttribute('fill', 'none');
|
124
|
+
}
|
125
|
+
|
126
|
+
if (style.stroke) {
|
127
|
+
pathElem.setAttribute('stroke', style.stroke.color.toHexString());
|
128
|
+
pathElem.setAttribute('stroke-width', toRoundedString(style.stroke.width));
|
129
|
+
}
|
130
|
+
|
131
|
+
this.elem.appendChild(pathElem);
|
132
|
+
this.objectElems?.push(pathElem);
|
133
|
+
|
134
|
+
return pathElem;
|
135
|
+
}
|
136
|
+
|
137
|
+
public override drawPath(pathSpec: RenderablePathSpec) {
|
138
|
+
const style = pathSpec.style;
|
139
|
+
const path = pathFromRenderable(pathSpec).transformedBy(this.getCanvasToScreenTransform());
|
140
|
+
|
141
|
+
// Try to extend the previous path, if possible
|
142
|
+
if (
|
143
|
+
this.lastPathString.length === 0 || !this.lastPathStyle || !stylesEqual(this.lastPathStyle, style)
|
144
|
+
) {
|
145
|
+
this.addPathToSVG();
|
146
|
+
this.lastPathStyle = style;
|
147
|
+
this.lastPathString = [];
|
148
|
+
}
|
149
|
+
this.lastPathString.push(path.toString());
|
150
|
+
}
|
151
|
+
|
152
|
+
// Apply [elemTransform] to [elem]. Uses both a `matrix` and `.x`, `.y` properties if `setXY` is true.
|
153
|
+
// Otherwise, just uses a `matrix`.
|
154
|
+
private transformFrom(elemTransform: Mat33, elem: SVGElement, inCanvasSpace: boolean = false, setXY: boolean = true) {
|
155
|
+
let transform = !inCanvasSpace ? this.getCanvasToScreenTransform().rightMul(elemTransform) : elemTransform;
|
156
|
+
const translation = transform.transformVec2(Vec2.zero);
|
157
|
+
|
158
|
+
if (setXY) {
|
159
|
+
transform = transform.rightMul(Mat33.translation(translation.times(-1)));
|
160
|
+
}
|
161
|
+
|
162
|
+
if (!transform.eq(Mat33.identity)) {
|
163
|
+
elem.style.transform = `matrix(
|
164
|
+
${transform.a1}, ${transform.b1},
|
165
|
+
${transform.a2}, ${transform.b2},
|
166
|
+
${transform.a3}, ${transform.b3}
|
167
|
+
)`;
|
168
|
+
} else {
|
169
|
+
elem.style.transform = '';
|
170
|
+
}
|
171
|
+
|
172
|
+
if (setXY) {
|
173
|
+
elem.setAttribute('x', `${toRoundedString(translation.x)}`);
|
174
|
+
elem.setAttribute('y', `${toRoundedString(translation.y)}`);
|
175
|
+
}
|
176
|
+
}
|
177
|
+
|
178
|
+
private textContainer: SVGTextElement|null = null;
|
179
|
+
private textContainerTransform: Mat33|null = null;
|
180
|
+
private textParentStyle: Partial<TextRenderingStyle>|null = defaultTextStyle;
|
181
|
+
public drawText(text: string, transform: Mat33, style: TextRenderingStyle): void {
|
182
|
+
const applyTextStyles = (elem: SVGTextElement|SVGTSpanElement, style: TextRenderingStyle) => {
|
183
|
+
if (style.fontFamily !== this.textParentStyle?.fontFamily) {
|
184
|
+
elem.style.fontFamily = style.fontFamily;
|
185
|
+
}
|
186
|
+
if (style.fontVariant !== this.textParentStyle?.fontVariant) {
|
187
|
+
elem.style.fontVariant = style.fontVariant ?? '';
|
188
|
+
}
|
189
|
+
if (style.fontWeight !== this.textParentStyle?.fontWeight) {
|
190
|
+
elem.style.fontWeight = style.fontWeight ?? '';
|
191
|
+
}
|
192
|
+
if (style.fontStyle !== this.textParentStyle?.fontStyle) {
|
193
|
+
elem.style.fontStyle = style.fontStyle ?? '';
|
194
|
+
}
|
195
|
+
if (style.size !== this.textParentStyle?.size) {
|
196
|
+
elem.style.fontSize = style.size + 'px';
|
197
|
+
}
|
198
|
+
|
199
|
+
const fillString = style.renderingStyle.fill.toHexString();
|
200
|
+
// TODO: Uncomment at some future major version release --- currently causes incompatibility due
|
201
|
+
// to an SVG parsing bug in older versions.
|
202
|
+
//const parentFillString = this.textParentStyle?.renderingStyle?.fill?.toHexString();
|
203
|
+
//if (fillString !== parentFillString) {
|
204
|
+
elem.style.fill = fillString;
|
205
|
+
//}
|
206
|
+
|
207
|
+
if (style.renderingStyle.stroke) {
|
208
|
+
const strokeStyle = style.renderingStyle.stroke;
|
209
|
+
elem.style.stroke = strokeStyle.color.toHexString();
|
210
|
+
elem.style.strokeWidth = strokeStyle.width + 'px';
|
211
|
+
}
|
212
|
+
};
|
213
|
+
transform = this.getCanvasToScreenTransform().rightMul(transform);
|
214
|
+
|
215
|
+
if (!this.textContainer) {
|
216
|
+
const container = document.createElementNS(svgNameSpace, 'text');
|
217
|
+
container.appendChild(document.createTextNode(text));
|
218
|
+
|
219
|
+
// Don't set .x/.y properties (just use .style.transform).
|
220
|
+
// Child nodes aren't translated by .x/.y properties, but are by .style.transform.
|
221
|
+
const setXY = false;
|
222
|
+
this.transformFrom(transform, container, true, setXY);
|
223
|
+
applyTextStyles(container, style);
|
224
|
+
|
225
|
+
this.elem.appendChild(container);
|
226
|
+
this.objectElems?.push(container);
|
227
|
+
if (this.objectLevel > 0) {
|
228
|
+
this.textContainer = container;
|
229
|
+
this.textContainerTransform = transform;
|
230
|
+
this.textParentStyle = { ...defaultTextStyle, ...style };
|
231
|
+
}
|
232
|
+
} else {
|
233
|
+
const elem = document.createElementNS(svgNameSpace, 'tspan');
|
234
|
+
elem.appendChild(document.createTextNode(text));
|
235
|
+
this.textContainer.appendChild(elem);
|
236
|
+
|
237
|
+
// Make .x/.y relative to the parent.
|
238
|
+
transform = this.textContainerTransform!.inverse().rightMul(transform);
|
239
|
+
|
240
|
+
// .style.transform does nothing to tspan elements. As such, we need to set x/y:
|
241
|
+
const translation = transform.transformVec2(Vec2.zero);
|
242
|
+
elem.setAttribute('x', `${toRoundedString(translation.x)}`);
|
243
|
+
elem.setAttribute('y', `${toRoundedString(translation.y)}`);
|
244
|
+
|
245
|
+
applyTextStyles(elem, style);
|
246
|
+
}
|
247
|
+
}
|
248
|
+
|
249
|
+
public drawImage(image: RenderableImage) {
|
250
|
+
let label = image.label ?? image.image.getAttribute('aria-label') ?? '';
|
251
|
+
if (label === '') {
|
252
|
+
label = image.image.getAttribute('alt') ?? '';
|
253
|
+
}
|
254
|
+
|
255
|
+
const svgImgElem = document.createElementNS(svgNameSpace, 'image');
|
256
|
+
svgImgElem.setAttribute('href', image.base64Url);
|
257
|
+
svgImgElem.setAttribute('width', image.image.getAttribute('width') ?? '');
|
258
|
+
svgImgElem.setAttribute('height', image.image.getAttribute('height') ?? '');
|
259
|
+
svgImgElem.setAttribute('aria-label', label);
|
260
|
+
this.transformFrom(image.transform, svgImgElem);
|
261
|
+
|
262
|
+
this.elem.appendChild(svgImgElem);
|
263
|
+
this.objectElems?.push(svgImgElem);
|
264
|
+
}
|
265
|
+
|
266
|
+
public override startObject(boundingBox: Rect2) {
|
267
|
+
super.startObject(boundingBox);
|
268
|
+
|
269
|
+
// Only accumulate a path within an object
|
270
|
+
this.lastPathString = [];
|
271
|
+
this.lastPathStyle = null;
|
272
|
+
this.textContainer = null;
|
273
|
+
this.textParentStyle = defaultTextStyle;
|
274
|
+
this.objectElems = [];
|
275
|
+
}
|
276
|
+
|
277
|
+
public override endObject(loaderData?: LoadSaveDataTable, elemClassNames?: string[]) {
|
278
|
+
super.endObject(loaderData);
|
279
|
+
|
280
|
+
// Don't extend paths across objects
|
281
|
+
this.addPathToSVG();
|
282
|
+
|
283
|
+
// If empty/not an object, stop.
|
284
|
+
if (!this.objectElems) {
|
285
|
+
return;
|
286
|
+
}
|
287
|
+
|
288
|
+
if (loaderData && !this.sanitize) {
|
289
|
+
// Restore any attributes unsupported by the app.
|
290
|
+
for (const elem of this.objectElems) {
|
291
|
+
const attrs = loaderData[svgAttributesDataKey] as SVGLoaderUnknownAttribute[]|undefined;
|
292
|
+
const styleAttrs = loaderData[svgStyleAttributesDataKey] as SVGLoaderUnknownStyleAttribute[]|undefined;
|
293
|
+
|
294
|
+
if (attrs) {
|
295
|
+
for (const [ attr, value ] of attrs) {
|
296
|
+
elem.setAttribute(attr, value);
|
297
|
+
}
|
298
|
+
}
|
299
|
+
|
300
|
+
if (styleAttrs) {
|
301
|
+
for (const attr of styleAttrs) {
|
302
|
+
elem.style.setProperty(attr.key, attr.value, attr.priority);
|
303
|
+
}
|
304
|
+
}
|
305
|
+
}
|
306
|
+
|
307
|
+
// Update the parent
|
308
|
+
const containerIDData = loaderData[svgLoaderAttributeContainerID];
|
309
|
+
let containerIDList: string[] = [];
|
310
|
+
if (containerIDData && containerIDData[0]) {
|
311
|
+
// If a string list,
|
312
|
+
if ((containerIDData[0] as any).length) {
|
313
|
+
containerIDList = containerIDData[0] as string[];
|
314
|
+
}
|
315
|
+
}
|
316
|
+
|
317
|
+
if (
|
318
|
+
containerIDList.length > 0
|
319
|
+
// containerIDList must share a prefix with the last ID list
|
320
|
+
// otherwise, the z order of elements may have been changed from
|
321
|
+
// the original image.
|
322
|
+
// In the case that the z order has been changed, keep the current
|
323
|
+
// element as a child of the root to preserve z order.
|
324
|
+
&& listPrefixMatch(this.lastContainerIDList, containerIDList)
|
325
|
+
|
326
|
+
// The component can add at most one more parent than the previous item.
|
327
|
+
&& this.lastContainerIDList.length >= containerIDList.length - 1
|
328
|
+
) {
|
329
|
+
// Select the last
|
330
|
+
const containerID = containerIDList[containerIDList.length - 1];
|
331
|
+
|
332
|
+
const containerCandidates = this.elem.querySelectorAll(`g#${containerID}`);
|
333
|
+
if (containerCandidates.length >= 1) {
|
334
|
+
const container = containerCandidates[0];
|
335
|
+
|
336
|
+
// If this is the first time we're entering the group, the
|
337
|
+
// group should be empty.
|
338
|
+
// Otherwise, this may be a case that would break z-ordering.
|
339
|
+
if (container.children.length === 0 || this.lastContainerIDList.length >= containerIDList.length) {
|
340
|
+
// Move all objectElems to the found container
|
341
|
+
for (const elem of this.objectElems) {
|
342
|
+
elem.remove();
|
343
|
+
container.appendChild(elem);
|
344
|
+
}
|
345
|
+
} else {
|
346
|
+
containerIDList = [];
|
347
|
+
}
|
348
|
+
}
|
349
|
+
} else {
|
350
|
+
containerIDList = [];
|
351
|
+
}
|
352
|
+
|
353
|
+
this.lastContainerIDList = containerIDList;
|
354
|
+
}
|
355
|
+
|
356
|
+
// Add class names to the object, if given.
|
357
|
+
if (elemClassNames && this.objectElems) {
|
358
|
+
if (this.objectElems.length === 1) {
|
359
|
+
this.objectElems[0].classList.add(...elemClassNames);
|
360
|
+
} else {
|
361
|
+
const wrapper = document.createElementNS(svgNameSpace, 'g');
|
362
|
+
wrapper.classList.add(...elemClassNames);
|
363
|
+
|
364
|
+
for (const elem of this.objectElems) {
|
365
|
+
elem.remove();
|
366
|
+
wrapper.appendChild(elem);
|
367
|
+
}
|
368
|
+
|
369
|
+
this.elem.appendChild(wrapper);
|
370
|
+
}
|
371
|
+
}
|
372
|
+
}
|
373
|
+
|
374
|
+
// Not implemented -- use drawPath instead.
|
375
|
+
private unimplementedMessage() { throw new Error('Not implemenented!'); }
|
376
|
+
protected beginPath(_startPoint: Point2) { this.unimplementedMessage(); }
|
377
|
+
protected endPath(_style: RenderingStyle) { this.unimplementedMessage(); }
|
378
|
+
protected lineTo(_point: Point2) { this.unimplementedMessage(); }
|
379
|
+
protected moveTo(_point: Point2) { this.unimplementedMessage(); }
|
380
|
+
protected traceCubicBezierCurve(
|
381
|
+
_controlPoint1: Point2, _controlPoint2: Point2, _endPoint: Point2
|
382
|
+
) { this.unimplementedMessage(); }
|
383
|
+
protected traceQuadraticBezierCurve(_controlPoint: Point2, _endPoint: Point2) { this.unimplementedMessage(); }
|
384
|
+
|
385
|
+
public drawPoints(...points: Point2[]) {
|
386
|
+
points.map(point => {
|
387
|
+
const elem = document.createElementNS(svgNameSpace, 'circle');
|
388
|
+
elem.setAttribute('cx', `${point.x}`);
|
389
|
+
elem.setAttribute('cy', `${point.y}`);
|
390
|
+
elem.setAttribute('r', '15');
|
391
|
+
this.elem.appendChild(elem);
|
392
|
+
});
|
393
|
+
}
|
394
|
+
|
395
|
+
// Renders a **copy** of the given element.
|
396
|
+
public drawSVGElem(elem: SVGElement) {
|
397
|
+
if (this.sanitize) {
|
398
|
+
return;
|
399
|
+
}
|
400
|
+
|
401
|
+
// Don't add multiple copies of the default stylesheet.
|
402
|
+
if (elem.tagName.toLowerCase() === 'style' && elem.getAttribute('id') === renderedStylesheetId) {
|
403
|
+
return;
|
404
|
+
}
|
405
|
+
|
406
|
+
const elemToDraw = elem.cloneNode(true) as SVGElement;
|
407
|
+
this.elem.appendChild(elemToDraw);
|
408
|
+
this.objectElems?.push(elemToDraw);
|
409
|
+
}
|
410
|
+
|
411
|
+
public isTooSmallToRender(_rect: Rect2): boolean {
|
412
|
+
return false;
|
413
|
+
}
|
414
|
+
|
415
|
+
// Creates a new SVG element and SVGRenerer with attributes set for the given Viewport.
|
416
|
+
public static fromViewport(viewport: Viewport, sanitize: boolean = true) {
|
417
|
+
const svgNameSpace = 'http://www.w3.org/2000/svg';
|
418
|
+
const result = document.createElementNS(svgNameSpace, 'svg');
|
419
|
+
|
420
|
+
const rect = viewport.getScreenRectSize();
|
421
|
+
// rect.x -> size of rect in x direction, rect.y -> size of rect in y direction.
|
422
|
+
result.setAttribute('viewBox', [0, 0, rect.x, rect.y].map(part => toRoundedString(part)).join(' '));
|
423
|
+
result.setAttribute('width', toRoundedString(rect.x));
|
424
|
+
result.setAttribute('height', toRoundedString(rect.y));
|
425
|
+
|
426
|
+
// Ensure the image can be identified as an SVG if downloaded.
|
427
|
+
// See https://jwatt.org/svg/authoring/
|
428
|
+
result.setAttribute('version', '1.1');
|
429
|
+
result.setAttribute('baseProfile', 'full');
|
430
|
+
result.setAttribute('xmlns', svgNameSpace);
|
431
|
+
|
432
|
+
return { element: result, renderer: new SVGRenderer(result, viewport, sanitize) };
|
433
|
+
}
|
434
|
+
}
|
@@ -0,0 +1,34 @@
|
|
1
|
+
import { ImageComponent, Mat33 } from '../../lib';
|
2
|
+
import createEditor from '../../testing/createEditor';
|
3
|
+
import TextOnlyRenderer from './TextOnlyRenderer';
|
4
|
+
|
5
|
+
describe('TextOnlyRenderer', () => {
|
6
|
+
it('should summarize the number of visible image nodes', () => {
|
7
|
+
const editor = createEditor();
|
8
|
+
|
9
|
+
const htmlImage = new Image();
|
10
|
+
htmlImage.width = 500;
|
11
|
+
htmlImage.height = 200;
|
12
|
+
|
13
|
+
const image = new ImageComponent({
|
14
|
+
transform: Mat33.identity,
|
15
|
+
image: htmlImage,
|
16
|
+
base64Url: '',
|
17
|
+
label: 'Testing...',
|
18
|
+
});
|
19
|
+
editor.dispatch(editor.image.addElement(image));
|
20
|
+
|
21
|
+
const textRenderer = new TextOnlyRenderer(editor.viewport, editor.localization);
|
22
|
+
editor.image.render(textRenderer, editor.viewport);
|
23
|
+
|
24
|
+
// Should contian number of image nodes and the image description
|
25
|
+
expect(textRenderer.getDescription()).toContain('Testing...');
|
26
|
+
expect(textRenderer.getDescription()).toContain('1');
|
27
|
+
|
28
|
+
textRenderer.clear();
|
29
|
+
|
30
|
+
// After clearing, should not contain description of the image/image count
|
31
|
+
expect(textRenderer.getDescription()).not.toContain('Testing...');
|
32
|
+
expect(textRenderer.getDescription()).not.toContain('1');
|
33
|
+
});
|
34
|
+
});
|
@@ -0,0 +1,68 @@
|
|
1
|
+
import { Vec2, Vec3, Rect2, Mat33 } from '@js-draw/math';
|
2
|
+
import Viewport from '../../Viewport';
|
3
|
+
import { TextRendererLocalization } from '../localization';
|
4
|
+
import RenderingStyle from '../RenderingStyle';
|
5
|
+
import TextRenderingStyle from '../TextRenderingStyle';
|
6
|
+
import AbstractRenderer, { RenderableImage } from './AbstractRenderer';
|
7
|
+
|
8
|
+
// Outputs a description of what was rendered.
|
9
|
+
export default class TextOnlyRenderer extends AbstractRenderer {
|
10
|
+
private descriptionBuilder: string[] = [];
|
11
|
+
private pathCount: number = 0;
|
12
|
+
private textNodeCount: number = 0;
|
13
|
+
private imageNodeCount: number = 0;
|
14
|
+
|
15
|
+
public constructor(viewport: Viewport, private localizationTable: TextRendererLocalization) {
|
16
|
+
super(viewport);
|
17
|
+
}
|
18
|
+
|
19
|
+
public displaySize(): Vec3 {
|
20
|
+
// We don't have a graphical display, export a reasonable size.
|
21
|
+
return Vec2.of(500, 500);
|
22
|
+
}
|
23
|
+
|
24
|
+
public clear(): void {
|
25
|
+
this.descriptionBuilder = [];
|
26
|
+
this.pathCount = 0;
|
27
|
+
this.textNodeCount = 0;
|
28
|
+
this.imageNodeCount = 0;
|
29
|
+
}
|
30
|
+
|
31
|
+
public getDescription(): string {
|
32
|
+
return [
|
33
|
+
this.localizationTable.pathNodeCount(this.pathCount),
|
34
|
+
...(this.textNodeCount > 0 ? [this.localizationTable.textNodeCount(this.textNodeCount)] : []),
|
35
|
+
...(this.imageNodeCount > 0 ? [this.localizationTable.imageNodeCount(this.imageNodeCount)] : []),
|
36
|
+
...this.descriptionBuilder
|
37
|
+
].join('\n');
|
38
|
+
}
|
39
|
+
|
40
|
+
protected beginPath(_startPoint: Vec3): void {
|
41
|
+
}
|
42
|
+
protected endPath(_style: RenderingStyle): void {
|
43
|
+
this.pathCount ++;
|
44
|
+
}
|
45
|
+
protected lineTo(_point: Vec3): void {
|
46
|
+
}
|
47
|
+
protected moveTo(_point: Vec3): void {
|
48
|
+
}
|
49
|
+
protected traceCubicBezierCurve(_p1: Vec3, _p2: Vec3, _p3: Vec3): void {
|
50
|
+
}
|
51
|
+
protected traceQuadraticBezierCurve(_controlPoint: Vec3, _endPoint: Vec3): void {
|
52
|
+
}
|
53
|
+
public drawText(text: string, _transform: Mat33, _style: TextRenderingStyle): void {
|
54
|
+
this.descriptionBuilder.push(this.localizationTable.textNode(text));
|
55
|
+
this.textNodeCount ++;
|
56
|
+
}
|
57
|
+
public drawImage(image: RenderableImage) {
|
58
|
+
const label = image.label ? this.localizationTable.imageNode(image.label) : this.localizationTable.unlabeledImageNode;
|
59
|
+
|
60
|
+
this.descriptionBuilder.push(label);
|
61
|
+
this.imageNodeCount ++;
|
62
|
+
}
|
63
|
+
public isTooSmallToRender(rect: Rect2): boolean {
|
64
|
+
return rect.maxDimension < 15 / this.getSizeOfCanvasPixelOnScreen();
|
65
|
+
}
|
66
|
+
public drawPoints(..._points: Vec3[]): void {
|
67
|
+
}
|
68
|
+
}
|