js-draw 1.16.1 → 1.18.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +70 -10
- package/dist/bundle.js +2 -2
- package/dist/bundledStyles.js +1 -1
- package/dist/cjs/Editor.d.ts +75 -7
- package/dist/cjs/Editor.js +93 -90
- package/dist/cjs/Pointer.d.ts +2 -1
- package/dist/cjs/Pointer.js +9 -2
- package/dist/cjs/commands/localization.d.ts +1 -0
- package/dist/cjs/commands/localization.js +1 -0
- package/dist/cjs/commands/uniteCommands.d.ts +5 -1
- package/dist/cjs/commands/uniteCommands.js +33 -7
- package/dist/cjs/components/AbstractComponent.d.ts +17 -5
- package/dist/cjs/components/AbstractComponent.js +15 -15
- package/dist/cjs/components/Stroke.d.ts +4 -1
- package/dist/cjs/components/Stroke.js +158 -2
- package/dist/cjs/components/TextComponent.d.ts +36 -1
- package/dist/cjs/components/TextComponent.js +39 -1
- package/dist/cjs/components/builders/ArrowBuilder.js +1 -1
- package/dist/cjs/components/builders/PolylineBuilder.d.ts +35 -0
- package/dist/cjs/components/builders/PolylineBuilder.js +122 -0
- package/dist/cjs/components/builders/PressureSensitiveFreehandLineBuilder.d.ts +1 -1
- package/dist/cjs/components/builders/PressureSensitiveFreehandLineBuilder.js +44 -51
- package/dist/cjs/components/builders/autocorrect/makeShapeFitAutocorrect.js +1 -1
- package/dist/cjs/components/lib.d.ts +1 -0
- package/dist/cjs/components/lib.js +3 -1
- package/dist/cjs/components/util/StrokeSmoother.js +4 -4
- package/dist/cjs/image/EditorImage.d.ts +4 -1
- package/dist/cjs/image/EditorImage.js +5 -2
- package/dist/cjs/inputEvents.d.ts +11 -1
- package/dist/cjs/localizations/comments.d.ts +3 -0
- package/dist/cjs/localizations/comments.js +3 -0
- package/dist/cjs/localizations/de.js +1 -3
- package/dist/cjs/localizations/es.js +3 -3
- package/dist/cjs/rendering/renderers/CanvasRenderer.d.ts +7 -0
- package/dist/cjs/rendering/renderers/CanvasRenderer.js +16 -0
- package/dist/cjs/rendering/renderers/SVGRenderer.js +1 -1
- package/dist/cjs/testing/createEditor.d.ts +2 -2
- package/dist/cjs/testing/createEditor.js +2 -2
- package/dist/cjs/toolbar/IconProvider.d.ts +9 -4
- package/dist/cjs/toolbar/IconProvider.js +21 -7
- package/dist/cjs/toolbar/localization.d.ts +6 -1
- package/dist/cjs/toolbar/localization.js +7 -2
- package/dist/cjs/toolbar/widgets/DocumentPropertiesWidget.js +24 -1
- package/dist/cjs/toolbar/widgets/EraserToolWidget.d.ts +6 -1
- package/dist/cjs/toolbar/widgets/EraserToolWidget.js +45 -5
- package/dist/cjs/toolbar/widgets/PenToolWidget.d.ts +1 -1
- package/dist/cjs/toolbar/widgets/PenToolWidget.js +17 -4
- package/dist/cjs/toolbar/widgets/PenToolWidget.test.d.ts +1 -0
- package/dist/cjs/toolbar/widgets/keybindings.js +1 -1
- package/dist/cjs/tools/Eraser.d.ts +24 -4
- package/dist/cjs/tools/Eraser.js +108 -21
- package/dist/cjs/tools/InputFilter/InputStabilizer.js +3 -3
- package/dist/cjs/tools/PasteHandler.js +35 -10
- package/dist/cjs/tools/Pen.js +2 -2
- package/dist/cjs/tools/SelectionTool/SelectionTool.js +23 -4
- package/dist/cjs/tools/SelectionTool/ToPointerAutoscroller.js +1 -1
- package/dist/cjs/tools/ToolController.d.ts +17 -1
- package/dist/cjs/tools/ToolController.js +21 -8
- package/dist/cjs/tools/lib.d.ts +1 -4
- package/dist/cjs/tools/lib.js +2 -4
- package/dist/cjs/tools/localization.d.ts +2 -2
- package/dist/cjs/tools/localization.js +2 -2
- package/dist/cjs/util/ClipboardHandler.d.ts +27 -0
- package/dist/cjs/util/ClipboardHandler.js +205 -0
- package/dist/cjs/util/ClipboardHandler.test.d.ts +1 -0
- package/dist/cjs/version.d.ts +5 -0
- package/dist/cjs/version.js +6 -1
- package/dist/mjs/Editor.d.ts +75 -7
- package/dist/mjs/Editor.mjs +93 -90
- package/dist/mjs/Pointer.d.ts +2 -1
- package/dist/mjs/Pointer.mjs +9 -2
- package/dist/mjs/commands/localization.d.ts +1 -0
- package/dist/mjs/commands/localization.mjs +1 -0
- package/dist/mjs/commands/uniteCommands.d.ts +5 -1
- package/dist/mjs/commands/uniteCommands.mjs +33 -7
- package/dist/mjs/components/AbstractComponent.d.ts +17 -5
- package/dist/mjs/components/AbstractComponent.mjs +15 -15
- package/dist/mjs/components/Stroke.d.ts +4 -1
- package/dist/mjs/components/Stroke.mjs +159 -3
- package/dist/mjs/components/TextComponent.d.ts +36 -1
- package/dist/mjs/components/TextComponent.mjs +40 -2
- package/dist/mjs/components/builders/ArrowBuilder.mjs +1 -1
- package/dist/mjs/components/builders/PolylineBuilder.d.ts +35 -0
- package/dist/mjs/components/builders/PolylineBuilder.mjs +115 -0
- package/dist/mjs/components/builders/PressureSensitiveFreehandLineBuilder.d.ts +1 -1
- package/dist/mjs/components/builders/PressureSensitiveFreehandLineBuilder.mjs +45 -52
- package/dist/mjs/components/builders/autocorrect/makeShapeFitAutocorrect.mjs +1 -1
- package/dist/mjs/components/lib.d.ts +1 -0
- package/dist/mjs/components/lib.mjs +1 -0
- package/dist/mjs/components/util/StrokeSmoother.mjs +4 -4
- package/dist/mjs/image/EditorImage.d.ts +4 -1
- package/dist/mjs/image/EditorImage.mjs +5 -2
- package/dist/mjs/inputEvents.d.ts +11 -1
- package/dist/mjs/localizations/comments.d.ts +3 -0
- package/dist/mjs/localizations/comments.mjs +3 -0
- package/dist/mjs/localizations/de.mjs +1 -3
- package/dist/mjs/localizations/es.mjs +3 -3
- package/dist/mjs/rendering/renderers/CanvasRenderer.d.ts +7 -0
- package/dist/mjs/rendering/renderers/CanvasRenderer.mjs +16 -0
- package/dist/mjs/rendering/renderers/SVGRenderer.mjs +1 -1
- package/dist/mjs/testing/createEditor.d.ts +2 -2
- package/dist/mjs/testing/createEditor.mjs +2 -2
- package/dist/mjs/toolbar/IconProvider.d.ts +9 -4
- package/dist/mjs/toolbar/IconProvider.mjs +21 -7
- package/dist/mjs/toolbar/localization.d.ts +6 -1
- package/dist/mjs/toolbar/localization.mjs +7 -2
- package/dist/mjs/toolbar/widgets/DocumentPropertiesWidget.mjs +24 -1
- package/dist/mjs/toolbar/widgets/EraserToolWidget.d.ts +6 -1
- package/dist/mjs/toolbar/widgets/EraserToolWidget.mjs +47 -6
- package/dist/mjs/toolbar/widgets/PenToolWidget.d.ts +1 -1
- package/dist/mjs/toolbar/widgets/PenToolWidget.mjs +17 -4
- package/dist/mjs/toolbar/widgets/PenToolWidget.test.d.ts +1 -0
- package/dist/mjs/toolbar/widgets/keybindings.mjs +1 -1
- package/dist/mjs/tools/Eraser.d.ts +24 -4
- package/dist/mjs/tools/Eraser.mjs +108 -22
- package/dist/mjs/tools/InputFilter/InputStabilizer.mjs +3 -3
- package/dist/mjs/tools/PasteHandler.mjs +35 -10
- package/dist/mjs/tools/Pen.mjs +2 -2
- package/dist/mjs/tools/SelectionTool/SelectionTool.mjs +23 -4
- package/dist/mjs/tools/SelectionTool/ToPointerAutoscroller.mjs +1 -1
- package/dist/mjs/tools/ToolController.d.ts +17 -1
- package/dist/mjs/tools/ToolController.mjs +21 -8
- package/dist/mjs/tools/lib.d.ts +1 -4
- package/dist/mjs/tools/lib.mjs +1 -4
- package/dist/mjs/tools/localization.d.ts +2 -2
- package/dist/mjs/tools/localization.mjs +2 -2
- package/dist/mjs/util/ClipboardHandler.d.ts +27 -0
- package/dist/mjs/util/ClipboardHandler.mjs +200 -0
- package/dist/mjs/util/ClipboardHandler.test.d.ts +1 -0
- package/dist/mjs/version.d.ts +5 -0
- package/dist/mjs/version.mjs +6 -1
- package/package.json +6 -6
@@ -44,8 +44,8 @@ class Stroke extends AbstractComponent_1.default {
|
|
44
44
|
* ]);
|
45
45
|
* ```
|
46
46
|
*/
|
47
|
-
constructor(parts) {
|
48
|
-
super('stroke');
|
47
|
+
constructor(parts, initialZIndex) {
|
48
|
+
super('stroke', initialZIndex);
|
49
49
|
// @internal
|
50
50
|
// eslint-disable-next-line @typescript-eslint/prefer-as-const
|
51
51
|
this.isRestylableComponent = true;
|
@@ -123,6 +123,162 @@ class Stroke extends AbstractComponent_1.default {
|
|
123
123
|
editor.queueRerender();
|
124
124
|
}
|
125
125
|
}
|
126
|
+
/** @beta -- May fail for concave `path`s */
|
127
|
+
withRegionErased(eraserPath, viewport) {
|
128
|
+
const polyline = eraserPath.polylineApproximation();
|
129
|
+
const isPointInsideEraser = (point) => {
|
130
|
+
return eraserPath.closedContainsPoint(point);
|
131
|
+
};
|
132
|
+
const newStrokes = [];
|
133
|
+
let failedAssertions = false;
|
134
|
+
for (const part of this.parts) {
|
135
|
+
const path = part.path;
|
136
|
+
const makeStroke = (path) => {
|
137
|
+
if (part.style.fill.a > 0) {
|
138
|
+
// Remove visually empty paths.
|
139
|
+
if (path.parts.length < 1 || (path.parts.length === 1 && path.parts[0].kind === math_1.PathCommandType.LineTo)) {
|
140
|
+
// TODO: If this isn't present, a very large number of strokes are created while erasing.
|
141
|
+
return null;
|
142
|
+
}
|
143
|
+
else {
|
144
|
+
// Filled paths must be closed (allows for optimizations elsewhere)
|
145
|
+
path = path.asClosed();
|
146
|
+
}
|
147
|
+
}
|
148
|
+
if (isNaN(path.getExactBBox().area)) {
|
149
|
+
console.warn('Prevented creating a stroke with NaN area');
|
150
|
+
failedAssertions = true;
|
151
|
+
return null;
|
152
|
+
}
|
153
|
+
return new Stroke([(0, RenderablePathSpec_1.pathToRenderable)(path, part.style)], this.getZIndex());
|
154
|
+
};
|
155
|
+
const intersectionPoints = [];
|
156
|
+
// If stroked, finds intersections with the middle of the stroke.
|
157
|
+
// If filled, finds intersections with the edge of the stroke.
|
158
|
+
for (const segment of polyline) {
|
159
|
+
intersectionPoints.push(...path.intersection(segment));
|
160
|
+
}
|
161
|
+
// When stroked, if the stroke width is significantly larger than the eraser,
|
162
|
+
// it can't intersect both the edge of the stroke and its middle at the same time
|
163
|
+
// (generally, erasing is triggered by the eraser touching the edge of this stroke).
|
164
|
+
//
|
165
|
+
// As such, we also look for intersections along the edge of this, if none with the
|
166
|
+
// center were found, but only within a certain range of sizes because:
|
167
|
+
// 1. Intersection testing with stroked paths is generally much slower than with
|
168
|
+
// non-stroked paths.
|
169
|
+
// 2. If zoomed in significantly, it's unlikely that the user wants to erase a large
|
170
|
+
// part of the stroke.
|
171
|
+
let isErasingFromEdge = false;
|
172
|
+
if (intersectionPoints.length === 0
|
173
|
+
&& part.style.stroke
|
174
|
+
&& part.style.stroke.width > eraserPath.bbox.minDimension * 0.3
|
175
|
+
&& part.style.stroke.width < eraserPath.bbox.maxDimension * 30) {
|
176
|
+
for (const segment of polyline) {
|
177
|
+
intersectionPoints.push(...path.intersection(segment, part.style.stroke.width / 2));
|
178
|
+
}
|
179
|
+
isErasingFromEdge = true;
|
180
|
+
}
|
181
|
+
// Sort first by curve index, then by parameter value
|
182
|
+
intersectionPoints.sort(math_1.comparePathIndices);
|
183
|
+
const isInsideJustBeforeFirst = (() => {
|
184
|
+
if (intersectionPoints.length === 0) {
|
185
|
+
return false;
|
186
|
+
}
|
187
|
+
// The eraser may not be near the center of the curve -- approximate.
|
188
|
+
if (isErasingFromEdge) {
|
189
|
+
return intersectionPoints[0].curveIndex === 0 && intersectionPoints[0].parameterValue <= 0;
|
190
|
+
}
|
191
|
+
const justBeforeFirstIntersection = (0, math_1.stepPathIndexBy)(intersectionPoints[0], -1e-10);
|
192
|
+
return isPointInsideEraser(path.at(justBeforeFirstIntersection));
|
193
|
+
})();
|
194
|
+
let intersectionCount = isInsideJustBeforeFirst ? 1 : 0;
|
195
|
+
const addNewPath = (path, knownToBeInside) => {
|
196
|
+
const component = makeStroke(path);
|
197
|
+
let isInside = intersectionCount % 2 === 1;
|
198
|
+
intersectionCount++;
|
199
|
+
if (knownToBeInside !== undefined) {
|
200
|
+
isInside = knownToBeInside;
|
201
|
+
}
|
202
|
+
// Here, we work around bugs in the underlying Bezier curve library
|
203
|
+
// (including https://github.com/Pomax/bezierjs/issues/179).
|
204
|
+
// Even if not all intersections are returned correctly, we still want
|
205
|
+
// isInside to be roughly correct.
|
206
|
+
if (knownToBeInside === undefined && !isInside && eraserPath.closedContainsPoint(path.getExactBBox().center)) {
|
207
|
+
isInside = !isInside;
|
208
|
+
}
|
209
|
+
if (!component) {
|
210
|
+
return;
|
211
|
+
}
|
212
|
+
// Assertion: Avoid deleting sections that are much larger than the eraser.
|
213
|
+
failedAssertions ||= isInside && path.getExactBBox().maxDimension > eraserPath.getExactBBox().maxDimension * 2;
|
214
|
+
if (!isInside) {
|
215
|
+
newStrokes.push(component);
|
216
|
+
}
|
217
|
+
};
|
218
|
+
if (part.style.fill.a === 0) { // Not filled?
|
219
|
+
// An additional case where we erase completely -- without the padding of the stroke,
|
220
|
+
// the path is smaller than the eraser (allows us to erase dots completely).
|
221
|
+
const shouldEraseCompletely = eraserPath.getExactBBox().maxDimension / 10 > path.getExactBBox().maxDimension;
|
222
|
+
if (!shouldEraseCompletely) {
|
223
|
+
const split = path.splitAt(intersectionPoints, { mapNewPoint: p => viewport.roundPoint(p) });
|
224
|
+
for (const splitPart of split) {
|
225
|
+
addNewPath(splitPart);
|
226
|
+
}
|
227
|
+
}
|
228
|
+
}
|
229
|
+
else if (intersectionPoints.length >= 2 && intersectionPoints.length % 2 === 0) {
|
230
|
+
// TODO: Support subtractive erasing on small scales -- see https://github.com/personalizedrefrigerator/js-draw/pull/63/commits/568686e2384219ad0bb07617ea4efff1540aed00
|
231
|
+
// for a broken implementation.
|
232
|
+
//
|
233
|
+
// We currently assume that a 4-point intersection means that the intersection
|
234
|
+
// looks similar to this:
|
235
|
+
// -----------
|
236
|
+
// | STROKE |
|
237
|
+
// | |
|
238
|
+
//%%x-----------x%%%%%%%
|
239
|
+
//% %
|
240
|
+
//% ERASER %
|
241
|
+
//% %
|
242
|
+
//%%x-----------x%%%%%%%
|
243
|
+
// | STROKE |
|
244
|
+
// -----------
|
245
|
+
//
|
246
|
+
// Our goal is to separate STROKE into the contiguous parts outside
|
247
|
+
// of the eraser (as shown above).
|
248
|
+
//
|
249
|
+
// To do this, we split STROKE at each intersection:
|
250
|
+
// 3 3 3 3 3 3
|
251
|
+
// 3 STROKE 3
|
252
|
+
// 3 3
|
253
|
+
// x x
|
254
|
+
// 2 4
|
255
|
+
// 2 STROKE 4
|
256
|
+
// 2 4
|
257
|
+
// x x
|
258
|
+
// 1 STROKE 5
|
259
|
+
// . 5 5 5 5 5
|
260
|
+
// ^
|
261
|
+
// Start
|
262
|
+
//
|
263
|
+
// The difficulty here is correctly pairing edges to create the the output
|
264
|
+
// strokes, particularly because we don't know the order of intersection points.
|
265
|
+
const parts = path.splitAt(intersectionPoints, { mapNewPoint: p => viewport.roundPoint(p) });
|
266
|
+
for (let i = 0; i < Math.floor(parts.length / 2); i++) {
|
267
|
+
addNewPath(parts[i].union(parts[parts.length - i - 1]).asClosed());
|
268
|
+
}
|
269
|
+
if (parts.length % 2 !== 0) {
|
270
|
+
addNewPath(parts[Math.floor(parts.length / 2)].asClosed());
|
271
|
+
}
|
272
|
+
}
|
273
|
+
else {
|
274
|
+
addNewPath(path, false);
|
275
|
+
}
|
276
|
+
}
|
277
|
+
if (failedAssertions) {
|
278
|
+
return [this];
|
279
|
+
}
|
280
|
+
return newStrokes;
|
281
|
+
}
|
126
282
|
intersects(line) {
|
127
283
|
for (const part of this.parts) {
|
128
284
|
const strokeWidth = part.style.stroke?.width;
|
@@ -19,6 +19,41 @@ export declare enum TextTransformMode {
|
|
19
19
|
type TextElement = TextComponent | string;
|
20
20
|
/**
|
21
21
|
* Displays text.
|
22
|
+
*
|
23
|
+
* A `TextComponent` is a collection of `TextElement`s (`string`s or {@link TextComponent}s).
|
24
|
+
*
|
25
|
+
* **Example**:
|
26
|
+
*
|
27
|
+
* ```ts,runnable
|
28
|
+
* import { Editor, TextComponent, Mat33, Vec2, Color4, TextRenderingStyle } from 'js-draw';
|
29
|
+
* const editor = new Editor(document.body);
|
30
|
+
* editor.dispatch(editor.setBackgroundStyle({ color: Color4.black, autoresize: true ));
|
31
|
+
* ---visible---
|
32
|
+
* /// Adding a simple TextComponent
|
33
|
+
* ///------------------------------
|
34
|
+
*
|
35
|
+
* const positioning1 = Mat33.translation(Vec2.of(10, 10));
|
36
|
+
* const style: TextRenderingStyle = {
|
37
|
+
* fontFamily: 'sans', size: 12, renderingStyle: { fill: Color4.green },
|
38
|
+
* };
|
39
|
+
*
|
40
|
+
* editor.dispatch(
|
41
|
+
* editor.image.addElement(new TextComponent(['Hello, world'], positioning1, style)),
|
42
|
+
* );
|
43
|
+
*
|
44
|
+
*
|
45
|
+
* /// Adding nested TextComponents
|
46
|
+
* ///-----------------------------
|
47
|
+
*
|
48
|
+
* // Add another TextComponent that contains text and a TextComponent. Observe that '[Test]'
|
49
|
+
* // is placed directly after 'Test'.
|
50
|
+
* const positioning2 = Mat33.translation(Vec2.of(10, 50));
|
51
|
+
* editor.dispatch(
|
52
|
+
* editor.image.addElement(
|
53
|
+
* new TextComponent([ new TextComponent(['Test'], positioning1, style), '[Test]' ], positioning2, style)
|
54
|
+
* ),
|
55
|
+
* );
|
56
|
+
* ```
|
22
57
|
*/
|
23
58
|
export default class TextComponent extends AbstractComponent implements RestyleableComponent {
|
24
59
|
protected readonly textObjects: Array<TextElement>;
|
@@ -32,7 +67,7 @@ export default class TextComponent extends AbstractComponent implements Restylea
|
|
32
67
|
*
|
33
68
|
* @see {@link fromLines}
|
34
69
|
*/
|
35
|
-
constructor(textObjects: Array<TextElement>, transform: Mat33, style
|
70
|
+
constructor(textObjects: Array<TextElement>, transform: Mat33, style?: TextRenderingStyle, transformMode?: TextTransformMode);
|
36
71
|
static applyTextStyles(ctx: CanvasRenderingContext2D, style: TextRenderingStyle): void;
|
37
72
|
private static textMeasuringCtx;
|
38
73
|
private static estimateTextDimens;
|
@@ -20,8 +20,46 @@ var TextTransformMode;
|
|
20
20
|
/**Relatively positioned in the Y direction, absolutely positioned in the X direction. */
|
21
21
|
TextTransformMode[TextTransformMode["RELATIVE_Y_ABSOLUTE_X"] = 3] = "RELATIVE_Y_ABSOLUTE_X";
|
22
22
|
})(TextTransformMode || (exports.TextTransformMode = TextTransformMode = {}));
|
23
|
+
const defaultTextStyle = {
|
24
|
+
fontFamily: 'sans', size: 12, renderingStyle: { fill: math_1.Color4.purple },
|
25
|
+
};
|
23
26
|
/**
|
24
27
|
* Displays text.
|
28
|
+
*
|
29
|
+
* A `TextComponent` is a collection of `TextElement`s (`string`s or {@link TextComponent}s).
|
30
|
+
*
|
31
|
+
* **Example**:
|
32
|
+
*
|
33
|
+
* ```ts,runnable
|
34
|
+
* import { Editor, TextComponent, Mat33, Vec2, Color4, TextRenderingStyle } from 'js-draw';
|
35
|
+
* const editor = new Editor(document.body);
|
36
|
+
* editor.dispatch(editor.setBackgroundStyle({ color: Color4.black, autoresize: true ));
|
37
|
+
* ---visible---
|
38
|
+
* /// Adding a simple TextComponent
|
39
|
+
* ///------------------------------
|
40
|
+
*
|
41
|
+
* const positioning1 = Mat33.translation(Vec2.of(10, 10));
|
42
|
+
* const style: TextRenderingStyle = {
|
43
|
+
* fontFamily: 'sans', size: 12, renderingStyle: { fill: Color4.green },
|
44
|
+
* };
|
45
|
+
*
|
46
|
+
* editor.dispatch(
|
47
|
+
* editor.image.addElement(new TextComponent(['Hello, world'], positioning1, style)),
|
48
|
+
* );
|
49
|
+
*
|
50
|
+
*
|
51
|
+
* /// Adding nested TextComponents
|
52
|
+
* ///-----------------------------
|
53
|
+
*
|
54
|
+
* // Add another TextComponent that contains text and a TextComponent. Observe that '[Test]'
|
55
|
+
* // is placed directly after 'Test'.
|
56
|
+
* const positioning2 = Mat33.translation(Vec2.of(10, 50));
|
57
|
+
* editor.dispatch(
|
58
|
+
* editor.image.addElement(
|
59
|
+
* new TextComponent([ new TextComponent(['Test'], positioning1, style), '[Test]' ], positioning2, style)
|
60
|
+
* ),
|
61
|
+
* );
|
62
|
+
* ```
|
25
63
|
*/
|
26
64
|
class TextComponent extends AbstractComponent_1.default {
|
27
65
|
/**
|
@@ -31,7 +69,7 @@ class TextComponent extends AbstractComponent_1.default {
|
|
31
69
|
*/
|
32
70
|
constructor(textObjects,
|
33
71
|
// Transformation relative to this component's parent element.
|
34
|
-
transform, style,
|
72
|
+
transform, style = defaultTextStyle,
|
35
73
|
// @internal
|
36
74
|
transformMode = TextTransformMode.ABSOLUTE_XY) {
|
37
75
|
super(componentTypeId);
|
@@ -27,7 +27,7 @@ class ArrowBuilder {
|
|
27
27
|
const lineStartPoint = this.startPoint.pos;
|
28
28
|
const endPoint = this.endPoint.pos;
|
29
29
|
const toEnd = endPoint.minus(lineStartPoint).normalized();
|
30
|
-
const arrowLength = endPoint.
|
30
|
+
const arrowLength = endPoint.distanceTo(lineStartPoint);
|
31
31
|
// Ensure that the arrow tip is smaller than the arrow.
|
32
32
|
const arrowTipSize = Math.min(this.getLineWidth(), arrowLength / 2);
|
33
33
|
const startSize = this.startPoint.width / 2;
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import AbstractRenderer from '../../rendering/renderers/AbstractRenderer';
|
2
|
+
import RenderablePathSpec from '../../rendering/RenderablePathSpec';
|
3
|
+
import { Rect2 } from '@js-draw/math';
|
4
|
+
import Stroke from '../Stroke';
|
5
|
+
import Viewport from '../../Viewport';
|
6
|
+
import { StrokeDataPoint } from '../../types';
|
7
|
+
import { ComponentBuilder, ComponentBuilderFactory } from './types';
|
8
|
+
import RenderingStyle from '../../rendering/RenderingStyle';
|
9
|
+
/**
|
10
|
+
* Creates strokes from line segments rather than Bézier curves.
|
11
|
+
*
|
12
|
+
*/
|
13
|
+
export declare const makePolylineBuilder: ComponentBuilderFactory;
|
14
|
+
export default class PolylineBuilder implements ComponentBuilder {
|
15
|
+
private minFitAllowed;
|
16
|
+
private viewport;
|
17
|
+
private parts;
|
18
|
+
private bbox;
|
19
|
+
private averageWidth;
|
20
|
+
private widthAverageNumSamples;
|
21
|
+
private lastPoint;
|
22
|
+
private startPoint;
|
23
|
+
private lastLineSegment;
|
24
|
+
constructor(startPoint: StrokeDataPoint, minFitAllowed: number, viewport: Viewport);
|
25
|
+
getBBox(): Rect2;
|
26
|
+
protected getRenderingStyle(): RenderingStyle;
|
27
|
+
protected previewCurrentPath(): RenderablePathSpec;
|
28
|
+
protected previewFullPath(): RenderablePathSpec[];
|
29
|
+
preview(renderer: AbstractRenderer): void;
|
30
|
+
build(): Stroke;
|
31
|
+
private getMinFit;
|
32
|
+
private roundPoint;
|
33
|
+
private roundDistance;
|
34
|
+
addPoint(newPoint: StrokeDataPoint): void;
|
35
|
+
}
|
@@ -0,0 +1,122 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.makePolylineBuilder = void 0;
|
7
|
+
const math_1 = require("@js-draw/math");
|
8
|
+
const Stroke_1 = __importDefault(require("../Stroke"));
|
9
|
+
const Viewport_1 = __importDefault(require("../../Viewport"));
|
10
|
+
const makeShapeFitAutocorrect_1 = __importDefault(require("./autocorrect/makeShapeFitAutocorrect"));
|
11
|
+
/**
|
12
|
+
* Creates strokes from line segments rather than Bézier curves.
|
13
|
+
*
|
14
|
+
*/
|
15
|
+
exports.makePolylineBuilder = (0, makeShapeFitAutocorrect_1.default)((initialPoint, viewport) => {
|
16
|
+
const minFit = viewport.getSizeOfPixelOnCanvas();
|
17
|
+
return new PolylineBuilder(initialPoint, minFit, viewport);
|
18
|
+
});
|
19
|
+
class PolylineBuilder {
|
20
|
+
constructor(startPoint, minFitAllowed, viewport) {
|
21
|
+
this.minFitAllowed = minFitAllowed;
|
22
|
+
this.viewport = viewport;
|
23
|
+
this.parts = [];
|
24
|
+
this.widthAverageNumSamples = 1;
|
25
|
+
this.lastLineSegment = null;
|
26
|
+
this.averageWidth = startPoint.width;
|
27
|
+
this.startPoint = {
|
28
|
+
...startPoint,
|
29
|
+
pos: this.roundPoint(startPoint.pos),
|
30
|
+
};
|
31
|
+
this.lastPoint = this.startPoint.pos;
|
32
|
+
this.bbox = new math_1.Rect2(this.startPoint.pos.x, this.startPoint.pos.y, 0, 0);
|
33
|
+
this.parts = [
|
34
|
+
{
|
35
|
+
kind: math_1.PathCommandType.MoveTo,
|
36
|
+
point: this.startPoint.pos,
|
37
|
+
},
|
38
|
+
];
|
39
|
+
}
|
40
|
+
getBBox() {
|
41
|
+
return this.bbox.grownBy(this.averageWidth);
|
42
|
+
}
|
43
|
+
getRenderingStyle() {
|
44
|
+
return {
|
45
|
+
fill: math_1.Color4.transparent,
|
46
|
+
stroke: {
|
47
|
+
color: this.startPoint.color,
|
48
|
+
width: this.roundDistance(this.averageWidth),
|
49
|
+
}
|
50
|
+
};
|
51
|
+
}
|
52
|
+
previewCurrentPath() {
|
53
|
+
const startPoint = this.startPoint.pos;
|
54
|
+
const commands = [...this.parts];
|
55
|
+
// TODO: For now, this is necesary for the path to be visible.
|
56
|
+
if (commands.length <= 1) {
|
57
|
+
commands.push({
|
58
|
+
kind: math_1.PathCommandType.LineTo,
|
59
|
+
point: startPoint.plus(math_1.Vec2.of(this.averageWidth / 4, 0)),
|
60
|
+
});
|
61
|
+
}
|
62
|
+
return {
|
63
|
+
startPoint,
|
64
|
+
commands,
|
65
|
+
style: this.getRenderingStyle(),
|
66
|
+
};
|
67
|
+
}
|
68
|
+
previewFullPath() {
|
69
|
+
return [this.previewCurrentPath()];
|
70
|
+
}
|
71
|
+
preview(renderer) {
|
72
|
+
const paths = this.previewFullPath();
|
73
|
+
if (paths) {
|
74
|
+
const approxBBox = this.viewport.visibleRect;
|
75
|
+
renderer.startObject(approxBBox);
|
76
|
+
for (const path of paths) {
|
77
|
+
renderer.drawPath(path);
|
78
|
+
}
|
79
|
+
renderer.endObject();
|
80
|
+
}
|
81
|
+
}
|
82
|
+
build() {
|
83
|
+
return new Stroke_1.default(this.previewFullPath());
|
84
|
+
}
|
85
|
+
getMinFit() {
|
86
|
+
let minFit = Math.min(this.minFitAllowed, this.averageWidth / 3);
|
87
|
+
if (minFit < 1e-10) {
|
88
|
+
minFit = this.minFitAllowed;
|
89
|
+
}
|
90
|
+
return minFit;
|
91
|
+
}
|
92
|
+
roundPoint(point) {
|
93
|
+
const minFit = this.getMinFit();
|
94
|
+
return Viewport_1.default.roundPoint(point, minFit);
|
95
|
+
}
|
96
|
+
roundDistance(dist) {
|
97
|
+
const minFit = this.getMinFit();
|
98
|
+
return Viewport_1.default.roundPoint(dist, minFit);
|
99
|
+
}
|
100
|
+
addPoint(newPoint) {
|
101
|
+
this.widthAverageNumSamples++;
|
102
|
+
this.averageWidth =
|
103
|
+
this.averageWidth * (this.widthAverageNumSamples - 1) / this.widthAverageNumSamples
|
104
|
+
+ newPoint.width / this.widthAverageNumSamples;
|
105
|
+
const roundedPoint = this.roundPoint(newPoint.pos);
|
106
|
+
if (!roundedPoint.eq(this.lastPoint)) {
|
107
|
+
// If almost exactly in the same line as the previous
|
108
|
+
if (this.lastLineSegment && this.lastLineSegment.direction.dot(roundedPoint.minus(this.lastPoint).normalized()) > 0.997) {
|
109
|
+
this.parts.pop();
|
110
|
+
this.lastPoint = this.lastLineSegment.p1;
|
111
|
+
}
|
112
|
+
this.parts.push({
|
113
|
+
kind: math_1.PathCommandType.LineTo,
|
114
|
+
point: this.roundPoint(newPoint.pos),
|
115
|
+
});
|
116
|
+
this.bbox = this.bbox.grownToPoint(roundedPoint);
|
117
|
+
this.lastLineSegment = new math_1.LineSegment2(this.lastPoint, roundedPoint);
|
118
|
+
this.lastPoint = roundedPoint;
|
119
|
+
}
|
120
|
+
}
|
121
|
+
}
|
122
|
+
exports.default = PolylineBuilder;
|
@@ -12,6 +12,7 @@ export default class PressureSensitiveFreehandLineBuilder implements ComponentBu
|
|
12
12
|
private isFirstSegment;
|
13
13
|
private pathStartConnector;
|
14
14
|
private mostRecentConnector;
|
15
|
+
private nextCurveStartConnector;
|
15
16
|
private upperSegments;
|
16
17
|
private lowerSegments;
|
17
18
|
private lastUpperBezier;
|
@@ -25,7 +26,6 @@ export default class PressureSensitiveFreehandLineBuilder implements ComponentBu
|
|
25
26
|
private getRenderingStyle;
|
26
27
|
private previewCurrentPath;
|
27
28
|
private previewFullPath;
|
28
|
-
private previewStroke;
|
29
29
|
preview(renderer: AbstractRenderer): void;
|
30
30
|
build(): Stroke;
|
31
31
|
private roundPoint;
|