js-draw 0.15.1 → 0.16.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/.github/ISSUE_TEMPLATE/translation.yml +56 -0
- package/CHANGELOG.md +13 -0
- package/dist/bundle.js +1 -1
- package/dist/src/Color4.d.ts +1 -1
- package/dist/src/Color4.js +5 -1
- package/dist/src/Editor.d.ts +11 -2
- package/dist/src/Editor.js +66 -33
- package/dist/src/EditorImage.d.ts +28 -3
- package/dist/src/EditorImage.js +109 -18
- package/dist/src/EventDispatcher.d.ts +4 -3
- package/dist/src/SVGLoader.d.ts +1 -0
- package/dist/src/SVGLoader.js +15 -1
- package/dist/src/Viewport.d.ts +8 -3
- package/dist/src/Viewport.js +15 -8
- package/dist/src/components/AbstractComponent.d.ts +6 -1
- package/dist/src/components/AbstractComponent.js +15 -2
- package/dist/src/components/ImageBackground.d.ts +42 -0
- package/dist/src/components/ImageBackground.js +139 -0
- package/dist/src/components/ImageComponent.js +2 -0
- package/dist/src/components/builders/ArrowBuilder.d.ts +3 -1
- package/dist/src/components/builders/ArrowBuilder.js +43 -40
- package/dist/src/components/builders/LineBuilder.d.ts +3 -1
- package/dist/src/components/builders/LineBuilder.js +25 -28
- package/dist/src/components/builders/RectangleBuilder.js +1 -1
- package/dist/src/components/lib.d.ts +2 -1
- package/dist/src/components/lib.js +2 -1
- package/dist/src/components/localization.d.ts +2 -0
- package/dist/src/components/localization.js +2 -0
- package/dist/src/localizations/es.js +1 -1
- package/dist/src/math/Mat33.js +43 -5
- package/dist/src/math/Path.d.ts +5 -0
- package/dist/src/math/Path.js +80 -28
- package/dist/src/math/Vec3.js +1 -1
- package/dist/src/rendering/Display.js +1 -1
- package/dist/src/rendering/renderers/AbstractRenderer.d.ts +13 -1
- package/dist/src/rendering/renderers/AbstractRenderer.js +18 -3
- package/dist/src/rendering/renderers/CanvasRenderer.d.ts +2 -1
- package/dist/src/rendering/renderers/CanvasRenderer.js +12 -2
- package/dist/src/rendering/renderers/SVGRenderer.d.ts +1 -1
- package/dist/src/rendering/renderers/SVGRenderer.js +8 -2
- package/dist/src/testing/sendTouchEvent.d.ts +6 -0
- package/dist/src/testing/sendTouchEvent.js +26 -0
- package/dist/src/toolbar/HTMLToolbar.d.ts +25 -2
- package/dist/src/toolbar/HTMLToolbar.js +127 -15
- package/dist/src/toolbar/IconProvider.d.ts +2 -0
- package/dist/src/toolbar/IconProvider.js +45 -2
- package/dist/src/toolbar/localization.d.ts +5 -0
- package/dist/src/toolbar/localization.js +5 -0
- package/dist/src/toolbar/widgets/ActionButtonWidget.d.ts +3 -1
- package/dist/src/toolbar/widgets/ActionButtonWidget.js +5 -1
- package/dist/src/toolbar/widgets/BaseToolWidget.d.ts +1 -1
- package/dist/src/toolbar/widgets/BaseToolWidget.js +2 -1
- package/dist/src/toolbar/widgets/BaseWidget.d.ts +7 -2
- package/dist/src/toolbar/widgets/BaseWidget.js +23 -1
- package/dist/src/toolbar/widgets/DocumentPropertiesWidget.d.ts +19 -0
- package/dist/src/toolbar/widgets/DocumentPropertiesWidget.js +135 -0
- package/dist/src/toolbar/widgets/HandToolWidget.js +1 -1
- package/dist/src/toolbar/widgets/OverflowWidget.d.ts +25 -0
- package/dist/src/toolbar/widgets/OverflowWidget.js +65 -0
- package/dist/src/toolbar/widgets/lib.d.ts +1 -0
- package/dist/src/toolbar/widgets/lib.js +1 -0
- package/dist/src/tools/Eraser.js +5 -2
- package/dist/src/tools/PanZoom.js +12 -0
- package/dist/src/tools/PasteHandler.js +2 -2
- package/dist/src/tools/SelectionTool/Selection.d.ts +2 -1
- package/dist/src/tools/SelectionTool/Selection.js +3 -2
- package/dist/src/tools/SelectionTool/SelectionTool.js +5 -1
- package/package.json +1 -1
- package/src/Color4.test.ts +6 -0
- package/src/Color4.ts +6 -1
- package/src/Editor.loadFrom.test.ts +24 -0
- package/src/Editor.ts +73 -39
- package/src/EditorImage.ts +136 -21
- package/src/EventDispatcher.ts +4 -1
- package/src/SVGLoader.ts +12 -1
- package/src/Viewport.ts +17 -7
- package/src/components/AbstractComponent.ts +17 -1
- package/src/components/ImageBackground.test.ts +35 -0
- package/src/components/ImageBackground.ts +176 -0
- package/src/components/ImageComponent.ts +2 -0
- package/src/components/builders/ArrowBuilder.ts +44 -41
- package/src/components/builders/LineBuilder.ts +26 -28
- package/src/components/builders/RectangleBuilder.ts +1 -1
- package/src/components/lib.ts +2 -0
- package/src/components/localization.ts +4 -0
- package/src/localizations/es.ts +8 -0
- package/src/math/Mat33.test.ts +47 -3
- package/src/math/Mat33.ts +47 -5
- package/src/math/Path.ts +87 -28
- package/src/math/Vec3.test.ts +4 -0
- package/src/math/Vec3.ts +1 -1
- package/src/rendering/Display.ts +1 -1
- package/src/rendering/renderers/AbstractRenderer.ts +20 -3
- package/src/rendering/renderers/CanvasRenderer.ts +17 -4
- package/src/rendering/renderers/DummyRenderer.test.ts +1 -2
- package/src/rendering/renderers/SVGRenderer.ts +8 -1
- package/src/testing/sendTouchEvent.ts +43 -0
- package/src/toolbar/HTMLToolbar.ts +164 -16
- package/src/toolbar/IconProvider.ts +47 -2
- package/src/toolbar/localization.ts +10 -0
- package/src/toolbar/toolbar.css +2 -0
- package/src/toolbar/widgets/ActionButtonWidget.ts +5 -0
- package/src/toolbar/widgets/BaseToolWidget.ts +3 -1
- package/src/toolbar/widgets/BaseWidget.ts +34 -2
- package/src/toolbar/widgets/DocumentPropertiesWidget.ts +185 -0
- package/src/toolbar/widgets/HandToolWidget.ts +1 -1
- package/src/toolbar/widgets/OverflowWidget.css +9 -0
- package/src/toolbar/widgets/OverflowWidget.ts +83 -0
- package/src/toolbar/widgets/lib.ts +2 -1
- package/src/tools/Eraser.test.ts +24 -1
- package/src/tools/Eraser.ts +6 -2
- package/src/tools/PanZoom.test.ts +267 -23
- package/src/tools/PanZoom.ts +15 -1
- package/src/tools/PasteHandler.ts +3 -2
- package/src/tools/SelectionTool/Selection.ts +3 -2
- package/src/tools/SelectionTool/SelectionTool.ts +6 -1
- package/src/types.ts +1 -0
@@ -1,4 +1,5 @@
|
|
1
1
|
import SerializableCommand from '../commands/SerializableCommand';
|
2
|
+
import EditorImage from '../EditorImage';
|
2
3
|
import LineSegment2 from '../math/LineSegment2';
|
3
4
|
import Mat33 from '../math/Mat33';
|
4
5
|
import Rect2 from '../math/Rect2';
|
@@ -17,7 +18,7 @@ export default abstract class AbstractComponent {
|
|
17
18
|
private zIndex;
|
18
19
|
private id;
|
19
20
|
private static zIndexCounter;
|
20
|
-
protected constructor(componentKind: string);
|
21
|
+
protected constructor(componentKind: string, initialZIndex?: number);
|
21
22
|
getId(): string;
|
22
23
|
private static deserializationCallbacks;
|
23
24
|
static registerComponent(componentKind: string, deserialize: DeserializeCallback | null): void;
|
@@ -33,6 +34,9 @@ export default abstract class AbstractComponent {
|
|
33
34
|
getZIndex(): number;
|
34
35
|
/** @returns the bounding box of */
|
35
36
|
getBBox(): Rect2;
|
37
|
+
/** Called when this component is added to the given image. */
|
38
|
+
onAddToImage(_image: EditorImage): void;
|
39
|
+
onRemoveFromImage(): void;
|
36
40
|
abstract render(canvas: AbstractRenderer, visibleRect?: Rect2): void;
|
37
41
|
/** @return true if `lineSegment` intersects this component. */
|
38
42
|
abstract intersects(lineSegment: LineSegment2): boolean;
|
@@ -46,6 +50,7 @@ export default abstract class AbstractComponent {
|
|
46
50
|
transformBy(affineTransfm: Mat33): SerializableCommand;
|
47
51
|
setZIndex(newZIndex: number): SerializableCommand;
|
48
52
|
isSelectable(): boolean;
|
53
|
+
isBackground(): boolean;
|
49
54
|
getProportionalRenderingTime(): number;
|
50
55
|
private static transformElementCommandId;
|
51
56
|
private static TransformElementCommand;
|
@@ -9,12 +9,17 @@ import UnresolvedSerializableCommand from '../commands/UnresolvedCommand';
|
|
9
9
|
export default class AbstractComponent {
|
10
10
|
constructor(
|
11
11
|
// A unique identifier for the type of component
|
12
|
-
componentKind) {
|
12
|
+
componentKind, initialZIndex) {
|
13
13
|
this.componentKind = componentKind;
|
14
14
|
// Stores data attached by a loader.
|
15
15
|
this.loadSaveData = {};
|
16
16
|
this.lastChangedTime = (new Date()).getTime();
|
17
|
-
|
17
|
+
if (initialZIndex !== undefined) {
|
18
|
+
this.zIndex = initialZIndex;
|
19
|
+
}
|
20
|
+
else {
|
21
|
+
this.zIndex = AbstractComponent.zIndexCounter++;
|
22
|
+
}
|
18
23
|
// Create a unique ID.
|
19
24
|
this.id = `${new Date().getTime()}-${Math.random()}`;
|
20
25
|
if (AbstractComponent.deserializationCallbacks[componentKind] === undefined) {
|
@@ -54,6 +59,9 @@ export default class AbstractComponent {
|
|
54
59
|
getBBox() {
|
55
60
|
return this.contentBBox;
|
56
61
|
}
|
62
|
+
/** Called when this component is added to the given image. */
|
63
|
+
onAddToImage(_image) { }
|
64
|
+
onRemoveFromImage() { }
|
57
65
|
/**
|
58
66
|
* @returns true if this component intersects `rect` -- it is entirely contained
|
59
67
|
* within the rectangle or one of the rectangle's edges intersects this component.
|
@@ -86,6 +94,11 @@ export default class AbstractComponent {
|
|
86
94
|
isSelectable() {
|
87
95
|
return true;
|
88
96
|
}
|
97
|
+
// @returns true iff this component should be added to the background, rather than the
|
98
|
+
// foreground of the image.
|
99
|
+
isBackground() {
|
100
|
+
return false;
|
101
|
+
}
|
89
102
|
// @returns an approximation of the proportional time it takes to render this component.
|
90
103
|
// This is intended to be a rough estimate, but, for example, a stroke with two points sould have
|
91
104
|
// a renderingWeight approximately twice that of a stroke with one point.
|
@@ -0,0 +1,42 @@
|
|
1
|
+
import Color4 from '../Color4';
|
2
|
+
import Editor from '../Editor';
|
3
|
+
import EditorImage from '../EditorImage';
|
4
|
+
import SerializableCommand from '../commands/SerializableCommand';
|
5
|
+
import LineSegment2 from '../math/LineSegment2';
|
6
|
+
import Mat33 from '../math/Mat33';
|
7
|
+
import Rect2 from '../math/Rect2';
|
8
|
+
import AbstractRenderer from '../rendering/renderers/AbstractRenderer';
|
9
|
+
import AbstractComponent from './AbstractComponent';
|
10
|
+
import { ImageComponentLocalization } from './localization';
|
11
|
+
import RestyleableComponent, { ComponentStyle } from './RestylableComponent';
|
12
|
+
export declare enum BackgroundType {
|
13
|
+
SolidColor = 0,
|
14
|
+
None = 1
|
15
|
+
}
|
16
|
+
export declare const imageBackgroundCSSClassName = "js-draw-image-background";
|
17
|
+
export default class ImageBackground extends AbstractComponent implements RestyleableComponent {
|
18
|
+
private backgroundType;
|
19
|
+
private mainColor;
|
20
|
+
protected contentBBox: Rect2;
|
21
|
+
private viewportSizeChangeListener;
|
22
|
+
readonly isRestylableComponent: true;
|
23
|
+
constructor(backgroundType: BackgroundType, mainColor: Color4);
|
24
|
+
getStyle(): ComponentStyle;
|
25
|
+
updateStyle(style: ComponentStyle): SerializableCommand;
|
26
|
+
forceStyle(style: ComponentStyle, editor: Editor | null): void;
|
27
|
+
onAddToImage(image: EditorImage): void;
|
28
|
+
onRemoveFromImage(): void;
|
29
|
+
private recomputeBBox;
|
30
|
+
render(canvas: AbstractRenderer, visibleRect?: Rect2): void;
|
31
|
+
intersects(lineSegment: LineSegment2): boolean;
|
32
|
+
isSelectable(): boolean;
|
33
|
+
isBackground(): boolean;
|
34
|
+
protected serializeToJSON(): {
|
35
|
+
mainColor: string;
|
36
|
+
backgroundType: BackgroundType;
|
37
|
+
};
|
38
|
+
protected applyTransformation(_affineTransfm: Mat33): void;
|
39
|
+
description(localizationTable: ImageComponentLocalization): string;
|
40
|
+
protected createClone(): AbstractComponent;
|
41
|
+
static deserializeFromJSON(json: any): ImageBackground;
|
42
|
+
}
|
@@ -0,0 +1,139 @@
|
|
1
|
+
import Color4 from '../Color4';
|
2
|
+
import { EditorImageEventType } from '../EditorImage';
|
3
|
+
import Rect2 from '../math/Rect2';
|
4
|
+
import AbstractComponent from './AbstractComponent';
|
5
|
+
import { createRestyleComponentCommand } from './RestylableComponent';
|
6
|
+
export var BackgroundType;
|
7
|
+
(function (BackgroundType) {
|
8
|
+
BackgroundType[BackgroundType["SolidColor"] = 0] = "SolidColor";
|
9
|
+
BackgroundType[BackgroundType["None"] = 1] = "None";
|
10
|
+
})(BackgroundType || (BackgroundType = {}));
|
11
|
+
export const imageBackgroundCSSClassName = 'js-draw-image-background';
|
12
|
+
// Represents the background of an image in the editor.
|
13
|
+
export default class ImageBackground extends AbstractComponent {
|
14
|
+
constructor(backgroundType, mainColor) {
|
15
|
+
super('image-background', 0);
|
16
|
+
this.backgroundType = backgroundType;
|
17
|
+
this.mainColor = mainColor;
|
18
|
+
this.viewportSizeChangeListener = null;
|
19
|
+
// eslint-disable-next-line @typescript-eslint/prefer-as-const
|
20
|
+
this.isRestylableComponent = true;
|
21
|
+
this.contentBBox = Rect2.empty;
|
22
|
+
}
|
23
|
+
getStyle() {
|
24
|
+
let color = this.mainColor;
|
25
|
+
if (this.backgroundType === BackgroundType.None) {
|
26
|
+
color = undefined;
|
27
|
+
}
|
28
|
+
return {
|
29
|
+
color,
|
30
|
+
};
|
31
|
+
}
|
32
|
+
updateStyle(style) {
|
33
|
+
return createRestyleComponentCommand(this.getStyle(), style, this);
|
34
|
+
}
|
35
|
+
// @internal
|
36
|
+
forceStyle(style, editor) {
|
37
|
+
const fill = style.color;
|
38
|
+
if (!fill) {
|
39
|
+
return;
|
40
|
+
}
|
41
|
+
this.mainColor = fill;
|
42
|
+
if (fill.eq(Color4.transparent)) {
|
43
|
+
this.backgroundType = BackgroundType.None;
|
44
|
+
}
|
45
|
+
else {
|
46
|
+
this.backgroundType = BackgroundType.SolidColor;
|
47
|
+
}
|
48
|
+
if (editor) {
|
49
|
+
editor.image.queueRerenderOf(this);
|
50
|
+
editor.queueRerender();
|
51
|
+
}
|
52
|
+
}
|
53
|
+
onAddToImage(image) {
|
54
|
+
if (this.viewportSizeChangeListener) {
|
55
|
+
console.warn('onAddToImage called when background is already in an image');
|
56
|
+
this.onRemoveFromImage();
|
57
|
+
}
|
58
|
+
this.viewportSizeChangeListener = image.notifier.on(EditorImageEventType.ExportViewportChanged, () => {
|
59
|
+
this.recomputeBBox(image);
|
60
|
+
});
|
61
|
+
this.recomputeBBox(image);
|
62
|
+
}
|
63
|
+
onRemoveFromImage() {
|
64
|
+
var _a;
|
65
|
+
(_a = this.viewportSizeChangeListener) === null || _a === void 0 ? void 0 : _a.remove();
|
66
|
+
this.viewportSizeChangeListener = null;
|
67
|
+
}
|
68
|
+
recomputeBBox(image) {
|
69
|
+
const importExportRect = image.getImportExportViewport().visibleRect;
|
70
|
+
if (!this.contentBBox.eq(importExportRect)) {
|
71
|
+
this.contentBBox = importExportRect;
|
72
|
+
// Re-render this if already added to the EditorImage.
|
73
|
+
image.queueRerenderOf(this);
|
74
|
+
}
|
75
|
+
}
|
76
|
+
render(canvas, visibleRect) {
|
77
|
+
if (this.backgroundType === BackgroundType.None) {
|
78
|
+
return;
|
79
|
+
}
|
80
|
+
canvas.startObject(this.contentBBox);
|
81
|
+
if (this.backgroundType === BackgroundType.SolidColor) {
|
82
|
+
// If the rectangle for this region contains the visible rect,
|
83
|
+
// we can fill the entire visible rectangle (which may be more efficient than
|
84
|
+
// filling the entire region for this.)
|
85
|
+
if (visibleRect) {
|
86
|
+
const intersection = visibleRect.intersection(this.contentBBox);
|
87
|
+
if (intersection) {
|
88
|
+
canvas.fillRect(intersection, this.mainColor);
|
89
|
+
}
|
90
|
+
}
|
91
|
+
else {
|
92
|
+
canvas.fillRect(this.contentBBox, this.mainColor);
|
93
|
+
}
|
94
|
+
}
|
95
|
+
canvas.endObject(this.getLoadSaveData(), [imageBackgroundCSSClassName]);
|
96
|
+
}
|
97
|
+
intersects(lineSegment) {
|
98
|
+
return this.contentBBox.getEdges().some(edge => edge.intersects(lineSegment));
|
99
|
+
}
|
100
|
+
isSelectable() {
|
101
|
+
return false;
|
102
|
+
}
|
103
|
+
isBackground() {
|
104
|
+
return true;
|
105
|
+
}
|
106
|
+
serializeToJSON() {
|
107
|
+
return {
|
108
|
+
mainColor: this.mainColor.toHexString(),
|
109
|
+
backgroundType: this.backgroundType,
|
110
|
+
};
|
111
|
+
}
|
112
|
+
applyTransformation(_affineTransfm) {
|
113
|
+
// Do nothing — it doesn't make sense to transform the background.
|
114
|
+
}
|
115
|
+
description(localizationTable) {
|
116
|
+
if (this.backgroundType === BackgroundType.SolidColor) {
|
117
|
+
return localizationTable.filledBackgroundWithColor(this.mainColor.toString());
|
118
|
+
}
|
119
|
+
else {
|
120
|
+
return localizationTable.emptyBackground;
|
121
|
+
}
|
122
|
+
}
|
123
|
+
createClone() {
|
124
|
+
return new ImageBackground(this.backgroundType, this.mainColor);
|
125
|
+
}
|
126
|
+
// @internal
|
127
|
+
static deserializeFromJSON(json) {
|
128
|
+
if (typeof json === 'string') {
|
129
|
+
json = JSON.parse(json);
|
130
|
+
}
|
131
|
+
if (typeof json.mainColor !== 'string') {
|
132
|
+
throw new Error('Error deserializing — mainColor must be of type string.');
|
133
|
+
}
|
134
|
+
const backgroundType = json.backgroundType === BackgroundType.SolidColor ? BackgroundType.SolidColor : BackgroundType.None;
|
135
|
+
const mainColor = Color4.fromHex(json.mainColor);
|
136
|
+
return new ImageBackground(backgroundType, mainColor);
|
137
|
+
}
|
138
|
+
}
|
139
|
+
AbstractComponent.registerComponent('image-background', ImageBackground.deserializeFromJSON);
|
@@ -80,7 +80,9 @@ export default class ImageComponent extends AbstractComponent {
|
|
80
80
|
});
|
81
81
|
}
|
82
82
|
render(canvas, _visibleRect) {
|
83
|
+
canvas.startObject(this.contentBBox);
|
83
84
|
canvas.drawImage(this.image);
|
85
|
+
canvas.endObject(this.getLoadSaveData());
|
84
86
|
}
|
85
87
|
getProportionalRenderingTime() {
|
86
88
|
// Estimate: Equivalent to a stroke with 10 segments.
|
@@ -1,13 +1,15 @@
|
|
1
1
|
import Rect2 from '../../math/Rect2';
|
2
2
|
import AbstractRenderer from '../../rendering/renderers/AbstractRenderer';
|
3
3
|
import { StrokeDataPoint } from '../../types';
|
4
|
+
import Viewport from '../../Viewport';
|
4
5
|
import AbstractComponent from '../AbstractComponent';
|
5
6
|
import { ComponentBuilder, ComponentBuilderFactory } from './types';
|
6
7
|
export declare const makeArrowBuilder: ComponentBuilderFactory;
|
7
8
|
export default class ArrowBuilder implements ComponentBuilder {
|
8
9
|
private readonly startPoint;
|
10
|
+
private readonly viewport;
|
9
11
|
private endPoint;
|
10
|
-
constructor(startPoint: StrokeDataPoint);
|
12
|
+
constructor(startPoint: StrokeDataPoint, viewport: Viewport);
|
11
13
|
private getLineWidth;
|
12
14
|
getBBox(): Rect2;
|
13
15
|
private buildPreview;
|
@@ -1,11 +1,12 @@
|
|
1
|
-
import { PathCommandType } from '../../math/Path';
|
1
|
+
import Path, { PathCommandType } from '../../math/Path';
|
2
2
|
import Stroke from '../Stroke';
|
3
|
-
export const makeArrowBuilder = (initialPoint,
|
4
|
-
return new ArrowBuilder(initialPoint);
|
3
|
+
export const makeArrowBuilder = (initialPoint, viewport) => {
|
4
|
+
return new ArrowBuilder(initialPoint, viewport);
|
5
5
|
};
|
6
6
|
export default class ArrowBuilder {
|
7
|
-
constructor(startPoint) {
|
7
|
+
constructor(startPoint, viewport) {
|
8
8
|
this.startPoint = startPoint;
|
9
|
+
this.viewport = viewport;
|
9
10
|
this.endPoint = startPoint;
|
10
11
|
}
|
11
12
|
getLineWidth() {
|
@@ -16,10 +17,10 @@ export default class ArrowBuilder {
|
|
16
17
|
return preview.getBBox();
|
17
18
|
}
|
18
19
|
buildPreview() {
|
19
|
-
const
|
20
|
+
const lineStartPoint = this.startPoint.pos;
|
20
21
|
const endPoint = this.endPoint.pos;
|
21
|
-
const toEnd = endPoint.minus(
|
22
|
-
const arrowLength = endPoint.minus(
|
22
|
+
const toEnd = endPoint.minus(lineStartPoint).normalized();
|
23
|
+
const arrowLength = endPoint.minus(lineStartPoint).length();
|
23
24
|
// Ensure that the arrow tip is smaller than the arrow.
|
24
25
|
const arrowTipSize = Math.min(this.getLineWidth(), arrowLength / 2);
|
25
26
|
const startSize = this.startPoint.width / 2;
|
@@ -29,41 +30,43 @@ export default class ArrowBuilder {
|
|
29
30
|
const lineNormal = toEnd.orthog();
|
30
31
|
const scaledStartNormal = lineNormal.times(startSize);
|
31
32
|
const scaledBaseNormal = lineNormal.times(endSize);
|
33
|
+
const path = new Path(arrowTipBase.minus(scaledBaseNormal), [
|
34
|
+
// Stem
|
35
|
+
{
|
36
|
+
kind: PathCommandType.LineTo,
|
37
|
+
point: lineStartPoint.minus(scaledStartNormal),
|
38
|
+
},
|
39
|
+
{
|
40
|
+
kind: PathCommandType.LineTo,
|
41
|
+
point: lineStartPoint.plus(scaledStartNormal),
|
42
|
+
},
|
43
|
+
{
|
44
|
+
kind: PathCommandType.LineTo,
|
45
|
+
point: arrowTipBase.plus(scaledBaseNormal),
|
46
|
+
},
|
47
|
+
// Head
|
48
|
+
{
|
49
|
+
kind: PathCommandType.LineTo,
|
50
|
+
point: arrowTipBase.plus(lineNormal.times(arrowTipSize).plus(scaledBaseNormal)),
|
51
|
+
},
|
52
|
+
{
|
53
|
+
kind: PathCommandType.LineTo,
|
54
|
+
point: endPoint.plus(toEnd.times(endSize)),
|
55
|
+
},
|
56
|
+
{
|
57
|
+
kind: PathCommandType.LineTo,
|
58
|
+
point: arrowTipBase.plus(lineNormal.times(-arrowTipSize).minus(scaledBaseNormal)),
|
59
|
+
},
|
60
|
+
{
|
61
|
+
kind: PathCommandType.LineTo,
|
62
|
+
point: arrowTipBase.minus(scaledBaseNormal),
|
63
|
+
},
|
64
|
+
// Round all points in the arrow (to remove unnecessary decimal places)
|
65
|
+
]).mapPoints(point => this.viewport.roundPoint(point));
|
32
66
|
const preview = new Stroke([
|
33
67
|
{
|
34
|
-
startPoint:
|
35
|
-
commands:
|
36
|
-
// Stem
|
37
|
-
{
|
38
|
-
kind: PathCommandType.LineTo,
|
39
|
-
point: startPoint.minus(scaledStartNormal),
|
40
|
-
},
|
41
|
-
{
|
42
|
-
kind: PathCommandType.LineTo,
|
43
|
-
point: startPoint.plus(scaledStartNormal),
|
44
|
-
},
|
45
|
-
{
|
46
|
-
kind: PathCommandType.LineTo,
|
47
|
-
point: arrowTipBase.plus(scaledBaseNormal),
|
48
|
-
},
|
49
|
-
// Head
|
50
|
-
{
|
51
|
-
kind: PathCommandType.LineTo,
|
52
|
-
point: arrowTipBase.plus(lineNormal.times(arrowTipSize).plus(scaledBaseNormal))
|
53
|
-
},
|
54
|
-
{
|
55
|
-
kind: PathCommandType.LineTo,
|
56
|
-
point: endPoint.plus(toEnd.times(endSize)),
|
57
|
-
},
|
58
|
-
{
|
59
|
-
kind: PathCommandType.LineTo,
|
60
|
-
point: arrowTipBase.plus(lineNormal.times(-arrowTipSize).minus(scaledBaseNormal)),
|
61
|
-
},
|
62
|
-
{
|
63
|
-
kind: PathCommandType.LineTo,
|
64
|
-
point: arrowTipBase.minus(scaledBaseNormal),
|
65
|
-
},
|
66
|
-
],
|
68
|
+
startPoint: path.startPoint,
|
69
|
+
commands: path.parts,
|
67
70
|
style: {
|
68
71
|
fill: this.startPoint.color,
|
69
72
|
}
|
@@ -1,13 +1,15 @@
|
|
1
1
|
import Rect2 from '../../math/Rect2';
|
2
2
|
import AbstractRenderer from '../../rendering/renderers/AbstractRenderer';
|
3
3
|
import { StrokeDataPoint } from '../../types';
|
4
|
+
import Viewport from '../../Viewport';
|
4
5
|
import AbstractComponent from '../AbstractComponent';
|
5
6
|
import { ComponentBuilder, ComponentBuilderFactory } from './types';
|
6
7
|
export declare const makeLineBuilder: ComponentBuilderFactory;
|
7
8
|
export default class LineBuilder implements ComponentBuilder {
|
8
9
|
private readonly startPoint;
|
10
|
+
private readonly viewport;
|
9
11
|
private endPoint;
|
10
|
-
constructor(startPoint: StrokeDataPoint);
|
12
|
+
constructor(startPoint: StrokeDataPoint, viewport: Viewport);
|
11
13
|
getBBox(): Rect2;
|
12
14
|
private buildPreview;
|
13
15
|
build(): AbstractComponent;
|
@@ -1,11 +1,12 @@
|
|
1
|
-
import { PathCommandType } from '../../math/Path';
|
1
|
+
import Path, { PathCommandType } from '../../math/Path';
|
2
2
|
import Stroke from '../Stroke';
|
3
|
-
export const makeLineBuilder = (initialPoint,
|
4
|
-
return new LineBuilder(initialPoint);
|
3
|
+
export const makeLineBuilder = (initialPoint, viewport) => {
|
4
|
+
return new LineBuilder(initialPoint, viewport);
|
5
5
|
};
|
6
6
|
export default class LineBuilder {
|
7
|
-
constructor(startPoint) {
|
7
|
+
constructor(startPoint, viewport) {
|
8
8
|
this.startPoint = startPoint;
|
9
|
+
this.viewport = viewport;
|
9
10
|
this.endPoint = startPoint;
|
10
11
|
}
|
11
12
|
getBBox() {
|
@@ -21,31 +22,27 @@ export default class LineBuilder {
|
|
21
22
|
const lineNormal = toEnd.orthog();
|
22
23
|
const scaledStartNormal = lineNormal.times(startSize);
|
23
24
|
const scaledEndNormal = lineNormal.times(endSize);
|
24
|
-
const
|
25
|
+
const strokeStartPoint = startPoint.minus(scaledStartNormal);
|
26
|
+
const path = new Path(strokeStartPoint, [
|
27
|
+
{
|
28
|
+
kind: PathCommandType.LineTo,
|
29
|
+
point: startPoint.plus(scaledStartNormal),
|
30
|
+
},
|
31
|
+
{
|
32
|
+
kind: PathCommandType.LineTo,
|
33
|
+
point: endPoint.plus(scaledEndNormal),
|
34
|
+
},
|
25
35
|
{
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
{
|
37
|
-
kind: PathCommandType.LineTo,
|
38
|
-
point: endPoint.minus(scaledEndNormal),
|
39
|
-
},
|
40
|
-
{
|
41
|
-
kind: PathCommandType.LineTo,
|
42
|
-
point: startPoint.minus(scaledStartNormal),
|
43
|
-
},
|
44
|
-
],
|
45
|
-
style: {
|
46
|
-
fill: this.startPoint.color,
|
47
|
-
}
|
48
|
-
}
|
36
|
+
kind: PathCommandType.LineTo,
|
37
|
+
point: endPoint.minus(scaledEndNormal),
|
38
|
+
},
|
39
|
+
{
|
40
|
+
kind: PathCommandType.LineTo,
|
41
|
+
point: startPoint.minus(scaledStartNormal),
|
42
|
+
},
|
43
|
+
]).mapPoints(point => this.viewport.roundPoint(point));
|
44
|
+
const preview = new Stroke([
|
45
|
+
path.toRenderable({ fill: this.startPoint.color })
|
49
46
|
]);
|
50
47
|
return preview;
|
51
48
|
}
|
@@ -30,7 +30,7 @@ export default class RectangleBuilder {
|
|
30
30
|
const rect = Rect2.fromCorners(startPoint, endPoint);
|
31
31
|
const path = Path.fromRect(rect, this.filled ? null : this.endPoint.width).transformedBy(
|
32
32
|
// Rotate the canvas rectangle so that its rotation matches the screen
|
33
|
-
rotationMat);
|
33
|
+
rotationMat).mapPoints(point => this.viewport.roundPoint(point));
|
34
34
|
const preview = new Stroke([
|
35
35
|
path.toRenderable({
|
36
36
|
fill: this.endPoint.color
|
@@ -8,4 +8,5 @@ import Stroke from './Stroke';
|
|
8
8
|
import TextComponent from './TextComponent';
|
9
9
|
import ImageComponent from './ImageComponent';
|
10
10
|
import RestyleableComponent, { createRestyleComponentCommand } from './RestylableComponent';
|
11
|
-
|
11
|
+
import ImageBackground from './ImageBackground';
|
12
|
+
export { Stroke, TextComponent as Text, RestyleableComponent, createRestyleComponentCommand, TextComponent, Stroke as StrokeComponent, ImageBackground as BackgroundComponent, ImageComponent, };
|
@@ -8,4 +8,5 @@ import Stroke from './Stroke';
|
|
8
8
|
import TextComponent from './TextComponent';
|
9
9
|
import ImageComponent from './ImageComponent';
|
10
10
|
import { createRestyleComponentCommand } from './RestylableComponent';
|
11
|
-
|
11
|
+
import ImageBackground from './ImageBackground';
|
12
|
+
export { Stroke, TextComponent as Text, createRestyleComponentCommand, TextComponent, Stroke as StrokeComponent, ImageBackground as BackgroundComponent, ImageComponent, };
|
@@ -4,6 +4,8 @@ export interface ImageComponentLocalization {
|
|
4
4
|
imageNode: (description: string) => string;
|
5
5
|
stroke: string;
|
6
6
|
svgObject: string;
|
7
|
+
emptyBackground: string;
|
8
|
+
filledBackgroundWithColor: (color: string) => string;
|
7
9
|
restyledElements: string;
|
8
10
|
}
|
9
11
|
export declare const defaultComponentLocalization: ImageComponentLocalization;
|
@@ -3,6 +3,8 @@ export const defaultComponentLocalization = {
|
|
3
3
|
stroke: 'Stroke',
|
4
4
|
svgObject: 'SVG Object',
|
5
5
|
restyledElements: 'Restyled elements',
|
6
|
+
emptyBackground: 'Empty background',
|
7
|
+
filledBackgroundWithColor: (color) => `Filled background (${color})`,
|
6
8
|
text: (text) => `Text object: ${text}`,
|
7
9
|
imageNode: (description) => `Image: ${description}`,
|
8
10
|
};
|
@@ -14,5 +14,5 @@ const localization = Object.assign(Object.assign({}, defaultEditorLocalization),
|
|
14
14
|
return `Color fue cambiado a ${color}`;
|
15
15
|
}, keyboardPanZoom: 'Mover la pantalla con el teclado', penTool: function (penId) {
|
16
16
|
return `Lapiz ${penId}`;
|
17
|
-
}, selectionTool: 'Selecciona', eraserTool: 'Borrador', textTool: 'Texto', enterTextToInsert: 'Entra texto', rerenderAsText: 'Redibuja la pantalla al texto', image: 'Imagen', imageSize: (size, units) => `Tamaño del imagen: ${size} ${units}`, imageLoadError: (message) => `Error cargando imagen: ${message}
|
17
|
+
}, selectionTool: 'Selecciona', eraserTool: 'Borrador', textTool: 'Texto', enterTextToInsert: 'Entra texto', textSize: 'Tamaño', rerenderAsText: 'Redibuja la pantalla al texto', lockRotation: 'Bloquea rotación', image: 'Imagen', imageSize: (size, units) => `Tamaño del imagen: ${size} ${units}`, imageLoadError: (message) => `Error cargando imagen: ${message}`, toggleOverflow: 'Más', documentProperties: 'Fondo', imageWidthOption: 'Ancho: ', imageHeightOption: 'Alto: ', backgroundColor: 'Color de fondo: ' });
|
18
18
|
export default localization;
|
package/dist/src/math/Mat33.js
CHANGED
@@ -170,11 +170,46 @@ export default class Mat33 {
|
|
170
170
|
return true;
|
171
171
|
}
|
172
172
|
toString() {
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
173
|
+
let result = '';
|
174
|
+
const maxColumnLens = [0, 0, 0];
|
175
|
+
// Determine the longest item in each column so we can pad the others to that
|
176
|
+
// length.
|
177
|
+
for (const row of this.rows) {
|
178
|
+
for (let i = 0; i < 3; i++) {
|
179
|
+
maxColumnLens[i] = Math.max(maxColumnLens[0], `${row.at(i)}`.length);
|
180
|
+
}
|
181
|
+
}
|
182
|
+
for (let i = 0; i < 3; i++) {
|
183
|
+
if (i === 0) {
|
184
|
+
result += '⎡ ';
|
185
|
+
}
|
186
|
+
else if (i === 1) {
|
187
|
+
result += '⎢ ';
|
188
|
+
}
|
189
|
+
else {
|
190
|
+
result += '⎣ ';
|
191
|
+
}
|
192
|
+
// Add each component of the ith row (after padding it)
|
193
|
+
for (let j = 0; j < 3; j++) {
|
194
|
+
const val = this.rows[i].at(j).toString();
|
195
|
+
let padding = '';
|
196
|
+
for (let i = val.length; i < maxColumnLens[j]; i++) {
|
197
|
+
padding += ' ';
|
198
|
+
}
|
199
|
+
result += val + ', ' + padding;
|
200
|
+
}
|
201
|
+
if (i === 0) {
|
202
|
+
result += ' ⎤';
|
203
|
+
}
|
204
|
+
else if (i === 1) {
|
205
|
+
result += ' ⎥';
|
206
|
+
}
|
207
|
+
else {
|
208
|
+
result += ' ⎦';
|
209
|
+
}
|
210
|
+
result += '\n';
|
211
|
+
}
|
212
|
+
return result.trimEnd();
|
178
213
|
}
|
179
214
|
/**
|
180
215
|
* ```
|
@@ -219,6 +254,9 @@ export default class Mat33 {
|
|
219
254
|
return new Mat33(1, 0, amount.x, 0, 1, amount.y, 0, 0, 1);
|
220
255
|
}
|
221
256
|
static zRotation(radians, center = Vec2.zero) {
|
257
|
+
if (radians === 0) {
|
258
|
+
return Mat33.identity;
|
259
|
+
}
|
222
260
|
const cos = Math.cos(radians);
|
223
261
|
const sin = Math.sin(radians);
|
224
262
|
// Translate everything so that rotation is about the origin
|
package/dist/src/math/Path.d.ts
CHANGED
@@ -47,6 +47,7 @@ export default class Path {
|
|
47
47
|
polylineApproximation(): LineSegment2[];
|
48
48
|
static computeBBoxForSegment(startPoint: Point2, part: PathCommand): Rect2;
|
49
49
|
intersection(line: LineSegment2): IntersectionResult[];
|
50
|
+
private static mapPathCommand;
|
50
51
|
mapPoints(mapping: (point: Point2) => Point2): Path;
|
51
52
|
transformedBy(affineTransfm: Mat33): Path;
|
52
53
|
union(other: Path | null): Path;
|
@@ -56,6 +57,10 @@ export default class Path {
|
|
56
57
|
static fromRect(rect: Rect2, lineWidth?: number | null): Path;
|
57
58
|
static fromRenderable(renderable: RenderablePathSpec): Path;
|
58
59
|
toRenderable(fill: RenderingStyle): RenderablePathSpec;
|
60
|
+
/**
|
61
|
+
* @returns a Path that, when rendered, looks roughly equivalent to the given path.
|
62
|
+
*/
|
63
|
+
static visualEquivalent(renderablePath: RenderablePathSpec, visibleRect: Rect2): RenderablePathSpec;
|
59
64
|
private cachedStringVersion;
|
60
65
|
toString(useNonAbsCommands?: boolean): string;
|
61
66
|
serialize(): string;
|