js-draw 0.15.1 → 0.15.2
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/CHANGELOG.md +7 -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 +0 -2
- package/dist/src/Editor.js +15 -30
- package/dist/src/EditorImage.d.ts +25 -0
- package/dist/src/EditorImage.js +57 -2
- 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 +3 -3
- package/dist/src/Viewport.js +4 -8
- package/dist/src/components/AbstractComponent.d.ts +5 -1
- package/dist/src/components/AbstractComponent.js +10 -2
- package/dist/src/components/ImageBackground.d.ts +41 -0
- package/dist/src/components/ImageBackground.js +132 -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/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/IconProvider.js +1 -2
- package/dist/src/toolbar/widgets/HandToolWidget.js +1 -1
- package/dist/src/tools/Eraser.js +5 -2
- package/dist/src/tools/PanZoom.js +12 -0
- package/dist/src/tools/SelectionTool/Selection.js +1 -1
- 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.ts +15 -36
- package/src/EditorImage.ts +74 -2
- package/src/EventDispatcher.ts +4 -1
- package/src/SVGLoader.ts +12 -1
- package/src/Viewport.ts +4 -7
- package/src/components/AbstractComponent.ts +11 -1
- package/src/components/ImageBackground.ts +167 -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/math/Mat33.test.ts +20 -1
- 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 +16 -3
- 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/IconProvider.ts +1 -2
- package/src/toolbar/widgets/HandToolWidget.ts +1 -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/SelectionTool/Selection.ts +1 -1
- package/src/tools/SelectionTool/SelectionTool.ts +6 -1
- package/src/types.ts +1 -0
@@ -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
|
};
|
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;
|
package/dist/src/math/Path.js
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import { Bezier } from 'bezier-js';
|
2
2
|
import { toRoundedString, toStringOfSamePrecision } from './rounding';
|
3
3
|
import LineSegment2 from './LineSegment2';
|
4
|
+
import Mat33 from './Mat33';
|
4
5
|
import Rect2 from './Rect2';
|
5
6
|
import { Vec2 } from './Vec2';
|
6
7
|
export var PathCommandType;
|
@@ -143,38 +144,39 @@ export default class Path {
|
|
143
144
|
}
|
144
145
|
return result;
|
145
146
|
}
|
147
|
+
static mapPathCommand(part, mapping) {
|
148
|
+
switch (part.kind) {
|
149
|
+
case PathCommandType.MoveTo:
|
150
|
+
case PathCommandType.LineTo:
|
151
|
+
return {
|
152
|
+
kind: part.kind,
|
153
|
+
point: mapping(part.point),
|
154
|
+
};
|
155
|
+
break;
|
156
|
+
case PathCommandType.CubicBezierTo:
|
157
|
+
return {
|
158
|
+
kind: part.kind,
|
159
|
+
controlPoint1: mapping(part.controlPoint1),
|
160
|
+
controlPoint2: mapping(part.controlPoint2),
|
161
|
+
endPoint: mapping(part.endPoint),
|
162
|
+
};
|
163
|
+
break;
|
164
|
+
case PathCommandType.QuadraticBezierTo:
|
165
|
+
return {
|
166
|
+
kind: part.kind,
|
167
|
+
controlPoint: mapping(part.controlPoint),
|
168
|
+
endPoint: mapping(part.endPoint),
|
169
|
+
};
|
170
|
+
break;
|
171
|
+
}
|
172
|
+
const exhaustivenessCheck = part;
|
173
|
+
return exhaustivenessCheck;
|
174
|
+
}
|
146
175
|
mapPoints(mapping) {
|
147
176
|
const startPoint = mapping(this.startPoint);
|
148
177
|
const newParts = [];
|
149
|
-
let exhaustivenessCheck;
|
150
178
|
for (const part of this.parts) {
|
151
|
-
|
152
|
-
case PathCommandType.MoveTo:
|
153
|
-
case PathCommandType.LineTo:
|
154
|
-
newParts.push({
|
155
|
-
kind: part.kind,
|
156
|
-
point: mapping(part.point),
|
157
|
-
});
|
158
|
-
break;
|
159
|
-
case PathCommandType.CubicBezierTo:
|
160
|
-
newParts.push({
|
161
|
-
kind: part.kind,
|
162
|
-
controlPoint1: mapping(part.controlPoint1),
|
163
|
-
controlPoint2: mapping(part.controlPoint2),
|
164
|
-
endPoint: mapping(part.endPoint),
|
165
|
-
});
|
166
|
-
break;
|
167
|
-
case PathCommandType.QuadraticBezierTo:
|
168
|
-
newParts.push({
|
169
|
-
kind: part.kind,
|
170
|
-
controlPoint: mapping(part.controlPoint),
|
171
|
-
endPoint: mapping(part.endPoint),
|
172
|
-
});
|
173
|
-
break;
|
174
|
-
default:
|
175
|
-
exhaustivenessCheck = part;
|
176
|
-
return exhaustivenessCheck;
|
177
|
-
}
|
179
|
+
newParts.push(Path.mapPathCommand(part, mapping));
|
178
180
|
}
|
179
181
|
return new Path(startPoint, newParts);
|
180
182
|
}
|
@@ -329,6 +331,56 @@ export default class Path {
|
|
329
331
|
path: this,
|
330
332
|
};
|
331
333
|
}
|
334
|
+
/**
|
335
|
+
* @returns a Path that, when rendered, looks roughly equivalent to the given path.
|
336
|
+
*/
|
337
|
+
static visualEquivalent(renderablePath, visibleRect) {
|
338
|
+
var _a, _b;
|
339
|
+
const path = Path.fromRenderable(renderablePath);
|
340
|
+
const strokeWidth = (_b = (_a = renderablePath.style.stroke) === null || _a === void 0 ? void 0 : _a.width) !== null && _b !== void 0 ? _b : 0;
|
341
|
+
const onlyStroked = strokeWidth > 0 && renderablePath.style.fill.a === 0;
|
342
|
+
// Scale the expanded rect --- the visual equivalent is only close for huge strokes.
|
343
|
+
const expandedRect = visibleRect.grownBy(strokeWidth)
|
344
|
+
.transformedBoundingBox(Mat33.scaling2D(2, visibleRect.center));
|
345
|
+
// TODO: Handle simplifying very small paths.
|
346
|
+
if (expandedRect.containsRect(path.bbox.grownBy(strokeWidth))) {
|
347
|
+
return renderablePath;
|
348
|
+
}
|
349
|
+
const parts = [];
|
350
|
+
let startPoint = path.startPoint;
|
351
|
+
for (const part of path.parts) {
|
352
|
+
const partBBox = Path.computeBBoxForSegment(startPoint, part).grownBy(strokeWidth);
|
353
|
+
let endPoint;
|
354
|
+
if (part.kind === PathCommandType.LineTo || part.kind === PathCommandType.MoveTo) {
|
355
|
+
endPoint = part.point;
|
356
|
+
}
|
357
|
+
else {
|
358
|
+
endPoint = part.endPoint;
|
359
|
+
}
|
360
|
+
const intersectsVisible = partBBox.intersects(visibleRect);
|
361
|
+
if (intersectsVisible) {
|
362
|
+
// TODO: Can we trim parts of paths that intersect the visible rectangle?
|
363
|
+
parts.push(part);
|
364
|
+
}
|
365
|
+
else if (onlyStroked || part.kind === PathCommandType.MoveTo) {
|
366
|
+
// We're stroking (not filling) and the path doesn't intersect the bounding box.
|
367
|
+
// Don't draw it, but preserve the endpoints.
|
368
|
+
parts.push({
|
369
|
+
kind: PathCommandType.MoveTo,
|
370
|
+
point: endPoint,
|
371
|
+
});
|
372
|
+
}
|
373
|
+
else {
|
374
|
+
// Otherwise, we may be filling. Try to roughly preserve the filled region.
|
375
|
+
parts.push({
|
376
|
+
kind: PathCommandType.LineTo,
|
377
|
+
point: endPoint,
|
378
|
+
});
|
379
|
+
}
|
380
|
+
startPoint = endPoint;
|
381
|
+
}
|
382
|
+
return new Path(path.startPoint, parts).toRenderable(renderablePath.style);
|
383
|
+
}
|
332
384
|
toString(useNonAbsCommands) {
|
333
385
|
if (this.cachedStringVersion) {
|
334
386
|
return this.cachedStringVersion;
|
package/dist/src/math/Vec3.js
CHANGED
@@ -65,7 +65,7 @@ export default class Vec3 {
|
|
65
65
|
return Vec3.of(this.x + v.x, this.y + v.y, this.z + v.z);
|
66
66
|
}
|
67
67
|
minus(v) {
|
68
|
-
return this.
|
68
|
+
return Vec3.of(this.x - v.x, this.y - v.y, this.z - v.z);
|
69
69
|
}
|
70
70
|
dot(other) {
|
71
71
|
return this.x * other.x + this.y * other.y + this.z * other.z;
|
@@ -70,7 +70,7 @@ export default class Display {
|
|
70
70
|
},
|
71
71
|
blockResolution: cacheBlockResolution,
|
72
72
|
cacheSize: 600 * 600 * 4 * 90,
|
73
|
-
maxScale: 1.
|
73
|
+
maxScale: 1.3,
|
74
74
|
// Require about 20 strokes with 4 parts each to cache an image in one of the
|
75
75
|
// parts of the cache grid.
|
76
76
|
minProportionalRenderTimePerCache: 20 * 4,
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import Color4 from '../../Color4';
|
1
2
|
import { LoadSaveDataTable } from '../../components/AbstractComponent';
|
2
3
|
import Mat33 from '../../math/Mat33';
|
3
4
|
import Path, { PathCommand } from '../../math/Path';
|
@@ -41,8 +42,19 @@ export default abstract class AbstractRenderer {
|
|
41
42
|
private flushPath;
|
42
43
|
drawPath(path: RenderablePathSpec): void;
|
43
44
|
drawRect(rect: Rect2, lineWidth: number, lineFill: RenderingStyle): void;
|
45
|
+
fillRect(rect: Rect2, fill: Color4): void;
|
44
46
|
startObject(_boundingBox: Rect2, _clip?: boolean): void;
|
45
|
-
|
47
|
+
/**
|
48
|
+
* Notes the end of an object.
|
49
|
+
* @param _loaderData - a map from strings to JSON-ifyable objects
|
50
|
+
* and contains properties attached to the object by whatever loader loaded the image. This
|
51
|
+
* is used to preserve attributes not supported by js-draw when loading/saving an image.
|
52
|
+
* Renderers may ignore this.
|
53
|
+
*
|
54
|
+
* @param _objectTags - a list of labels (e.g. `className`s) to be attached to the object.
|
55
|
+
* Renderers may ignore this.
|
56
|
+
*/
|
57
|
+
endObject(_loaderData?: LoadSaveDataTable, _objectTags?: string[]): void;
|
46
58
|
protected getNestingLevel(): number;
|
47
59
|
abstract drawPoints(...points: Point2[]): void;
|
48
60
|
canRenderFromWithoutDataLoss(_other: AbstractRenderer): boolean;
|
@@ -64,19 +64,34 @@ export default class AbstractRenderer {
|
|
64
64
|
this.currentPaths.push(path);
|
65
65
|
}
|
66
66
|
}
|
67
|
-
//
|
67
|
+
// Strokes a rectangle. Boundary lines have width [lineWidth] and are filled with [lineFill].
|
68
68
|
// This is equivalent to `drawPath(Path.fromRect(...).toRenderable(...))`.
|
69
69
|
drawRect(rect, lineWidth, lineFill) {
|
70
70
|
const path = Path.fromRect(rect, lineWidth);
|
71
71
|
this.drawPath(path.toRenderable(lineFill));
|
72
72
|
}
|
73
|
-
//
|
73
|
+
// Fills a rectangle.
|
74
|
+
fillRect(rect, fill) {
|
75
|
+
const path = Path.fromRect(rect);
|
76
|
+
this.drawPath(path.toRenderable({ fill }));
|
77
|
+
}
|
78
|
+
// Note the start of an object with the given bounding box.
|
74
79
|
// Renderers are not required to support [clip]
|
75
80
|
startObject(_boundingBox, _clip) {
|
76
81
|
this.currentPaths = [];
|
77
82
|
this.objectLevel++;
|
78
83
|
}
|
79
|
-
|
84
|
+
/**
|
85
|
+
* Notes the end of an object.
|
86
|
+
* @param _loaderData - a map from strings to JSON-ifyable objects
|
87
|
+
* and contains properties attached to the object by whatever loader loaded the image. This
|
88
|
+
* is used to preserve attributes not supported by js-draw when loading/saving an image.
|
89
|
+
* Renderers may ignore this.
|
90
|
+
*
|
91
|
+
* @param _objectTags - a list of labels (e.g. `className`s) to be attached to the object.
|
92
|
+
* Renderers may ignore this.
|
93
|
+
*/
|
94
|
+
endObject(_loaderData, _objectTags) {
|
80
95
|
// Render the paths all at once
|
81
96
|
this.flushPath();
|
82
97
|
this.currentPaths = null;
|
@@ -10,6 +10,7 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
10
10
|
private ctx;
|
11
11
|
private ignoreObjectsAboveLevel;
|
12
12
|
private ignoringObject;
|
13
|
+
private currentObjectBBox;
|
13
14
|
private minSquareCurveApproxDist;
|
14
15
|
private minRenderSizeAnyDimen;
|
15
16
|
private minRenderSizeBothDimens;
|
@@ -30,7 +31,7 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
30
31
|
drawText(text: string, transform: Mat33, style: TextStyle): void;
|
31
32
|
drawImage(image: RenderableImage): void;
|
32
33
|
private clipLevels;
|
33
|
-
startObject(boundingBox: Rect2, clip
|
34
|
+
startObject(boundingBox: Rect2, clip?: boolean): void;
|
34
35
|
endObject(): void;
|
35
36
|
drawPoints(...points: Point2[]): void;
|
36
37
|
isTooSmallToRender(rect: Rect2): boolean;
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import Color4 from '../../Color4';
|
2
2
|
import TextComponent from '../../components/TextComponent';
|
3
|
+
import Path from '../../math/Path';
|
3
4
|
import { Vec2 } from '../../math/Vec2';
|
4
5
|
import AbstractRenderer from './AbstractRenderer';
|
5
6
|
export default class CanvasRenderer extends AbstractRenderer {
|
@@ -8,6 +9,7 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
8
9
|
this.ctx = ctx;
|
9
10
|
this.ignoreObjectsAboveLevel = null;
|
10
11
|
this.ignoringObject = false;
|
12
|
+
this.currentObjectBBox = null;
|
11
13
|
this.clipLevels = [];
|
12
14
|
this.setDraftMode(false);
|
13
15
|
}
|
@@ -43,8 +45,8 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
43
45
|
}
|
44
46
|
else {
|
45
47
|
this.minSquareCurveApproxDist = 0.5;
|
46
|
-
this.minRenderSizeBothDimens = 0.
|
47
|
-
this.minRenderSizeAnyDimen = 1e-
|
48
|
+
this.minRenderSizeBothDimens = 0.2;
|
49
|
+
this.minRenderSizeAnyDimen = 1e-6;
|
48
50
|
}
|
49
51
|
}
|
50
52
|
displaySize() {
|
@@ -106,9 +108,15 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
106
108
|
}
|
107
109
|
}
|
108
110
|
drawPath(path) {
|
111
|
+
var _a;
|
109
112
|
if (this.ignoringObject) {
|
110
113
|
return;
|
111
114
|
}
|
115
|
+
// If part of a huge object, it might be worth trimming the path
|
116
|
+
if ((_a = this.currentObjectBBox) === null || _a === void 0 ? void 0 : _a.containsRect(this.getViewport().visibleRect)) {
|
117
|
+
// Try to trim/remove parts of the path outside of the bounding box.
|
118
|
+
path = Path.visualEquivalent(path, this.getViewport().visibleRect);
|
119
|
+
}
|
112
120
|
super.drawPath(path);
|
113
121
|
}
|
114
122
|
drawText(text, transform, style) {
|
@@ -140,6 +148,7 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
140
148
|
this.ignoringObject = true;
|
141
149
|
}
|
142
150
|
super.startObject(boundingBox);
|
151
|
+
this.currentObjectBBox = boundingBox;
|
143
152
|
if (!this.ignoringObject && clip) {
|
144
153
|
this.clipLevels.push(this.objectLevel);
|
145
154
|
this.ctx.save();
|
@@ -158,6 +167,7 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
158
167
|
this.clipLevels.pop();
|
159
168
|
}
|
160
169
|
}
|
170
|
+
this.currentObjectBBox = null;
|
161
171
|
super.endObject();
|
162
172
|
// If exiting an object with a too-small-to-draw bounding box,
|
163
173
|
if (this.ignoreObjectsAboveLevel !== null && this.getNestingLevel() <= this.ignoreObjectsAboveLevel) {
|
@@ -28,7 +28,7 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
28
28
|
drawText(text: string, transform: Mat33, style: TextStyle): void;
|
29
29
|
drawImage(image: RenderableImage): void;
|
30
30
|
startObject(boundingBox: Rect2): void;
|
31
|
-
endObject(loaderData?: LoadSaveDataTable): void;
|
31
|
+
endObject(loaderData?: LoadSaveDataTable, elemClassNames?: string[]): void;
|
32
32
|
private unimplementedMessage;
|
33
33
|
protected beginPath(_startPoint: Point2): void;
|
34
34
|
protected endPath(_style: RenderingStyle): void;
|