js-draw 0.19.0 → 0.21.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 +11 -0
- package/README.md +4 -4
- 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.d.ts +4 -1
- package/dist/cjs/src/Editor.js +30 -12
- 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/AbstractComponent.d.ts +13 -1
- package/dist/cjs/src/components/AbstractComponent.js +19 -9
- 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.d.ts +1 -0
- package/dist/cjs/src/components/Stroke.js +15 -2
- 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 +26 -18
- package/dist/cjs/src/localizations/de.js +1 -1
- package/dist/cjs/src/localizations/es.js +1 -1
- package/dist/cjs/src/math/LineSegment2.d.ts +2 -0
- package/dist/cjs/src/math/LineSegment2.js +4 -0
- package/dist/cjs/src/math/Path.d.ts +24 -3
- package/dist/cjs/src/math/Path.js +225 -4
- package/dist/cjs/src/math/Rect2.js +4 -3
- package/dist/cjs/src/math/polynomial/QuadraticBezier.d.ts +28 -0
- package/dist/cjs/src/math/polynomial/QuadraticBezier.js +114 -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/HTMLToolbar.js +7 -0
- 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.d.ts +4 -1
- package/dist/mjs/src/Editor.mjs +29 -11
- 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/AbstractComponent.d.ts +13 -1
- package/dist/mjs/src/components/AbstractComponent.mjs +19 -9
- 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.d.ts +1 -0
- package/dist/mjs/src/components/Stroke.mjs +15 -2
- 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 +26 -18
- package/dist/mjs/src/localizations/de.mjs +1 -1
- package/dist/mjs/src/localizations/es.mjs +1 -1
- package/dist/mjs/src/math/LineSegment2.d.ts +2 -0
- package/dist/mjs/src/math/LineSegment2.mjs +4 -0
- package/dist/mjs/src/math/Path.d.ts +24 -3
- package/dist/mjs/src/math/Path.mjs +225 -4
- package/dist/mjs/src/math/Rect2.mjs +4 -3
- package/dist/mjs/src/math/polynomial/QuadraticBezier.d.ts +28 -0
- package/dist/mjs/src/math/polynomial/QuadraticBezier.mjs +108 -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/HTMLToolbar.mjs +8 -1
- 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/src/Coloris.css +52 -0
- package/src/Editor.css +12 -0
- package/src/toolbar/toolbar.css +9 -0
- package/dist/cjs/src/components/ImageBackground.js +0 -146
- package/dist/mjs/src/components/ImageBackground.mjs +0 -139
@@ -0,0 +1,309 @@
|
|
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
|
+
};
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
27
|
+
};
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
29
|
+
exports.backgroundTypeToClassNameMap = exports.imageBackgroundNonAutomaticSecondaryColorCSSClassName = exports.imageBackgroundGridSizeCSSPrefix = exports.imageBackgroundCSSClassName = exports.BackgroundType = void 0;
|
30
|
+
const Color4_1 = __importDefault(require("../Color4"));
|
31
|
+
const EditorImage_1 = require("../EditorImage");
|
32
|
+
const Rect2_1 = __importDefault(require("../math/Rect2"));
|
33
|
+
const AbstractComponent_1 = __importDefault(require("./AbstractComponent"));
|
34
|
+
const RestylableComponent_1 = require("./RestylableComponent");
|
35
|
+
const Path_1 = __importStar(require("../math/Path"));
|
36
|
+
const Vec2_1 = require("../math/Vec2");
|
37
|
+
const Viewport_1 = __importDefault(require("../Viewport"));
|
38
|
+
const rounding_1 = require("../math/rounding");
|
39
|
+
var BackgroundType;
|
40
|
+
(function (BackgroundType) {
|
41
|
+
BackgroundType[BackgroundType["SolidColor"] = 0] = "SolidColor";
|
42
|
+
BackgroundType[BackgroundType["Grid"] = 1] = "Grid";
|
43
|
+
BackgroundType[BackgroundType["None"] = 2] = "None";
|
44
|
+
})(BackgroundType = exports.BackgroundType || (exports.BackgroundType = {}));
|
45
|
+
exports.imageBackgroundCSSClassName = 'js-draw-image-background';
|
46
|
+
// Class name prefix indicating the size of the background's grid cells (if present).
|
47
|
+
exports.imageBackgroundGridSizeCSSPrefix = 'js-draw-image-background-grid-';
|
48
|
+
// Flag included in rendered SVGs (etc) that indicates that the secondary color of the
|
49
|
+
// background has been manually set.
|
50
|
+
exports.imageBackgroundNonAutomaticSecondaryColorCSSClassName = 'js-draw-image-background-non-automatic-secondary-color';
|
51
|
+
exports.backgroundTypeToClassNameMap = {
|
52
|
+
[BackgroundType.Grid]: 'js-draw-image-background-grid',
|
53
|
+
[BackgroundType.SolidColor]: exports.imageBackgroundCSSClassName,
|
54
|
+
[BackgroundType.None]: '',
|
55
|
+
};
|
56
|
+
// Represents the background of the editor's canvas.
|
57
|
+
class BackgroundComponent extends AbstractComponent_1.default {
|
58
|
+
constructor(backgroundType, mainColor) {
|
59
|
+
super('image-background', 0);
|
60
|
+
this.backgroundType = backgroundType;
|
61
|
+
this.mainColor = mainColor;
|
62
|
+
this.viewportSizeChangeListener = null;
|
63
|
+
this.gridSize = Viewport_1.default.getGridSize(2);
|
64
|
+
this.gridStrokeWidth = 0.7;
|
65
|
+
this.secondaryColor = null;
|
66
|
+
// eslint-disable-next-line @typescript-eslint/prefer-as-const
|
67
|
+
this.isRestylableComponent = true;
|
68
|
+
this.contentBBox = Rect2_1.default.empty;
|
69
|
+
}
|
70
|
+
static ofGrid(backgroundColor, gridSize, gridColor, gridStrokeWidth) {
|
71
|
+
const background = new BackgroundComponent(BackgroundType.Grid, backgroundColor);
|
72
|
+
if (gridSize !== undefined) {
|
73
|
+
background.gridSize = gridSize;
|
74
|
+
}
|
75
|
+
if (gridColor !== undefined) {
|
76
|
+
background.secondaryColor = gridColor;
|
77
|
+
}
|
78
|
+
if (gridStrokeWidth !== undefined) {
|
79
|
+
background.gridStrokeWidth = gridStrokeWidth;
|
80
|
+
}
|
81
|
+
return background;
|
82
|
+
}
|
83
|
+
getBackgroundType() {
|
84
|
+
return this.backgroundType;
|
85
|
+
}
|
86
|
+
// @internal
|
87
|
+
getMainColor() {
|
88
|
+
return this.mainColor;
|
89
|
+
}
|
90
|
+
// @internal
|
91
|
+
getSecondaryColor() {
|
92
|
+
return this.secondaryColor;
|
93
|
+
}
|
94
|
+
// @internal
|
95
|
+
getGridSize() {
|
96
|
+
return this.gridSize;
|
97
|
+
}
|
98
|
+
getStyle() {
|
99
|
+
let color = this.mainColor;
|
100
|
+
if (this.backgroundType === BackgroundType.None) {
|
101
|
+
color = undefined;
|
102
|
+
}
|
103
|
+
return {
|
104
|
+
color,
|
105
|
+
};
|
106
|
+
}
|
107
|
+
updateStyle(style) {
|
108
|
+
return (0, RestylableComponent_1.createRestyleComponentCommand)(this.getStyle(), style, this);
|
109
|
+
}
|
110
|
+
// @internal
|
111
|
+
forceStyle(style, editor) {
|
112
|
+
const fill = style.color;
|
113
|
+
if (!fill) {
|
114
|
+
return;
|
115
|
+
}
|
116
|
+
this.mainColor = fill;
|
117
|
+
// A solid background and transparent fill is equivalent to no background.
|
118
|
+
if (fill.eq(Color4_1.default.transparent) && this.backgroundType === BackgroundType.SolidColor) {
|
119
|
+
this.backgroundType = BackgroundType.None;
|
120
|
+
}
|
121
|
+
else if (this.backgroundType === BackgroundType.None) {
|
122
|
+
this.backgroundType = BackgroundType.SolidColor;
|
123
|
+
}
|
124
|
+
if (editor) {
|
125
|
+
editor.image.queueRerenderOf(this);
|
126
|
+
editor.queueRerender();
|
127
|
+
}
|
128
|
+
}
|
129
|
+
onAddToImage(image) {
|
130
|
+
if (this.viewportSizeChangeListener) {
|
131
|
+
console.warn('onAddToImage called when background is already in an image');
|
132
|
+
this.onRemoveFromImage();
|
133
|
+
}
|
134
|
+
this.viewportSizeChangeListener = image.notifier.on(EditorImage_1.EditorImageEventType.ExportViewportChanged, () => {
|
135
|
+
this.recomputeBBox(image);
|
136
|
+
});
|
137
|
+
this.recomputeBBox(image);
|
138
|
+
}
|
139
|
+
onRemoveFromImage() {
|
140
|
+
var _a;
|
141
|
+
(_a = this.viewportSizeChangeListener) === null || _a === void 0 ? void 0 : _a.remove();
|
142
|
+
this.viewportSizeChangeListener = null;
|
143
|
+
}
|
144
|
+
recomputeBBox(image) {
|
145
|
+
const importExportRect = image.getImportExportViewport().visibleRect;
|
146
|
+
if (!this.contentBBox.eq(importExportRect)) {
|
147
|
+
this.contentBBox = importExportRect;
|
148
|
+
// Re-render this if already added to the EditorImage.
|
149
|
+
image.queueRerenderOf(this);
|
150
|
+
}
|
151
|
+
}
|
152
|
+
generateGridPath(visibleRect) {
|
153
|
+
var _a, _b;
|
154
|
+
const targetRect = (_b = (_a = visibleRect === null || visibleRect === void 0 ? void 0 : visibleRect.grownBy(this.gridStrokeWidth)) === null || _a === void 0 ? void 0 : _a.intersection(this.contentBBox)) !== null && _b !== void 0 ? _b : this.contentBBox;
|
155
|
+
const roundDownToGrid = (coord) => Math.floor(coord / this.gridSize) * this.gridSize;
|
156
|
+
const roundUpToGrid = (coord) => Math.ceil(coord / this.gridSize) * this.gridSize;
|
157
|
+
const startY = roundUpToGrid(targetRect.y);
|
158
|
+
const endY = roundDownToGrid(targetRect.y + targetRect.h);
|
159
|
+
const startX = roundUpToGrid(targetRect.x);
|
160
|
+
const endX = roundDownToGrid(targetRect.x + targetRect.w);
|
161
|
+
const result = [];
|
162
|
+
// Don't generate grids with a huge number of rows/columns -- such grids
|
163
|
+
// take a long time to render and are likely invisible due to the number of
|
164
|
+
// cells.
|
165
|
+
const rowCount = (endY - startY) / this.gridSize;
|
166
|
+
const colCount = (endX - startX) / this.gridSize;
|
167
|
+
const maxGridCols = 1000;
|
168
|
+
const maxGridRows = 1000;
|
169
|
+
if (rowCount > maxGridRows || colCount > maxGridCols) {
|
170
|
+
return Path_1.default.empty;
|
171
|
+
}
|
172
|
+
const startPoint = Vec2_1.Vec2.of(targetRect.x, startY);
|
173
|
+
for (let y = startY; y <= endY; y += this.gridSize) {
|
174
|
+
result.push({
|
175
|
+
kind: Path_1.PathCommandType.MoveTo,
|
176
|
+
point: Vec2_1.Vec2.of(targetRect.x, y),
|
177
|
+
});
|
178
|
+
result.push({
|
179
|
+
kind: Path_1.PathCommandType.LineTo,
|
180
|
+
point: Vec2_1.Vec2.of(targetRect.x + targetRect.w, y),
|
181
|
+
});
|
182
|
+
}
|
183
|
+
for (let x = startX; x <= endX; x += this.gridSize) {
|
184
|
+
result.push({
|
185
|
+
kind: Path_1.PathCommandType.MoveTo,
|
186
|
+
point: Vec2_1.Vec2.of(x, targetRect.y),
|
187
|
+
});
|
188
|
+
result.push({
|
189
|
+
kind: Path_1.PathCommandType.LineTo,
|
190
|
+
point: Vec2_1.Vec2.of(x, targetRect.y + targetRect.h)
|
191
|
+
});
|
192
|
+
}
|
193
|
+
return new Path_1.default(startPoint, result);
|
194
|
+
}
|
195
|
+
render(canvas, visibleRect) {
|
196
|
+
if (this.backgroundType === BackgroundType.None) {
|
197
|
+
return;
|
198
|
+
}
|
199
|
+
const clip = true;
|
200
|
+
canvas.startObject(this.contentBBox, clip);
|
201
|
+
if (this.backgroundType === BackgroundType.SolidColor || this.backgroundType === BackgroundType.Grid) {
|
202
|
+
// If the rectangle for this region contains the visible rect,
|
203
|
+
// we can fill the entire visible rectangle (which may be more efficient than
|
204
|
+
// filling the entire region for this.)
|
205
|
+
if (visibleRect) {
|
206
|
+
const intersection = visibleRect.intersection(this.contentBBox);
|
207
|
+
if (intersection) {
|
208
|
+
canvas.fillRect(intersection, this.mainColor);
|
209
|
+
}
|
210
|
+
}
|
211
|
+
else {
|
212
|
+
canvas.fillRect(this.contentBBox, this.mainColor);
|
213
|
+
}
|
214
|
+
}
|
215
|
+
if (this.backgroundType === BackgroundType.Grid) {
|
216
|
+
let gridColor = this.secondaryColor;
|
217
|
+
gridColor !== null && gridColor !== void 0 ? gridColor : (gridColor = Color4_1.default.ofRGBA(1 - this.mainColor.r, 1 - this.mainColor.g, 1 - this.mainColor.b, 0.2));
|
218
|
+
// If the background fill is completely transparent, ensure visibility on otherwise light
|
219
|
+
// and dark backgrounds.
|
220
|
+
if (this.mainColor.a === 0) {
|
221
|
+
gridColor = Color4_1.default.ofRGBA(0.5, 0.5, 0.5, 0.2);
|
222
|
+
}
|
223
|
+
const style = {
|
224
|
+
fill: Color4_1.default.transparent,
|
225
|
+
stroke: { width: this.gridStrokeWidth, color: gridColor }
|
226
|
+
};
|
227
|
+
canvas.drawPath(this.generateGridPath(visibleRect).toRenderable(style));
|
228
|
+
}
|
229
|
+
const backgroundTypeCSSClass = exports.backgroundTypeToClassNameMap[this.backgroundType];
|
230
|
+
const classNames = [exports.imageBackgroundCSSClassName];
|
231
|
+
if (backgroundTypeCSSClass !== exports.imageBackgroundCSSClassName) {
|
232
|
+
classNames.push(backgroundTypeCSSClass);
|
233
|
+
const gridSizeStr = (0, rounding_1.toRoundedString)(this.gridSize).replace(/[.]/g, 'p');
|
234
|
+
classNames.push(exports.imageBackgroundGridSizeCSSPrefix + gridSizeStr);
|
235
|
+
}
|
236
|
+
if (this.secondaryColor !== null) {
|
237
|
+
classNames.push(exports.imageBackgroundNonAutomaticSecondaryColorCSSClassName);
|
238
|
+
}
|
239
|
+
canvas.endObject(this.getLoadSaveData(), classNames);
|
240
|
+
}
|
241
|
+
intersects(lineSegment) {
|
242
|
+
return this.contentBBox.getEdges().some(edge => edge.intersects(lineSegment));
|
243
|
+
}
|
244
|
+
isSelectable() {
|
245
|
+
return false;
|
246
|
+
}
|
247
|
+
isBackground() {
|
248
|
+
return true;
|
249
|
+
}
|
250
|
+
serializeToJSON() {
|
251
|
+
var _a;
|
252
|
+
return {
|
253
|
+
mainColor: this.mainColor.toHexString(),
|
254
|
+
secondaryColor: (_a = this.secondaryColor) === null || _a === void 0 ? void 0 : _a.toHexString(),
|
255
|
+
backgroundType: this.backgroundType,
|
256
|
+
gridSize: this.gridSize,
|
257
|
+
gridStrokeWidth: this.gridStrokeWidth,
|
258
|
+
};
|
259
|
+
}
|
260
|
+
applyTransformation(_affineTransfm) {
|
261
|
+
// Do nothing — it doesn't make sense to transform the background.
|
262
|
+
}
|
263
|
+
description(localizationTable) {
|
264
|
+
if (this.backgroundType === BackgroundType.SolidColor) {
|
265
|
+
return localizationTable.filledBackgroundWithColor(this.mainColor.toString());
|
266
|
+
}
|
267
|
+
else {
|
268
|
+
return localizationTable.emptyBackground;
|
269
|
+
}
|
270
|
+
}
|
271
|
+
createClone() {
|
272
|
+
return new BackgroundComponent(this.backgroundType, this.mainColor);
|
273
|
+
}
|
274
|
+
// @internal
|
275
|
+
static deserializeFromJSON(json) {
|
276
|
+
var _a, _b;
|
277
|
+
if (typeof json === 'string') {
|
278
|
+
json = JSON.parse(json);
|
279
|
+
}
|
280
|
+
if (typeof json.mainColor !== 'string') {
|
281
|
+
throw new Error('Error deserializing — mainColor must be of type string.');
|
282
|
+
}
|
283
|
+
let backgroundType;
|
284
|
+
const jsonBackgroundType = json.backgroundType;
|
285
|
+
if (jsonBackgroundType === BackgroundType.None || jsonBackgroundType === BackgroundType.Grid
|
286
|
+
|| jsonBackgroundType === BackgroundType.SolidColor) {
|
287
|
+
backgroundType = jsonBackgroundType;
|
288
|
+
}
|
289
|
+
else {
|
290
|
+
const exhaustivenessCheck = jsonBackgroundType;
|
291
|
+
return exhaustivenessCheck;
|
292
|
+
}
|
293
|
+
const mainColor = Color4_1.default.fromHex(json.mainColor);
|
294
|
+
const secondaryColor = json.secondaryColor ? Color4_1.default.fromHex(json.secondaryColor) : null;
|
295
|
+
const gridSize = (_a = json.gridSize) !== null && _a !== void 0 ? _a : undefined;
|
296
|
+
const gridStrokeWidth = (_b = json.gridStrokeWidth) !== null && _b !== void 0 ? _b : undefined;
|
297
|
+
const result = new BackgroundComponent(backgroundType, mainColor);
|
298
|
+
result.secondaryColor = secondaryColor;
|
299
|
+
if (gridSize) {
|
300
|
+
result.gridSize = gridSize;
|
301
|
+
}
|
302
|
+
if (gridStrokeWidth) {
|
303
|
+
result.gridStrokeWidth = gridStrokeWidth;
|
304
|
+
}
|
305
|
+
return result;
|
306
|
+
}
|
307
|
+
}
|
308
|
+
exports.default = BackgroundComponent;
|
309
|
+
AbstractComponent_1.default.registerComponent('image-background', BackgroundComponent.deserializeFromJSON);
|
@@ -53,6 +53,7 @@ export default class Stroke extends AbstractComponent implements RestyleableComp
|
|
53
53
|
render(canvas: AbstractRenderer, visibleRect?: Rect2): void;
|
54
54
|
getProportionalRenderingTime(): number;
|
55
55
|
private bboxForPart;
|
56
|
+
getExactBBox(): Rect2;
|
56
57
|
protected applyTransformation(affineTransfm: Mat33): void;
|
57
58
|
/**
|
58
59
|
* @returns the {@link Path.union} of all paths that make up this stroke.
|
@@ -114,8 +114,11 @@ class Stroke extends AbstractComponent_1.default {
|
|
114
114
|
}
|
115
115
|
}
|
116
116
|
intersects(line) {
|
117
|
+
var _a;
|
117
118
|
for (const part of this.parts) {
|
118
|
-
|
119
|
+
const strokeWidth = (_a = part.style.stroke) === null || _a === void 0 ? void 0 : _a.width;
|
120
|
+
const strokeRadius = strokeWidth ? strokeWidth / 2 : undefined;
|
121
|
+
if (part.path.intersection(line, strokeRadius).length > 0) {
|
119
122
|
return true;
|
120
123
|
}
|
121
124
|
}
|
@@ -130,7 +133,7 @@ class Stroke extends AbstractComponent_1.default {
|
|
130
133
|
if (!bbox.intersects(visibleRect)) {
|
131
134
|
continue;
|
132
135
|
}
|
133
|
-
const muchBiggerThanVisible = bbox.size.x > visibleRect.size.x *
|
136
|
+
const muchBiggerThanVisible = bbox.size.x > visibleRect.size.x * 3 || bbox.size.y > visibleRect.size.y * 3;
|
134
137
|
if (muchBiggerThanVisible && !part.path.roughlyIntersects(visibleRect, (_b = (_a = part.style.stroke) === null || _a === void 0 ? void 0 : _a.width) !== null && _b !== void 0 ? _b : 0)) {
|
135
138
|
continue;
|
136
139
|
}
|
@@ -149,6 +152,16 @@ class Stroke extends AbstractComponent_1.default {
|
|
149
152
|
}
|
150
153
|
return origBBox.grownBy(style.stroke.width / 2);
|
151
154
|
}
|
155
|
+
getExactBBox() {
|
156
|
+
let bbox = null;
|
157
|
+
for (const { path, style } of this.parts) {
|
158
|
+
// Paths' default .bbox can be
|
159
|
+
const partBBox = this.bboxForPart(path.getExactBBox(), style);
|
160
|
+
bbox !== null && bbox !== void 0 ? bbox : (bbox = partBBox);
|
161
|
+
bbox = bbox.union(partBBox);
|
162
|
+
}
|
163
|
+
return bbox !== null && bbox !== void 0 ? bbox : Rect2_1.default.empty;
|
164
|
+
}
|
152
165
|
applyTransformation(affineTransfm) {
|
153
166
|
this.contentBBox = Rect2_1.default.empty;
|
154
167
|
let isFirstPart = true;
|
@@ -36,19 +36,7 @@ export default class TextComponent extends AbstractComponent implements Restylea
|
|
36
36
|
getStyle(): ComponentStyle;
|
37
37
|
updateStyle(style: ComponentStyle): SerializableCommand;
|
38
38
|
forceStyle(style: ComponentStyle, editor: Editor | null): void;
|
39
|
-
getTextStyle():
|
40
|
-
renderingStyle: {
|
41
|
-
fill: import("../Color4").default;
|
42
|
-
stroke: {
|
43
|
-
color: import("../Color4").default;
|
44
|
-
width: number;
|
45
|
-
} | undefined;
|
46
|
-
};
|
47
|
-
size: number;
|
48
|
-
fontFamily: string;
|
49
|
-
fontWeight?: string | undefined;
|
50
|
-
fontVariant?: string | undefined;
|
51
|
-
};
|
39
|
+
getTextStyle(): TextRenderingStyle;
|
52
40
|
getBaselinePos(): import("../lib").Vec3;
|
53
41
|
getTransform(): Mat33;
|
54
42
|
protected applyTransformation(affineTransfm: Mat33): void;
|
@@ -9,5 +9,5 @@ import TextComponent from './TextComponent';
|
|
9
9
|
import ImageComponent from './ImageComponent';
|
10
10
|
import RestyleableComponent from './RestylableComponent';
|
11
11
|
import { createRestyleComponentCommand, isRestylableComponent, ComponentStyle as RestyleableComponentStyle } from './RestylableComponent';
|
12
|
-
import
|
13
|
-
export { Stroke, TextComponent as Text, RestyleableComponent, createRestyleComponentCommand, isRestylableComponent, RestyleableComponentStyle, TextComponent, Stroke as StrokeComponent,
|
12
|
+
import BackgroundComponent from './BackgroundComponent';
|
13
|
+
export { Stroke, TextComponent as Text, RestyleableComponent, createRestyleComponentCommand, isRestylableComponent, RestyleableComponentStyle, TextComponent, Stroke as StrokeComponent, BackgroundComponent, ImageComponent, };
|
@@ -39,5 +39,5 @@ exports.ImageComponent = ImageComponent_1.default;
|
|
39
39
|
const RestylableComponent_1 = require("./RestylableComponent");
|
40
40
|
Object.defineProperty(exports, "createRestyleComponentCommand", { enumerable: true, get: function () { return RestylableComponent_1.createRestyleComponentCommand; } });
|
41
41
|
Object.defineProperty(exports, "isRestylableComponent", { enumerable: true, get: function () { return RestylableComponent_1.isRestylableComponent; } });
|
42
|
-
const
|
43
|
-
exports.BackgroundComponent =
|
42
|
+
const BackgroundComponent_1 = __importDefault(require("./BackgroundComponent"));
|
43
|
+
exports.BackgroundComponent = BackgroundComponent_1.default;
|
@@ -4,10 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
4
|
};
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
6
|
exports.StrokeSmoother = void 0;
|
7
|
-
const bezier_js_1 = require("bezier-js");
|
8
7
|
const Vec2_1 = require("../../math/Vec2");
|
9
8
|
const Rect2_1 = __importDefault(require("../../math/Rect2"));
|
10
9
|
const LineSegment2_1 = __importDefault(require("../../math/LineSegment2"));
|
10
|
+
const QuadraticBezier_1 = __importDefault(require("../../math/polynomial/QuadraticBezier"));
|
11
11
|
// Handles stroke smoothing
|
12
12
|
class StrokeSmoother {
|
13
13
|
constructor(startPoint,
|
@@ -44,9 +44,9 @@ class StrokeSmoother {
|
|
44
44
|
if (!this.currentCurve) {
|
45
45
|
return 0;
|
46
46
|
}
|
47
|
-
const startPt =
|
48
|
-
const controlPt =
|
49
|
-
const endPt =
|
47
|
+
const startPt = this.currentCurve.p0;
|
48
|
+
const controlPt = this.currentCurve.p1;
|
49
|
+
const endPt = this.currentCurve.p2;
|
50
50
|
const toControlDist = startPt.minus(controlPt).length();
|
51
51
|
const toEndDist = endPt.minus(controlPt).length();
|
52
52
|
return toControlDist + toEndDist;
|
@@ -58,7 +58,7 @@ class StrokeSmoother {
|
|
58
58
|
}
|
59
59
|
this.onCurveAdded(this.currentSegmentToPath());
|
60
60
|
const lastPoint = this.buffer[this.buffer.length - 1];
|
61
|
-
this.lastExitingVec =
|
61
|
+
this.lastExitingVec = this.currentCurve.p2.minus(this.currentCurve.p1);
|
62
62
|
console.assert(this.lastExitingVec.magnitude() !== 0, 'lastExitingVec has zero length!');
|
63
63
|
// Use the last two points to start a new curve (the last point isn't used
|
64
64
|
// in the current curve and we want connected curves to share end points)
|
@@ -73,13 +73,13 @@ class StrokeSmoother {
|
|
73
73
|
if (this.currentCurve == null) {
|
74
74
|
throw new Error('Invalid State: currentCurve is null!');
|
75
75
|
}
|
76
|
-
const startVec =
|
76
|
+
const startVec = this.currentCurve.normal(0).normalized();
|
77
77
|
if (!isFinite(startVec.magnitude())) {
|
78
78
|
throw new Error(`startVec(${startVec}) is NaN or ∞`);
|
79
79
|
}
|
80
|
-
const startPt =
|
81
|
-
const endPt =
|
82
|
-
const controlPoint =
|
80
|
+
const startPt = this.currentCurve.at(0);
|
81
|
+
const endPt = this.currentCurve.at(1);
|
82
|
+
const controlPoint = this.currentCurve.p1;
|
83
83
|
return {
|
84
84
|
startPoint: startPt,
|
85
85
|
controlPoint,
|
@@ -131,7 +131,7 @@ class StrokeSmoother {
|
|
131
131
|
const p2 = lastPoint.pos.plus((_b = this.lastExitingVec) !== null && _b !== void 0 ? _b : Vec2_1.Vec2.unitX);
|
132
132
|
const p3 = newPoint.pos;
|
133
133
|
// Quadratic Bézier curve
|
134
|
-
this.currentCurve = new
|
134
|
+
this.currentCurve = new QuadraticBezier_1.default(p1, p2, p3);
|
135
135
|
console.assert(!isNaN(p1.magnitude()) && !isNaN(p2.magnitude()) && !isNaN(p3.magnitude()), 'Expected !NaN');
|
136
136
|
if (this.isFirstSegment) {
|
137
137
|
// The start of a curve often lacks accurate pressure information. Update it.
|
@@ -152,7 +152,7 @@ class StrokeSmoother {
|
|
152
152
|
}
|
153
153
|
let exitingVec = this.computeExitingVec();
|
154
154
|
// Find the intersection between the entering vector and the exiting vector
|
155
|
-
const maxRelativeLength = 2
|
155
|
+
const maxRelativeLength = 2;
|
156
156
|
const segmentStart = this.buffer[0];
|
157
157
|
const segmentEnd = newPoint.pos;
|
158
158
|
const startEndDist = segmentEnd.minus(segmentStart).magnitude();
|
@@ -182,20 +182,28 @@ class StrokeSmoother {
|
|
182
182
|
console.assert(!segmentStart.eq(controlPoint, 1e-11), 'Start and control points are equal!');
|
183
183
|
console.assert(!controlPoint.eq(segmentEnd, 1e-11), 'Control and end points are equal!');
|
184
184
|
const prevCurve = this.currentCurve;
|
185
|
-
this.currentCurve = new
|
186
|
-
if (isNaN(
|
185
|
+
this.currentCurve = new QuadraticBezier_1.default(segmentStart, controlPoint, segmentEnd);
|
186
|
+
if (isNaN(this.currentCurve.normal(0).magnitude())) {
|
187
187
|
console.error('NaN normal at 0. Curve:', this.currentCurve);
|
188
188
|
this.currentCurve = prevCurve;
|
189
189
|
}
|
190
190
|
// Should we start making a new curve? Check whether all buffer points are within
|
191
191
|
// ±strokeWidth of the curve.
|
192
192
|
const curveMatchesPoints = (curve) => {
|
193
|
+
const minFit = Math.min(Math.max(Math.min(this.curveStartWidth, this.curveEndWidth) / 4, this.minFitAllowed), this.maxFitAllowed);
|
194
|
+
// The sum of distances greater than minFit must not exceed this:
|
195
|
+
const maxNonMatchingDistSum = minFit;
|
196
|
+
// Sum of distances greater than minFit.
|
197
|
+
let nonMatchingDistSum = 0;
|
193
198
|
for (const point of this.buffer) {
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
+
let dist = curve.approximateDistance(point);
|
200
|
+
if (dist > minFit) {
|
201
|
+
// Use the more accurate distance function
|
202
|
+
dist = curve.distance(point);
|
203
|
+
nonMatchingDistSum += Math.max(0, dist - minFit);
|
204
|
+
if (nonMatchingDistSum > maxNonMatchingDistSum) {
|
205
|
+
return false; // false: Curve doesn't match points well enough.
|
206
|
+
}
|
199
207
|
}
|
200
208
|
}
|
201
209
|
return true;
|
@@ -2,5 +2,5 @@
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
3
|
const localization_1 = require("../localization");
|
4
4
|
// German localization
|
5
|
-
const localization = Object.assign(Object.assign({}, localization_1.defaultEditorLocalization), { pen: 'Stift', eraser: 'Radierer', select: 'Auswahl', handTool: 'Verschieben', zoom: 'Vergrößerung', resetView: 'Ansicht zurücksetzen', thicknessLabel: 'Dicke: ', colorLabel: 'Farbe: ', fontLabel: 'Schriftart: ', resizeImageToSelection: 'Bildgröße an Auswahl anpassen', deleteSelection: 'Auswahl löschen', duplicateSelection: 'Auswahl duplizieren', undo: 'Rückgängig', redo: 'Wiederholen', pickColorFromScreen: 'Farbe von Bildschirm auswählen', clickToPickColorAnnouncement: 'Klicke auf den Bildschirm, um eine Farbe auszuwählen', selectionToolKeyboardShortcuts: 'Auswahl-Werkzeug: Verwende die Pfeiltasten, um ausgewählte Elemente zu verschieben und ‚i‘ und ‚o‘, um ihre Größe zu ändern.', touchPanning: 'Ansicht mit Touchscreen verschieben', anyDevicePanning: 'Ansicht mit jedem Eingabegerät verschieben',
|
5
|
+
const localization = Object.assign(Object.assign({}, localization_1.defaultEditorLocalization), { pen: 'Stift', eraser: 'Radierer', select: 'Auswahl', handTool: 'Verschieben', zoom: 'Vergrößerung', resetView: 'Ansicht zurücksetzen', thicknessLabel: 'Dicke: ', colorLabel: 'Farbe: ', fontLabel: 'Schriftart: ', resizeImageToSelection: 'Bildgröße an Auswahl anpassen', deleteSelection: 'Auswahl löschen', duplicateSelection: 'Auswahl duplizieren', undo: 'Rückgängig', redo: 'Wiederholen', pickColorFromScreen: 'Farbe von Bildschirm auswählen', clickToPickColorAnnouncement: 'Klicke auf den Bildschirm, um eine Farbe auszuwählen', selectionToolKeyboardShortcuts: 'Auswahl-Werkzeug: Verwende die Pfeiltasten, um ausgewählte Elemente zu verschieben und ‚i‘ und ‚o‘, um ihre Größe zu ändern.', touchPanning: 'Ansicht mit Touchscreen verschieben', anyDevicePanning: 'Ansicht mit jedem Eingabegerät verschieben', selectPenType: 'Objekt-Typ: ', freehandPen: 'Freihand', arrowPen: 'Pfeil', linePen: 'Linie', outlinedRectanglePen: 'Umrissenes Rechteck', filledRectanglePen: 'Ausgefülltes Rechteck', dropdownShown: t => `Dropdown-Menü für ${t} angezeigt`, dropdownHidden: t => `Dropdown-Menü für ${t} versteckt`, zoomLevel: t => `Vergößerung: ${t}%`, colorChangedAnnouncement: t => `Farbe zu ${t} geändert`, penTool: t => `Stift ${t}`, selectionTool: 'Auswahl', eraserTool: 'Radiergummi', touchPanTool: 'Ansicht mit Touchscreen verschieben', twoFingerPanZoomTool: 'Ansicht verschieben und vergrößern', undoRedoTool: 'Rückgängig/Wiederholen', rightClickDragPanTool: 'Rechtsklick-Ziehen', pipetteTool: 'Farbe von Bildschirm auswählen', keyboardPanZoom: 'Tastaturkürzel zum Verschieben/Vergrößern der Ansicht', textTool: 'Text', enterTextToInsert: 'Einzufügender Text', toolEnabledAnnouncement: t => `${t} aktiviert`, toolDisabledAnnouncement: t => `${t} deaktiviert`, updatedViewport: 'Transformierte Ansicht', transformedElements: t => `${t} Element${1 === t ? '' : 'e'} transformiert`, resizeOutputCommand: t => `Bildgröße auf ${t.w}x${t.h} geändert`, addElementAction: t => `${t} hinzugefügt`, eraseAction: (t, e) => `${e} ${t} gelöscht`, duplicateAction: (t, e) => `${e} ${t} dupliziert`, inverseOf: t => `Umkehrung von ${t}`, elements: 'Elemente', erasedNoElements: 'Nichts entfernt', duplicatedNoElements: 'Nichts dupliziert', rotatedBy: t => `${Math.abs(t)} Grad ${t < 0 ? 'im Uhrzeigersinn' : 'gegen den Uhrzeigersinn'} gedreht`, movedLeft: 'Nacht links bewegt', movedUp: 'Nacht oben bewegt', movedDown: 'Nacht unten bewegt', movedRight: 'Nacht rechts bewegt', zoomedOut: 'Ansicht verkleinert', zoomedIn: 'Ansicht vergrößert', selectedElements: t => `${t} Element${1 === t ? '' : 'e'} ausgewählt`, stroke: 'Strich', svgObject: 'SVG-Objekt', text: t => `Text-Objekt: ${t}`, pathNodeCount: t => `Es gibt ${t} sichtbare Pfad-Objekte.`, textNodeCount: t => `Es gibt ${t} sichtbare Text-Knotenpunkte.`, textNode: t => `Text: ${t}`, rerenderAsText: 'Als Text darstellen', accessibilityInputInstructions: 'Drücke ‚t‘, um den Inhalt des Ansichtsfensters als Text zu lesen. Verwende die Pfeiltasten, um die Ansicht zu verschieben, und klicke und ziehe, um Striche zu zeichnen. Drücke ‚w‘ zum Vergrößern und ‚s‘ zum Verkleinern der Ansicht.', loading: t => `Laden ${t}%...`, doneLoading: 'Laden fertig', imageEditor: 'Bild-Editor', undoAnnouncement: t => `Rückgangig gemacht ${t}`, redoAnnouncement: t => `Wiederholt ${t}` });
|
6
6
|
exports.default = localization;
|
@@ -8,7 +8,7 @@ const localization = Object.assign(Object.assign({}, localization_1.defaultEdito
|
|
8
8
|
loading: (percentage) => `Cargando: ${percentage}%...`, imageEditor: 'Editor de dibujos', undoAnnouncement: (commandDescription) => `${commandDescription} fue deshecho`, redoAnnouncement: (commandDescription) => `${commandDescription} fue rehecho`, undo: 'Deshace', redo: 'Rehace',
|
9
9
|
// Strings for the toolbar
|
10
10
|
// (see src/toolbar/localization.ts)
|
11
|
-
pen: 'Lapiz', eraser: 'Borrador', select: 'Selecciona', thicknessLabel: 'Tamaño: ', colorLabel: 'Color: ', doneLoading: 'El cargado terminó', fontLabel: 'Fuente: ', anyDevicePanning: 'Mover la pantalla con todo dispotivo', touchPanning: 'Mover la pantalla con un dedo', touchPanTool: 'Instrumento de mover la pantalla con un dedo', outlinedRectanglePen: 'Rectángulo con nada más que un borde', filledRectanglePen: 'Rectángulo sin borde', linePen: 'Línea', arrowPen: 'Flecha', freehandPen: 'Dibuja sin restricción de forma',
|
11
|
+
pen: 'Lapiz', eraser: 'Borrador', select: 'Selecciona', thicknessLabel: 'Tamaño: ', colorLabel: 'Color: ', doneLoading: 'El cargado terminó', fontLabel: 'Fuente: ', anyDevicePanning: 'Mover la pantalla con todo dispotivo', touchPanning: 'Mover la pantalla con un dedo', touchPanTool: 'Instrumento de mover la pantalla con un dedo', outlinedRectanglePen: 'Rectángulo con nada más que un borde', filledRectanglePen: 'Rectángulo sin borde', linePen: 'Línea', arrowPen: 'Flecha', freehandPen: 'Dibuja sin restricción de forma', selectPenType: 'Forma de dibuja:', handTool: 'Mover', zoom: 'Zoom', resetView: 'Reiniciar vista', resizeImageToSelection: 'Redimensionar la imagen a lo que está seleccionado', deleteSelection: 'Borra la selección', duplicateSelection: 'Duplica la selección', pickColorFromScreen: 'Selecciona un color de la pantalla', clickToPickColorAnnouncement: 'Haga un clic en la pantalla para seleccionar un color', dropdownShown(toolName) {
|
12
12
|
return `Menú por ${toolName} es visible`;
|
13
13
|
}, dropdownHidden: function (toolName) {
|
14
14
|
return `Menú por ${toolName} fue ocultado`;
|
@@ -18,6 +18,8 @@ export default class LineSegment2 {
|
|
18
18
|
intersection(other: LineSegment2): IntersectionResult | null;
|
19
19
|
intersects(other: LineSegment2): boolean;
|
20
20
|
closestPointTo(target: Point2): import("./Vec3").default;
|
21
|
+
/** Returns the distance from this line segment to `target`. */
|
22
|
+
distance(target: Point2): number;
|
21
23
|
transformedBy(affineTransfm: Mat33): LineSegment2;
|
22
24
|
toString(): string;
|
23
25
|
}
|
@@ -121,6 +121,10 @@ class LineSegment2 {
|
|
121
121
|
return this.p1;
|
122
122
|
}
|
123
123
|
}
|
124
|
+
/** Returns the distance from this line segment to `target`. */
|
125
|
+
distance(target) {
|
126
|
+
return this.closestPointTo(target).minus(target).magnitude();
|
127
|
+
}
|
124
128
|
transformedBy(affineTransfm) {
|
125
129
|
return new LineSegment2(affineTransfm.transformVec2(this.p1), affineTransfm.transformVec2(this.p2));
|
126
130
|
}
|
@@ -32,21 +32,42 @@ export interface MoveToPathCommand {
|
|
32
32
|
}
|
33
33
|
export type PathCommand = CubicBezierPathCommand | LinePathCommand | QuadraticBezierPathCommand | MoveToPathCommand;
|
34
34
|
interface IntersectionResult {
|
35
|
-
curve: LineSegment2 | Bezier;
|
35
|
+
curve: LineSegment2 | Bezier | Point2;
|
36
36
|
parameterValue: number;
|
37
37
|
point: Point2;
|
38
38
|
}
|
39
|
+
type GeometryType = LineSegment2 | Bezier | Point2;
|
40
|
+
type GeometryArrayType = Array<GeometryType>;
|
39
41
|
export default class Path {
|
40
42
|
readonly startPoint: Point2;
|
41
43
|
readonly parts: PathCommand[];
|
44
|
+
/**
|
45
|
+
* A rough estimate of the bounding box of the path.
|
46
|
+
* A slight overestimate.
|
47
|
+
* See {@link getExactBBox}
|
48
|
+
*/
|
42
49
|
readonly bbox: Rect2;
|
43
50
|
constructor(startPoint: Point2, parts: PathCommand[]);
|
51
|
+
getExactBBox(): Rect2;
|
44
52
|
private cachedGeometry;
|
45
|
-
get geometry():
|
53
|
+
get geometry(): GeometryArrayType;
|
46
54
|
private cachedPolylineApproximation;
|
47
55
|
polylineApproximation(): LineSegment2[];
|
48
56
|
static computeBBoxForSegment(startPoint: Point2, part: PathCommand): Rect2;
|
49
|
-
|
57
|
+
/**
|
58
|
+
* Let `S` be a closed path a distance `strokeRadius` from this path.
|
59
|
+
*
|
60
|
+
* @returns Approximate intersections of `line` with `S` using ray marching, starting from
|
61
|
+
* both end points of `line` and each point in `additionalRaymarchStartPoints`.
|
62
|
+
*/
|
63
|
+
private raymarchIntersectionWith;
|
64
|
+
/**
|
65
|
+
* Returns a list of intersections with this path. If `strokeRadius` is given,
|
66
|
+
* intersections are approximated with the surface `strokeRadius` away from this.
|
67
|
+
*
|
68
|
+
* If `strokeRadius > 0`, the resultant `parameterValue` has no defined value.
|
69
|
+
*/
|
70
|
+
intersection(line: LineSegment2, strokeRadius?: number): IntersectionResult[];
|
50
71
|
private static mapPathCommand;
|
51
72
|
mapPoints(mapping: (point: Point2) => Point2): Path;
|
52
73
|
transformedBy(affineTransfm: Mat33): Path;
|