js-draw 0.19.0 → 0.20.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/.eslintrc.js +1 -0
- package/CHANGELOG.md +3 -0
- package/dist/bundle.js +2 -2
- package/dist/bundledStyles.js +1 -1
- package/dist/cjs/src/Color4.js +3 -3
- package/dist/cjs/src/Editor.js +5 -5
- package/dist/cjs/src/SVGLoader.js +69 -7
- package/dist/cjs/src/Viewport.d.ts +2 -0
- package/dist/cjs/src/Viewport.js +6 -2
- package/dist/cjs/src/components/{ImageBackground.d.ts → BackgroundComponent.d.ts} +23 -3
- package/dist/cjs/src/components/BackgroundComponent.js +309 -0
- package/dist/cjs/src/components/Stroke.js +1 -1
- package/dist/cjs/src/components/TextComponent.d.ts +1 -13
- package/dist/cjs/src/components/TextComponent.js +1 -1
- package/dist/cjs/src/components/lib.d.ts +2 -2
- package/dist/cjs/src/components/lib.js +2 -2
- package/dist/cjs/src/components/util/StrokeSmoother.js +25 -15
- package/dist/cjs/src/localizations/de.js +1 -1
- package/dist/cjs/src/localizations/es.js +1 -1
- package/dist/cjs/src/math/Path.js +1 -1
- package/dist/cjs/src/math/polynomial/QuadraticBezier.d.ts +28 -0
- package/dist/cjs/src/math/polynomial/QuadraticBezier.js +115 -0
- package/dist/cjs/src/math/polynomial/solveQuadratic.d.ts +6 -0
- package/dist/cjs/src/math/polynomial/solveQuadratic.js +36 -0
- package/dist/cjs/src/rendering/renderers/CanvasRenderer.js +5 -3
- package/dist/cjs/src/rendering/renderers/SVGRenderer.js +15 -6
- package/dist/cjs/src/toolbar/localization.d.ts +2 -1
- package/dist/cjs/src/toolbar/localization.js +2 -1
- package/dist/cjs/src/toolbar/widgets/DocumentPropertiesWidget.d.ts +5 -0
- package/dist/cjs/src/toolbar/widgets/DocumentPropertiesWidget.js +77 -2
- package/dist/cjs/src/toolbar/widgets/PenToolWidget.js +1 -1
- package/dist/cjs/src/tools/FindTool.js +1 -1
- package/dist/cjs/src/tools/SoundUITool.js +1 -1
- package/dist/mjs/src/Color4.mjs +3 -3
- package/dist/mjs/src/Editor.mjs +4 -4
- package/dist/mjs/src/SVGLoader.mjs +68 -6
- package/dist/mjs/src/Viewport.d.ts +2 -0
- package/dist/mjs/src/Viewport.mjs +6 -2
- package/dist/mjs/src/components/{ImageBackground.d.ts → BackgroundComponent.d.ts} +23 -3
- package/dist/mjs/src/components/BackgroundComponent.mjs +279 -0
- package/dist/mjs/src/components/Stroke.mjs +1 -1
- package/dist/mjs/src/components/TextComponent.d.ts +1 -13
- package/dist/mjs/src/components/TextComponent.mjs +1 -1
- package/dist/mjs/src/components/lib.d.ts +2 -2
- package/dist/mjs/src/components/lib.mjs +2 -2
- package/dist/mjs/src/components/util/StrokeSmoother.mjs +25 -15
- package/dist/mjs/src/localizations/de.mjs +1 -1
- package/dist/mjs/src/localizations/es.mjs +1 -1
- package/dist/mjs/src/math/Path.mjs +1 -1
- package/dist/mjs/src/math/polynomial/QuadraticBezier.d.ts +28 -0
- package/dist/mjs/src/math/polynomial/QuadraticBezier.mjs +109 -0
- package/dist/mjs/src/math/polynomial/solveQuadratic.d.ts +6 -0
- package/dist/mjs/src/math/polynomial/solveQuadratic.mjs +34 -0
- package/dist/mjs/src/rendering/renderers/CanvasRenderer.mjs +5 -3
- package/dist/mjs/src/rendering/renderers/SVGRenderer.mjs +15 -6
- package/dist/mjs/src/toolbar/localization.d.ts +2 -1
- package/dist/mjs/src/toolbar/localization.mjs +2 -1
- package/dist/mjs/src/toolbar/widgets/DocumentPropertiesWidget.d.ts +5 -0
- package/dist/mjs/src/toolbar/widgets/DocumentPropertiesWidget.mjs +54 -2
- package/dist/mjs/src/toolbar/widgets/PenToolWidget.mjs +1 -1
- package/dist/mjs/src/tools/FindTool.mjs +1 -1
- package/dist/mjs/src/tools/SoundUITool.mjs +1 -1
- package/jest.config.js +1 -1
- package/package.json +14 -14
- package/dist/cjs/src/components/ImageBackground.js +0 -146
- package/dist/mjs/src/components/ImageBackground.mjs +0 -139
@@ -0,0 +1,36 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
/**
|
4
|
+
* Solves an equation of the form ax² + bx + c = 0.
|
5
|
+
* The larger solution is returned first.
|
6
|
+
*/
|
7
|
+
const solveQuadratic = (a, b, c) => {
|
8
|
+
// See also https://en.wikipedia.org/wiki/Quadratic_formula
|
9
|
+
if (a === 0) {
|
10
|
+
let solution;
|
11
|
+
if (b === 0) {
|
12
|
+
solution = c === 0 ? 0 : NaN;
|
13
|
+
}
|
14
|
+
else {
|
15
|
+
// Then we have bx + c = 0
|
16
|
+
// which implies bx = -c.
|
17
|
+
// Thus, x = -c/b
|
18
|
+
solution = -c / b;
|
19
|
+
}
|
20
|
+
return [solution, solution];
|
21
|
+
}
|
22
|
+
const discriminant = b * b - 4 * a * c;
|
23
|
+
if (discriminant < 0) {
|
24
|
+
return [NaN, NaN];
|
25
|
+
}
|
26
|
+
const rootDiscriminant = Math.sqrt(discriminant);
|
27
|
+
const solution1 = (-b + rootDiscriminant) / (2 * a);
|
28
|
+
const solution2 = (-b - rootDiscriminant) / (2 * a);
|
29
|
+
if (solution1 > solution2) {
|
30
|
+
return [solution1, solution2];
|
31
|
+
}
|
32
|
+
else {
|
33
|
+
return [solution2, solution1];
|
34
|
+
}
|
35
|
+
};
|
36
|
+
exports.default = solveQuadratic;
|
@@ -191,14 +191,16 @@ class CanvasRenderer extends AbstractRenderer_1.default {
|
|
191
191
|
}
|
192
192
|
}
|
193
193
|
endObject() {
|
194
|
+
// Cache this.objectLevel — it may be decremented by super.endObject.
|
195
|
+
const objectLevel = this.objectLevel;
|
196
|
+
this.currentObjectBBox = null;
|
197
|
+
super.endObject();
|
194
198
|
if (!this.ignoringObject && this.clipLevels.length > 0) {
|
195
|
-
if (this.clipLevels[this.clipLevels.length - 1] ===
|
199
|
+
if (this.clipLevels[this.clipLevels.length - 1] === objectLevel) {
|
196
200
|
this.ctx.restore();
|
197
201
|
this.clipLevels.pop();
|
198
202
|
}
|
199
203
|
}
|
200
|
-
this.currentObjectBBox = null;
|
201
|
-
super.endObject();
|
202
204
|
// If exiting an object with a too-small-to-draw bounding box,
|
203
205
|
if (this.ignoreObjectsAboveLevel !== null && this.getNestingLevel() <= this.ignoreObjectsAboveLevel) {
|
204
206
|
this.ignoreObjectsAboveLevel = null;
|
@@ -9,6 +9,7 @@ const Path_1 = __importDefault(require("../../math/Path"));
|
|
9
9
|
const rounding_1 = require("../../math/rounding");
|
10
10
|
const Vec2_1 = require("../../math/Vec2");
|
11
11
|
const SVGLoader_1 = require("../../SVGLoader");
|
12
|
+
const RenderingStyle_1 = require("../RenderingStyle");
|
12
13
|
const AbstractRenderer_1 = __importDefault(require("./AbstractRenderer"));
|
13
14
|
exports.renderedStylesheetId = 'js-draw-style-sheet';
|
14
15
|
const svgNameSpace = 'http://www.w3.org/2000/svg';
|
@@ -114,11 +115,10 @@ class SVGRenderer extends AbstractRenderer_1.default {
|
|
114
115
|
(_a = this.objectElems) === null || _a === void 0 ? void 0 : _a.push(pathElem);
|
115
116
|
}
|
116
117
|
drawPath(pathSpec) {
|
117
|
-
var _a;
|
118
118
|
const style = pathSpec.style;
|
119
119
|
const path = Path_1.default.fromRenderable(pathSpec).transformedBy(this.getCanvasToScreenTransform());
|
120
120
|
// Try to extend the previous path, if possible
|
121
|
-
if (
|
121
|
+
if (this.lastPathString.length === 0 || !this.lastPathStyle || !(0, RenderingStyle_1.stylesEqual)(this.lastPathStyle, style)) {
|
122
122
|
this.addPathToSVG();
|
123
123
|
this.lastPathStyle = style;
|
124
124
|
this.lastPathString = [];
|
@@ -232,7 +232,7 @@ class SVGRenderer extends AbstractRenderer_1.default {
|
|
232
232
|
this.objectElems = [];
|
233
233
|
}
|
234
234
|
endObject(loaderData, elemClassNames) {
|
235
|
-
var _a
|
235
|
+
var _a;
|
236
236
|
super.endObject(loaderData);
|
237
237
|
// Don't extend paths across objects
|
238
238
|
this.addPathToSVG();
|
@@ -254,9 +254,18 @@ class SVGRenderer extends AbstractRenderer_1.default {
|
|
254
254
|
}
|
255
255
|
}
|
256
256
|
// Add class names to the object, if given.
|
257
|
-
if (elemClassNames) {
|
258
|
-
|
259
|
-
|
257
|
+
if (elemClassNames && this.objectElems) {
|
258
|
+
if (this.objectElems.length === 1) {
|
259
|
+
this.objectElems[0].classList.add(...elemClassNames);
|
260
|
+
}
|
261
|
+
else {
|
262
|
+
const wrapper = document.createElementNS(svgNameSpace, 'g');
|
263
|
+
wrapper.classList.add(...elemClassNames);
|
264
|
+
for (const elem of this.objectElems) {
|
265
|
+
elem.remove();
|
266
|
+
wrapper.appendChild(elem);
|
267
|
+
}
|
268
|
+
this.elem.appendChild(wrapper);
|
260
269
|
}
|
261
270
|
}
|
262
271
|
}
|
@@ -14,7 +14,7 @@ export interface ToolbarLocalization {
|
|
14
14
|
submit: string;
|
15
15
|
freehandPen: string;
|
16
16
|
pressureSensitiveFreehandPen: string;
|
17
|
-
|
17
|
+
selectPenType: string;
|
18
18
|
colorLabel: string;
|
19
19
|
pen: string;
|
20
20
|
eraser: string;
|
@@ -37,6 +37,7 @@ export interface ToolbarLocalization {
|
|
37
37
|
backgroundColor: string;
|
38
38
|
imageWidthOption: string;
|
39
39
|
imageHeightOption: string;
|
40
|
+
useGridOption: string;
|
40
41
|
toggleOverflow: string;
|
41
42
|
errorImageHasZeroSize: string;
|
42
43
|
dropdownShown: (toolName: string) => string;
|
@@ -23,7 +23,7 @@ exports.defaultToolbarLocalization = {
|
|
23
23
|
duplicateSelection: 'Duplicate selection',
|
24
24
|
undo: 'Undo',
|
25
25
|
redo: 'Redo',
|
26
|
-
|
26
|
+
selectPenType: 'Pen type: ',
|
27
27
|
pickColorFromScreen: 'Pick color from screen',
|
28
28
|
clickToPickColorAnnouncement: 'Click on the screen to pick a color',
|
29
29
|
selectionToolKeyboardShortcuts: 'Selection tool: Use arrow keys to move selected items, lowercase/uppercase ‘i’ and ‘o’ to resize.',
|
@@ -31,6 +31,7 @@ exports.defaultToolbarLocalization = {
|
|
31
31
|
backgroundColor: 'Background Color: ',
|
32
32
|
imageWidthOption: 'Width: ',
|
33
33
|
imageHeightOption: 'Height: ',
|
34
|
+
useGridOption: 'Grid: ',
|
34
35
|
toggleOverflow: 'More',
|
35
36
|
touchPanning: 'Touchscreen panning',
|
36
37
|
freehandPen: 'Freehand',
|
@@ -12,6 +12,11 @@ export default class DocumentPropertiesWidget extends BaseWidget {
|
|
12
12
|
private updateDropdown;
|
13
13
|
private setBackgroundColor;
|
14
14
|
private getBackgroundColor;
|
15
|
+
private removeBackgroundComponents;
|
16
|
+
/** Replace existing background components with a background of the given type. */
|
17
|
+
private setBackgroundType;
|
18
|
+
/** Returns the type of the topmost background component */
|
19
|
+
private getBackgroundType;
|
15
20
|
private updateImportExportRectSize;
|
16
21
|
private static idCounter;
|
17
22
|
protected fillDropdown(dropdown: HTMLElement): boolean;
|
@@ -1,8 +1,34 @@
|
|
1
1
|
"use strict";
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
+
if (k2 === undefined) k2 = k;
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
7
|
+
}
|
8
|
+
Object.defineProperty(o, k2, desc);
|
9
|
+
}) : (function(o, m, k, k2) {
|
10
|
+
if (k2 === undefined) k2 = k;
|
11
|
+
o[k2] = m[k];
|
12
|
+
}));
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
15
|
+
}) : function(o, v) {
|
16
|
+
o["default"] = v;
|
17
|
+
});
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
19
|
+
if (mod && mod.__esModule) return mod;
|
20
|
+
var result = {};
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
22
|
+
__setModuleDefault(result, mod);
|
23
|
+
return result;
|
24
|
+
};
|
2
25
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
27
|
};
|
5
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
29
|
+
const Erase_1 = __importDefault(require("../../commands/Erase"));
|
30
|
+
const uniteCommands_1 = __importDefault(require("../../commands/uniteCommands"));
|
31
|
+
const BackgroundComponent_1 = __importStar(require("../../components/BackgroundComponent"));
|
6
32
|
const EditorImage_1 = require("../../EditorImage");
|
7
33
|
const Rect2_1 = __importDefault(require("../../math/Rect2"));
|
8
34
|
const types_1 = require("../../types");
|
@@ -11,7 +37,7 @@ const makeColorInput_1 = __importDefault(require("../makeColorInput"));
|
|
11
37
|
const BaseWidget_1 = __importDefault(require("./BaseWidget"));
|
12
38
|
class DocumentPropertiesWidget extends BaseWidget_1.default {
|
13
39
|
constructor(editor, localizationTable) {
|
14
|
-
super(editor, '
|
40
|
+
super(editor, 'document-properties-widget', localizationTable);
|
15
41
|
this.updateDropdownContent = () => { };
|
16
42
|
this.dropdownUpdateQueued = false;
|
17
43
|
// Make it possible to open the dropdown, even if this widget isn't selected.
|
@@ -51,6 +77,33 @@ class DocumentPropertiesWidget extends BaseWidget_1.default {
|
|
51
77
|
getBackgroundColor() {
|
52
78
|
return this.editor.estimateBackgroundColor();
|
53
79
|
}
|
80
|
+
removeBackgroundComponents() {
|
81
|
+
const previousBackgrounds = [];
|
82
|
+
for (const component of this.editor.image.getBackgroundComponents()) {
|
83
|
+
if (component instanceof BackgroundComponent_1.default) {
|
84
|
+
previousBackgrounds.push(component);
|
85
|
+
}
|
86
|
+
}
|
87
|
+
return new Erase_1.default(previousBackgrounds);
|
88
|
+
}
|
89
|
+
/** Replace existing background components with a background of the given type. */
|
90
|
+
setBackgroundType(backgroundType) {
|
91
|
+
const prevBackgroundColor = this.editor.estimateBackgroundColor();
|
92
|
+
const newBackground = new BackgroundComponent_1.default(backgroundType, prevBackgroundColor);
|
93
|
+
const addBackgroundCommand = this.editor.image.addElement(newBackground);
|
94
|
+
return (0, uniteCommands_1.default)([this.removeBackgroundComponents(), addBackgroundCommand]);
|
95
|
+
}
|
96
|
+
/** Returns the type of the topmost background component */
|
97
|
+
getBackgroundType() {
|
98
|
+
const backgroundComponents = this.editor.image.getBackgroundComponents();
|
99
|
+
for (let i = backgroundComponents.length - 1; i >= 0; i--) {
|
100
|
+
const component = backgroundComponents[i];
|
101
|
+
if (component instanceof BackgroundComponent_1.default) {
|
102
|
+
return component.getBackgroundType();
|
103
|
+
}
|
104
|
+
}
|
105
|
+
return BackgroundComponent_1.BackgroundType.None;
|
106
|
+
}
|
54
107
|
updateImportExportRectSize(size) {
|
55
108
|
const filterDimension = (dim) => {
|
56
109
|
if (dim !== undefined && (!isFinite(dim) || dim <= 0)) {
|
@@ -79,6 +132,27 @@ class DocumentPropertiesWidget extends BaseWidget_1.default {
|
|
79
132
|
colorInput.id = `${HTMLToolbar_1.toolbarCSSPrefix}docPropertiesColorInput-${DocumentPropertiesWidget.idCounter++}`;
|
80
133
|
backgroundColorLabel.htmlFor = colorInput.id;
|
81
134
|
backgroundColorRow.replaceChildren(backgroundColorLabel, backgroundColorInputContainer);
|
135
|
+
const useGridRow = document.createElement('div');
|
136
|
+
const useGridLabel = document.createElement('label');
|
137
|
+
const useGridCheckbox = document.createElement('input');
|
138
|
+
useGridCheckbox.id = `${HTMLToolbar_1.toolbarCSSPrefix}docPropertiesUseGridCheckbox-${DocumentPropertiesWidget.idCounter++}`;
|
139
|
+
useGridLabel.htmlFor = useGridCheckbox.id;
|
140
|
+
useGridCheckbox.type = 'checkbox';
|
141
|
+
useGridLabel.innerText = this.localizationTable.useGridOption;
|
142
|
+
useGridCheckbox.oninput = () => {
|
143
|
+
const prevBackgroundType = this.getBackgroundType();
|
144
|
+
const wasGrid = prevBackgroundType === BackgroundComponent_1.BackgroundType.Grid;
|
145
|
+
if (wasGrid === useGridCheckbox.checked) {
|
146
|
+
// Already the requested background type.
|
147
|
+
return;
|
148
|
+
}
|
149
|
+
let newBackgroundType = BackgroundComponent_1.BackgroundType.SolidColor;
|
150
|
+
if (useGridCheckbox.checked) {
|
151
|
+
newBackgroundType = BackgroundComponent_1.BackgroundType.Grid;
|
152
|
+
}
|
153
|
+
this.editor.dispatch(this.setBackgroundType(newBackgroundType));
|
154
|
+
};
|
155
|
+
useGridRow.replaceChildren(useGridLabel, useGridCheckbox);
|
82
156
|
const addDimensionRow = (labelContent, onChange) => {
|
83
157
|
const row = document.createElement('div');
|
84
158
|
const label = document.createElement('label');
|
@@ -115,9 +189,10 @@ class DocumentPropertiesWidget extends BaseWidget_1.default {
|
|
115
189
|
const importExportRect = this.editor.getImportExportRect();
|
116
190
|
imageWidthRow.setValue(importExportRect.width);
|
117
191
|
imageHeightRow.setValue(importExportRect.height);
|
192
|
+
useGridCheckbox.checked = this.getBackgroundType() === BackgroundComponent_1.BackgroundType.Grid;
|
118
193
|
};
|
119
194
|
this.updateDropdownContent();
|
120
|
-
container.replaceChildren(backgroundColorRow, imageWidthRow.element, imageHeightRow.element);
|
195
|
+
container.replaceChildren(backgroundColorRow, useGridRow, imageWidthRow.element, imageHeightRow.element);
|
121
196
|
dropdown.replaceChildren(container);
|
122
197
|
return true;
|
123
198
|
}
|
@@ -115,7 +115,7 @@ class PenToolWidget extends BaseToolWidget_1.default {
|
|
115
115
|
objectTypeSelect.id = `${HTMLToolbar_1.toolbarCSSPrefix}penBuilderSelect${PenToolWidget.idCounter++}`;
|
116
116
|
thicknessLabel.innerText = this.localizationTable.thicknessLabel;
|
117
117
|
thicknessLabel.setAttribute('for', thicknessInput.id);
|
118
|
-
objectSelectLabel.innerText = this.localizationTable.
|
118
|
+
objectSelectLabel.innerText = this.localizationTable.selectPenType;
|
119
119
|
objectSelectLabel.setAttribute('for', objectTypeSelect.id);
|
120
120
|
// Use a logarithmic scale for thicknessInput (finer control over thinner strokewidths.)
|
121
121
|
const inverseThicknessInputFn = (t) => Math.log10(t);
|
@@ -1,5 +1,5 @@
|
|
1
1
|
"use strict";
|
2
|
-
// Displays a find dialog that allows the user to search for and focus text.
|
2
|
+
// Displays a find dialog that allows the user to search for and focus text.
|
3
3
|
//
|
4
4
|
// @packageDocumentation
|
5
5
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
@@ -74,7 +74,7 @@ class SoundFeedback {
|
|
74
74
|
announceBoundaryCross(boundaryCrossCount) {
|
75
75
|
this.boundaryGain.gain.cancelScheduledValues(this.ctx.currentTime);
|
76
76
|
this.boundaryGain.gain.setValueAtTime(0, this.ctx.currentTime);
|
77
|
-
this.boundaryGain.gain.linearRampToValueAtTime(0.
|
77
|
+
this.boundaryGain.gain.linearRampToValueAtTime(0.018, this.ctx.currentTime + 0.1);
|
78
78
|
this.boundaryOsc.frequency.setValueAtTime(440 + Math.atan(boundaryCrossCount / 2) * 100, this.ctx.currentTime);
|
79
79
|
this.boundaryGain.gain.linearRampToValueAtTime(0, this.ctx.currentTime + 0.25);
|
80
80
|
}
|
package/dist/mjs/src/Color4.mjs
CHANGED
@@ -167,8 +167,8 @@ export default class Color4 {
|
|
167
167
|
// \ | /
|
168
168
|
// \ | /
|
169
169
|
// . \/ .
|
170
|
-
//
|
171
|
-
// .
|
170
|
+
//
|
171
|
+
// .
|
172
172
|
//
|
173
173
|
// Let z be up and (x, y, 0) be in the plane.
|
174
174
|
//
|
@@ -181,7 +181,7 @@ export default class Color4 {
|
|
181
181
|
// /|
|
182
182
|
// 1/ | (√3)/2
|
183
183
|
// / |
|
184
|
-
// 1/2
|
184
|
+
// 1/2
|
185
185
|
//
|
186
186
|
const minComponent = Math.min(this.r, this.g, this.b);
|
187
187
|
const maxComponent = Math.max(this.r, this.g, this.b);
|
package/dist/mjs/src/Editor.mjs
CHANGED
@@ -31,7 +31,7 @@ import fileToBase64 from './util/fileToBase64.mjs';
|
|
31
31
|
import uniteCommands from './commands/uniteCommands.mjs';
|
32
32
|
import SelectionTool from './tools/SelectionTool/SelectionTool.mjs';
|
33
33
|
import Erase from './commands/Erase.mjs';
|
34
|
-
import
|
34
|
+
import BackgroundComponent, { BackgroundType } from './components/BackgroundComponent.mjs';
|
35
35
|
import sendPenEvent from './testing/sendPenEvent.mjs';
|
36
36
|
/**
|
37
37
|
* The main entrypoint for the full editor.
|
@@ -816,7 +816,7 @@ export class Editor {
|
|
816
816
|
// Find a background component, if one exists.
|
817
817
|
// Use the last (topmost) background component if there are multiple.
|
818
818
|
for (const component of this.image.getBackgroundComponents()) {
|
819
|
-
if (component instanceof
|
819
|
+
if (component instanceof BackgroundComponent) {
|
820
820
|
background = component;
|
821
821
|
}
|
822
822
|
}
|
@@ -829,7 +829,7 @@ export class Editor {
|
|
829
829
|
let background = this.getTopmostBackgroundComponent();
|
830
830
|
if (!background) {
|
831
831
|
const backgroundType = color.eq(Color4.transparent) ? BackgroundType.None : BackgroundType.SolidColor;
|
832
|
-
background = new
|
832
|
+
background = new BackgroundComponent(backgroundType, color);
|
833
833
|
return this.image.addElement(background);
|
834
834
|
}
|
835
835
|
else {
|
@@ -844,7 +844,7 @@ export class Editor {
|
|
844
844
|
var _a;
|
845
845
|
const backgroundColors = [];
|
846
846
|
for (const component of this.image.getBackgroundComponents()) {
|
847
|
-
if (component instanceof
|
847
|
+
if (component instanceof BackgroundComponent) {
|
848
848
|
backgroundColors.push((_a = component.getStyle().color) !== null && _a !== void 0 ? _a : Color4.transparent);
|
849
849
|
}
|
850
850
|
}
|
@@ -8,7 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
8
8
|
});
|
9
9
|
};
|
10
10
|
import Color4 from './Color4.mjs';
|
11
|
-
import
|
11
|
+
import BackgroundComponent, { BackgroundType, backgroundTypeToClassNameMap, imageBackgroundCSSClassName, imageBackgroundGridSizeCSSPrefix, imageBackgroundNonAutomaticSecondaryColorCSSClassName } from './components/BackgroundComponent.mjs';
|
12
12
|
import ImageComponent from './components/ImageComponent.mjs';
|
13
13
|
import Stroke from './components/Stroke.mjs';
|
14
14
|
import SVGGlobalAttributesObject from './components/SVGGlobalAttributesObject.mjs';
|
@@ -150,11 +150,69 @@ export default class SVGLoader {
|
|
150
150
|
});
|
151
151
|
}
|
152
152
|
addBackground(node) {
|
153
|
-
var _a, _b, _c;
|
153
|
+
var _a, _b, _c, _d;
|
154
154
|
return __awaiter(this, void 0, void 0, function* () {
|
155
|
-
|
156
|
-
|
157
|
-
|
155
|
+
// If a grid background,
|
156
|
+
if (node.classList.contains(backgroundTypeToClassNameMap[BackgroundType.Grid])) {
|
157
|
+
let foregroundStr;
|
158
|
+
let backgroundStr;
|
159
|
+
let gridStrokeWidthStr;
|
160
|
+
// If a group,
|
161
|
+
if (node.tagName.toLowerCase() === 'g') {
|
162
|
+
// We expect exactly two children. One of these is the solid
|
163
|
+
// background of the grid
|
164
|
+
if (node.children.length !== 2) {
|
165
|
+
yield this.addUnknownNode(node);
|
166
|
+
return;
|
167
|
+
}
|
168
|
+
const background = node.children[0];
|
169
|
+
const grid = node.children[1];
|
170
|
+
backgroundStr = background.getAttribute('fill');
|
171
|
+
foregroundStr = grid.getAttribute('stroke');
|
172
|
+
gridStrokeWidthStr = grid.getAttribute('stroke-width');
|
173
|
+
}
|
174
|
+
else {
|
175
|
+
backgroundStr = node.getAttribute('fill');
|
176
|
+
foregroundStr = node.getAttribute('stroke');
|
177
|
+
gridStrokeWidthStr = node.getAttribute('stroke-width');
|
178
|
+
}
|
179
|
+
// Default to a transparent background.
|
180
|
+
backgroundStr !== null && backgroundStr !== void 0 ? backgroundStr : (backgroundStr = Color4.transparent.toHexString());
|
181
|
+
// A grid must have a foreground color specified.
|
182
|
+
if (!foregroundStr) {
|
183
|
+
yield this.addUnknownNode(node);
|
184
|
+
return;
|
185
|
+
}
|
186
|
+
// Extract the grid size from the class name
|
187
|
+
let gridSize = undefined;
|
188
|
+
for (const className of node.classList) {
|
189
|
+
if (className.startsWith(imageBackgroundGridSizeCSSPrefix)) {
|
190
|
+
const sizeStr = className.substring(imageBackgroundGridSizeCSSPrefix.length);
|
191
|
+
gridSize = parseFloat(sizeStr.replace(/p/g, '.'));
|
192
|
+
}
|
193
|
+
}
|
194
|
+
let gridStrokeWidth = undefined;
|
195
|
+
if (gridStrokeWidthStr) {
|
196
|
+
gridStrokeWidth = parseFloat(gridStrokeWidthStr);
|
197
|
+
}
|
198
|
+
const backgroundColor = Color4.fromString(backgroundStr);
|
199
|
+
let foregroundColor = Color4.fromString(foregroundStr);
|
200
|
+
// Should the foreground color be determined automatically?
|
201
|
+
if (!node.classList.contains(imageBackgroundNonAutomaticSecondaryColorCSSClassName)) {
|
202
|
+
foregroundColor = undefined;
|
203
|
+
}
|
204
|
+
const elem = BackgroundComponent.ofGrid(backgroundColor, gridSize, foregroundColor, gridStrokeWidth);
|
205
|
+
yield ((_a = this.onAddComponent) === null || _a === void 0 ? void 0 : _a.call(this, elem));
|
206
|
+
}
|
207
|
+
// Otherwise, if just a <path/>, it's a solid color background.
|
208
|
+
else if (node.tagName.toLowerCase() === 'path') {
|
209
|
+
const fill = Color4.fromString((_c = (_b = node.getAttribute('fill')) !== null && _b !== void 0 ? _b : node.style.fill) !== null && _c !== void 0 ? _c : 'black');
|
210
|
+
const elem = new BackgroundComponent(BackgroundType.SolidColor, fill);
|
211
|
+
yield ((_d = this.onAddComponent) === null || _d === void 0 ? void 0 : _d.call(this, elem));
|
212
|
+
}
|
213
|
+
else {
|
214
|
+
yield this.addUnknownNode(node);
|
215
|
+
}
|
158
216
|
});
|
159
217
|
}
|
160
218
|
// If given, 'supportedAttrs' will have x, y, etc. attributes that were used in computing the transform added to it,
|
@@ -322,7 +380,11 @@ export default class SVGLoader {
|
|
322
380
|
let visitChildren = true;
|
323
381
|
switch (node.tagName.toLowerCase()) {
|
324
382
|
case 'g':
|
325
|
-
|
383
|
+
if (node.classList.contains(imageBackgroundCSSClassName)) {
|
384
|
+
yield this.addBackground(node);
|
385
|
+
visitChildren = false;
|
386
|
+
}
|
387
|
+
// Otherwise, continue -- visit the node's children.
|
326
388
|
break;
|
327
389
|
case 'path':
|
328
390
|
if (node.classList.contains(imageBackgroundCSSClassName)) {
|
@@ -50,6 +50,8 @@ export declare class Viewport {
|
|
50
50
|
*/
|
51
51
|
getScaleFactorToNearestPowerOfTen(): number;
|
52
52
|
private getScaleFactorToNearestPowerOf;
|
53
|
+
/** Returns the size of a grid cell (in canvas units) as used by {@link snapToGrid}. */
|
54
|
+
static getGridSize(scaleFactor: number): number;
|
53
55
|
snapToGrid(canvasPos: Point2): Vec3;
|
54
56
|
/** Returns the size of one screen pixel in canvas units. */
|
55
57
|
getSizeOfPixelOnCanvas(): number;
|
@@ -97,10 +97,14 @@ export class Viewport {
|
|
97
97
|
const scaleFactor = this.getScaleFactor();
|
98
98
|
return Math.pow(powerOf, Math.round(Math.log(scaleFactor) / Math.log(powerOf)));
|
99
99
|
}
|
100
|
+
/** Returns the size of a grid cell (in canvas units) as used by {@link snapToGrid}. */
|
101
|
+
static getGridSize(scaleFactor) {
|
102
|
+
return 50 / scaleFactor;
|
103
|
+
}
|
100
104
|
snapToGrid(canvasPos) {
|
105
|
+
const scaleFactor = this.getScaleFactorToNearestPowerOf(2);
|
101
106
|
const snapCoordinate = (coordinate) => {
|
102
|
-
const
|
103
|
-
const roundFactor = scaleFactor / 50;
|
107
|
+
const roundFactor = 1 / Viewport.getGridSize(scaleFactor);
|
104
108
|
const snapped = Math.round(coordinate * roundFactor) / roundFactor;
|
105
109
|
return snapped;
|
106
110
|
};
|
@@ -11,32 +11,52 @@ import { ImageComponentLocalization } from './localization';
|
|
11
11
|
import RestyleableComponent, { ComponentStyle } from './RestylableComponent';
|
12
12
|
export declare enum BackgroundType {
|
13
13
|
SolidColor = 0,
|
14
|
-
|
14
|
+
Grid = 1,
|
15
|
+
None = 2
|
15
16
|
}
|
16
17
|
export declare const imageBackgroundCSSClassName = "js-draw-image-background";
|
17
|
-
export
|
18
|
+
export declare const imageBackgroundGridSizeCSSPrefix = "js-draw-image-background-grid-";
|
19
|
+
export declare const imageBackgroundNonAutomaticSecondaryColorCSSClassName = "js-draw-image-background-non-automatic-secondary-color";
|
20
|
+
export declare const backgroundTypeToClassNameMap: {
|
21
|
+
1: string;
|
22
|
+
0: string;
|
23
|
+
2: string;
|
24
|
+
};
|
25
|
+
export default class BackgroundComponent extends AbstractComponent implements RestyleableComponent {
|
18
26
|
private backgroundType;
|
19
27
|
private mainColor;
|
20
28
|
protected contentBBox: Rect2;
|
21
29
|
private viewportSizeChangeListener;
|
30
|
+
private gridSize;
|
31
|
+
private gridStrokeWidth;
|
32
|
+
private secondaryColor;
|
22
33
|
readonly isRestylableComponent: true;
|
23
34
|
constructor(backgroundType: BackgroundType, mainColor: Color4);
|
35
|
+
static ofGrid(backgroundColor: Color4, gridSize?: number, gridColor?: Color4, gridStrokeWidth?: number): BackgroundComponent;
|
36
|
+
getBackgroundType(): BackgroundType;
|
37
|
+
getMainColor(): Color4;
|
38
|
+
getSecondaryColor(): Color4 | null;
|
39
|
+
getGridSize(): number;
|
24
40
|
getStyle(): ComponentStyle;
|
25
41
|
updateStyle(style: ComponentStyle): SerializableCommand;
|
26
42
|
forceStyle(style: ComponentStyle, editor: Editor | null): void;
|
27
43
|
onAddToImage(image: EditorImage): void;
|
28
44
|
onRemoveFromImage(): void;
|
29
45
|
private recomputeBBox;
|
46
|
+
private generateGridPath;
|
30
47
|
render(canvas: AbstractRenderer, visibleRect?: Rect2): void;
|
31
48
|
intersects(lineSegment: LineSegment2): boolean;
|
32
49
|
isSelectable(): boolean;
|
33
50
|
isBackground(): boolean;
|
34
51
|
protected serializeToJSON(): {
|
35
52
|
mainColor: string;
|
53
|
+
secondaryColor: string | undefined;
|
36
54
|
backgroundType: BackgroundType;
|
55
|
+
gridSize: number;
|
56
|
+
gridStrokeWidth: number;
|
37
57
|
};
|
38
58
|
protected applyTransformation(_affineTransfm: Mat33): void;
|
39
59
|
description(localizationTable: ImageComponentLocalization): string;
|
40
60
|
protected createClone(): AbstractComponent;
|
41
|
-
static deserializeFromJSON(json: any):
|
61
|
+
static deserializeFromJSON(json: any): BackgroundComponent;
|
42
62
|
}
|