js-draw 0.0.2 → 0.0.5

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.
Files changed (84) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +100 -3
  3. package/build_tools/BundledFile.ts +167 -0
  4. package/build_tools/bundle.ts +11 -0
  5. package/dist/build_tools/BundledFile.d.ts +13 -0
  6. package/dist/build_tools/BundledFile.js +157 -0
  7. package/dist/build_tools/bundle.d.ts +1 -0
  8. package/dist/build_tools/bundle.js +5 -0
  9. package/dist/bundle.js +1 -0
  10. package/dist/src/Display.js +4 -1
  11. package/dist/src/Editor.d.ts +9 -2
  12. package/dist/src/Editor.js +29 -7
  13. package/dist/src/EditorImage.d.ts +2 -0
  14. package/dist/src/EditorImage.js +29 -5
  15. package/dist/src/Pointer.js +1 -1
  16. package/dist/src/Viewport.d.ts +1 -1
  17. package/dist/src/bundle/bundled.d.ts +4 -0
  18. package/dist/src/bundle/bundled.js +5 -0
  19. package/dist/src/components/AbstractComponent.d.ts +1 -1
  20. package/dist/src/components/Stroke.d.ts +1 -1
  21. package/dist/src/components/Stroke.js +1 -1
  22. package/dist/src/components/UnknownSVGObject.d.ts +1 -1
  23. package/dist/src/components/builders/ArrowBuilder.d.ts +17 -0
  24. package/dist/src/components/builders/ArrowBuilder.js +83 -0
  25. package/dist/src/{StrokeBuilder.d.ts → components/builders/FreehandLineBuilder.d.ts} +9 -13
  26. package/dist/src/{StrokeBuilder.js → components/builders/FreehandLineBuilder.js} +27 -9
  27. package/dist/src/components/builders/LineBuilder.d.ts +16 -0
  28. package/dist/src/components/builders/LineBuilder.js +57 -0
  29. package/dist/src/components/builders/RectangleBuilder.d.ts +18 -0
  30. package/dist/src/components/builders/RectangleBuilder.js +41 -0
  31. package/dist/src/components/builders/types.d.ts +12 -0
  32. package/dist/src/components/builders/types.js +1 -0
  33. package/dist/src/geometry/Path.d.ts +1 -0
  34. package/dist/src/geometry/Path.js +32 -0
  35. package/dist/src/geometry/Vec3.d.ts +2 -0
  36. package/dist/src/geometry/Vec3.js +13 -0
  37. package/dist/src/localization.d.ts +1 -1
  38. package/dist/src/localization.js +2 -2
  39. package/dist/src/rendering/AbstractRenderer.js +3 -25
  40. package/dist/src/toolbar/HTMLToolbar.d.ts +5 -3
  41. package/dist/src/toolbar/HTMLToolbar.js +139 -30
  42. package/dist/src/toolbar/localization.d.ts +20 -0
  43. package/dist/src/toolbar/localization.js +19 -0
  44. package/dist/src/toolbar/types.d.ts +0 -13
  45. package/dist/src/tools/Pen.d.ts +13 -3
  46. package/dist/src/tools/Pen.js +37 -28
  47. package/dist/src/tools/SelectionTool.js +2 -1
  48. package/dist/src/tools/ToolController.js +3 -3
  49. package/dist/src/types.d.ts +14 -2
  50. package/dist/src/types.js +1 -0
  51. package/dist-test/test-dist-bundle.html +35 -0
  52. package/package.json +15 -5
  53. package/src/Display.ts +3 -1
  54. package/src/Editor.css +0 -1
  55. package/src/Editor.ts +51 -12
  56. package/src/EditorImage.test.ts +5 -3
  57. package/src/EditorImage.ts +31 -3
  58. package/src/Pointer.ts +1 -1
  59. package/src/Viewport.ts +1 -1
  60. package/src/bundle/bundled.ts +7 -0
  61. package/src/components/AbstractComponent.ts +1 -1
  62. package/src/components/Stroke.ts +2 -2
  63. package/src/components/UnknownSVGObject.ts +1 -1
  64. package/src/components/builders/ArrowBuilder.ts +104 -0
  65. package/src/{StrokeBuilder.ts → components/builders/FreehandLineBuilder.ts} +36 -18
  66. package/src/components/builders/LineBuilder.ts +75 -0
  67. package/src/components/builders/RectangleBuilder.ts +59 -0
  68. package/src/components/builders/types.ts +15 -0
  69. package/src/geometry/Path.ts +43 -0
  70. package/src/geometry/Vec2.test.ts +1 -0
  71. package/src/geometry/Vec3.test.ts +14 -0
  72. package/src/geometry/Vec3.ts +16 -0
  73. package/src/localization.ts +2 -3
  74. package/src/rendering/AbstractRenderer.ts +3 -32
  75. package/src/{editorStyles.js → styles.js} +0 -0
  76. package/src/toolbar/HTMLToolbar.ts +167 -39
  77. package/src/toolbar/localization.ts +44 -0
  78. package/src/toolbar/toolbar.css +12 -0
  79. package/src/toolbar/types.ts +0 -16
  80. package/src/tools/Pen.ts +56 -34
  81. package/src/tools/SelectionTool.test.ts +1 -1
  82. package/src/tools/SelectionTool.ts +1 -1
  83. package/src/tools/ToolController.ts +3 -3
  84. package/src/types.ts +16 -1
@@ -37,7 +37,7 @@ export default class Stroke extends AbstractComponent {
37
37
  canvas.startObject(this.getBBox());
38
38
  for (const part of this.parts) {
39
39
  const bbox = part.bbox;
40
- if (bbox.intersects(visibleRect)) {
40
+ if (!visibleRect || bbox.intersects(visibleRect)) {
41
41
  canvas.drawPath(part);
42
42
  }
43
43
  }
@@ -8,7 +8,7 @@ export default class UnknownSVGObject extends AbstractComponent {
8
8
  private svgObject;
9
9
  protected contentBBox: Rect2;
10
10
  constructor(svgObject: SVGElement);
11
- render(canvas: AbstractRenderer, _visibleRect: Rect2): void;
11
+ render(canvas: AbstractRenderer, _visibleRect?: Rect2): void;
12
12
  intersects(lineSegment: LineSegment2): boolean;
13
13
  protected applyTransformation(_affineTransfm: Mat33): void;
14
14
  description(localization: ImageComponentLocalization): string;
@@ -0,0 +1,17 @@
1
+ import Rect2 from '../../geometry/Rect2';
2
+ import AbstractRenderer from '../../rendering/AbstractRenderer';
3
+ import { StrokeDataPoint } from '../../types';
4
+ import AbstractComponent from '../AbstractComponent';
5
+ import { ComponentBuilder, ComponentBuilderFactory } from './types';
6
+ export declare const makeArrowBuilder: ComponentBuilderFactory;
7
+ export default class ArrowBuilder implements ComponentBuilder {
8
+ private readonly startPoint;
9
+ private endPoint;
10
+ constructor(startPoint: StrokeDataPoint);
11
+ private getLineWidth;
12
+ getBBox(): Rect2;
13
+ private buildPreview;
14
+ build(): AbstractComponent;
15
+ preview(renderer: AbstractRenderer): void;
16
+ addPoint(point: StrokeDataPoint): void;
17
+ }
@@ -0,0 +1,83 @@
1
+ import { PathCommandType } from '../../geometry/Path';
2
+ import Stroke from '../Stroke';
3
+ export const makeArrowBuilder = (initialPoint, _viewport) => {
4
+ return new ArrowBuilder(initialPoint);
5
+ };
6
+ export default class ArrowBuilder {
7
+ constructor(startPoint) {
8
+ this.startPoint = startPoint;
9
+ this.endPoint = startPoint;
10
+ }
11
+ getLineWidth() {
12
+ return Math.max(this.endPoint.width, this.startPoint.width);
13
+ }
14
+ getBBox() {
15
+ const preview = this.buildPreview();
16
+ return preview.getBBox();
17
+ }
18
+ buildPreview() {
19
+ const startPoint = this.startPoint.pos;
20
+ const endPoint = this.endPoint.pos;
21
+ const toEnd = endPoint.minus(startPoint).normalized();
22
+ const arrowLength = endPoint.minus(startPoint).length();
23
+ // Ensure that the arrow tip is smaller than the arrow.
24
+ const arrowTipSize = Math.min(this.getLineWidth(), arrowLength / 2);
25
+ const startSize = this.startPoint.width / 2;
26
+ const endSize = this.endPoint.width / 2;
27
+ const arrowTipBase = endPoint.minus(toEnd.times(arrowTipSize));
28
+ // Scaled normal vectors.
29
+ const lineNormal = toEnd.orthog();
30
+ const scaledStartNormal = lineNormal.times(startSize);
31
+ const scaledBaseNormal = lineNormal.times(endSize);
32
+ const preview = new Stroke([
33
+ {
34
+ startPoint: arrowTipBase.minus(scaledBaseNormal),
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
+ ],
67
+ style: {
68
+ fill: this.startPoint.color,
69
+ }
70
+ }
71
+ ]);
72
+ return preview;
73
+ }
74
+ build() {
75
+ return this.buildPreview();
76
+ }
77
+ preview(renderer) {
78
+ this.buildPreview().render(renderer);
79
+ }
80
+ addPoint(point) {
81
+ this.endPoint = point;
82
+ }
83
+ }
@@ -1,15 +1,10 @@
1
- import Color4 from './Color4';
2
- import { RenderablePathSpec } from './rendering/AbstractRenderer';
3
- import { Point2 } from './geometry/Vec2';
4
- import Rect2 from './geometry/Rect2';
5
- import Stroke from './components/Stroke';
6
- export interface StrokeDataPoint {
7
- pos: Point2;
8
- width: number;
9
- time: number;
10
- color: Color4;
11
- }
12
- export default class StrokeBuilder {
1
+ import AbstractRenderer from '../../rendering/AbstractRenderer';
2
+ import Rect2 from '../../geometry/Rect2';
3
+ import Stroke from '../Stroke';
4
+ import { StrokeDataPoint } from '../../types';
5
+ import { ComponentBuilder, ComponentBuilderFactory } from './types';
6
+ export declare const makeFreehandLineBuilder: ComponentBuilderFactory;
7
+ export default class FreehandLineBuilder implements ComponentBuilder {
13
8
  private startPoint;
14
9
  private minFitAllowed;
15
10
  private maxFitAllowed;
@@ -25,7 +20,8 @@ export default class StrokeBuilder {
25
20
  constructor(startPoint: StrokeDataPoint, minFitAllowed: number, maxFitAllowed: number);
26
21
  getBBox(): Rect2;
27
22
  private getRenderingStyle;
28
- preview(): RenderablePathSpec[];
23
+ private getPreview;
24
+ preview(renderer: AbstractRenderer): void;
29
25
  build(): Stroke;
30
26
  private roundPoint;
31
27
  private finalizeCurrentCurve;
@@ -1,12 +1,20 @@
1
1
  import { Bezier } from 'bezier-js';
2
- import { Vec2 } from './geometry/Vec2';
3
- import Rect2 from './geometry/Rect2';
4
- import { PathCommandType } from './geometry/Path';
5
- import LineSegment2 from './geometry/LineSegment2';
6
- import Stroke from './components/Stroke';
7
- import Viewport from './Viewport';
2
+ import { Vec2 } from '../../geometry/Vec2';
3
+ import Rect2 from '../../geometry/Rect2';
4
+ import { PathCommandType } from '../../geometry/Path';
5
+ import LineSegment2 from '../../geometry/LineSegment2';
6
+ import Stroke from '../Stroke';
7
+ import Viewport from '../../Viewport';
8
+ export const makeFreehandLineBuilder = (initialPoint, viewport) => {
9
+ // Don't smooth if input is more than ± 7 pixels from the true curve, do smooth if
10
+ // less than ± 2 px from the curve.
11
+ const canvasTransform = viewport.screenToCanvasTransform;
12
+ const maxSmoothingDist = canvasTransform.transformVec3(Vec2.unitX).magnitude() * 7;
13
+ const minSmoothingDist = canvasTransform.transformVec3(Vec2.unitX).magnitude() * 2;
14
+ return new FreehandLineBuilder(initialPoint, minSmoothingDist, maxSmoothingDist);
15
+ };
8
16
  // Handles stroke smoothing and creates Strokes from user/stylus input.
9
- export default class StrokeBuilder {
17
+ export default class FreehandLineBuilder {
10
18
  constructor(startPoint,
11
19
  // Maximum distance from the actual curve (irrespective of stroke width)
12
20
  // for which a point is considered 'part of the curve'.
@@ -34,13 +42,18 @@ export default class StrokeBuilder {
34
42
  };
35
43
  }
36
44
  // Get the segments that make up this' path. Can be called after calling build()
37
- preview() {
45
+ getPreview() {
38
46
  if (this.currentCurve && this.lastPoint) {
39
47
  const currentPath = this.currentSegmentToPath();
40
48
  return this.segments.concat(currentPath);
41
49
  }
42
50
  return this.segments;
43
51
  }
52
+ preview(renderer) {
53
+ for (const part of this.getPreview()) {
54
+ renderer.drawPath(part);
55
+ }
56
+ }
44
57
  build() {
45
58
  if (this.lastPoint) {
46
59
  this.finalizeCurrentCurve();
@@ -179,7 +192,12 @@ export default class StrokeBuilder {
179
192
  return;
180
193
  }
181
194
  const threshold = Math.min(this.lastPoint.width, newPoint.width) / 4;
182
- if (this.lastPoint.pos.minus(newPoint.pos).magnitude() < threshold) {
195
+ const shouldSnapToInitial = this.startPoint.pos.minus(newPoint.pos).magnitude() < threshold
196
+ && this.segments.length === 0;
197
+ // Snap to the starting point if the stroke is contained within a small ball centered
198
+ // at the starting point.
199
+ // This allows us to create a circle/dot at the start of the stroke.
200
+ if (shouldSnapToInitial) {
183
201
  return;
184
202
  }
185
203
  const velocity = newPoint.pos.minus(this.lastPoint.pos).times(1 / (deltaTime) * 1000);
@@ -0,0 +1,16 @@
1
+ import Rect2 from '../../geometry/Rect2';
2
+ import AbstractRenderer from '../../rendering/AbstractRenderer';
3
+ import { StrokeDataPoint } from '../../types';
4
+ import AbstractComponent from '../AbstractComponent';
5
+ import { ComponentBuilder, ComponentBuilderFactory } from './types';
6
+ export declare const makeLineBuilder: ComponentBuilderFactory;
7
+ export default class LineBuilder implements ComponentBuilder {
8
+ private readonly startPoint;
9
+ private endPoint;
10
+ constructor(startPoint: StrokeDataPoint);
11
+ getBBox(): Rect2;
12
+ private buildPreview;
13
+ build(): AbstractComponent;
14
+ preview(renderer: AbstractRenderer): void;
15
+ addPoint(point: StrokeDataPoint): void;
16
+ }
@@ -0,0 +1,57 @@
1
+ import { PathCommandType } from '../../geometry/Path';
2
+ import Stroke from '../Stroke';
3
+ export const makeLineBuilder = (initialPoint, _viewport) => {
4
+ return new LineBuilder(initialPoint);
5
+ };
6
+ export default class LineBuilder {
7
+ constructor(startPoint) {
8
+ this.startPoint = startPoint;
9
+ this.endPoint = startPoint;
10
+ }
11
+ getBBox() {
12
+ const preview = this.buildPreview();
13
+ return preview.getBBox();
14
+ }
15
+ buildPreview() {
16
+ const startPoint = this.startPoint.pos;
17
+ const endPoint = this.endPoint.pos;
18
+ const toEnd = endPoint.minus(startPoint).normalized();
19
+ const startSize = this.startPoint.width / 2;
20
+ const endSize = this.endPoint.width / 2;
21
+ const lineNormal = toEnd.orthog();
22
+ const scaledStartNormal = lineNormal.times(startSize);
23
+ const scaledEndNormal = lineNormal.times(endSize);
24
+ const preview = new Stroke([
25
+ {
26
+ startPoint: startPoint.minus(scaledStartNormal),
27
+ commands: [
28
+ {
29
+ kind: PathCommandType.LineTo,
30
+ point: startPoint.plus(scaledStartNormal),
31
+ },
32
+ {
33
+ kind: PathCommandType.LineTo,
34
+ point: endPoint.plus(scaledEndNormal),
35
+ },
36
+ {
37
+ kind: PathCommandType.LineTo,
38
+ point: endPoint.minus(scaledEndNormal),
39
+ },
40
+ ],
41
+ style: {
42
+ fill: this.startPoint.color,
43
+ }
44
+ }
45
+ ]);
46
+ return preview;
47
+ }
48
+ build() {
49
+ return this.buildPreview();
50
+ }
51
+ preview(renderer) {
52
+ this.buildPreview().render(renderer);
53
+ }
54
+ addPoint(point) {
55
+ this.endPoint = point;
56
+ }
57
+ }
@@ -0,0 +1,18 @@
1
+ import Rect2 from '../../geometry/Rect2';
2
+ import AbstractRenderer from '../../rendering/AbstractRenderer';
3
+ import { StrokeDataPoint } from '../../types';
4
+ import AbstractComponent from '../AbstractComponent';
5
+ import { ComponentBuilder, ComponentBuilderFactory } from './types';
6
+ export declare const makeFilledRectangleBuilder: ComponentBuilderFactory;
7
+ export declare const makeOutlinedRectangleBuilder: ComponentBuilderFactory;
8
+ export default class RectangleBuilder implements ComponentBuilder {
9
+ private readonly startPoint;
10
+ private filled;
11
+ private endPoint;
12
+ constructor(startPoint: StrokeDataPoint, filled: boolean);
13
+ getBBox(): Rect2;
14
+ private buildPreview;
15
+ build(): AbstractComponent;
16
+ preview(renderer: AbstractRenderer): void;
17
+ addPoint(point: StrokeDataPoint): void;
18
+ }
@@ -0,0 +1,41 @@
1
+ import Path from '../../geometry/Path';
2
+ import Rect2 from '../../geometry/Rect2';
3
+ import Stroke from '../Stroke';
4
+ export const makeFilledRectangleBuilder = (initialPoint, _viewport) => {
5
+ return new RectangleBuilder(initialPoint, true);
6
+ };
7
+ export const makeOutlinedRectangleBuilder = (initialPoint, _viewport) => {
8
+ return new RectangleBuilder(initialPoint, false);
9
+ };
10
+ export default class RectangleBuilder {
11
+ constructor(startPoint, filled) {
12
+ this.startPoint = startPoint;
13
+ this.filled = filled;
14
+ // Initially, the start and end points are the same.
15
+ this.endPoint = startPoint;
16
+ }
17
+ getBBox() {
18
+ const preview = this.buildPreview();
19
+ return preview.getBBox();
20
+ }
21
+ buildPreview() {
22
+ const startPoint = this.startPoint.pos;
23
+ const endPoint = this.endPoint.pos;
24
+ const path = Path.fromRect(Rect2.fromCorners(startPoint, endPoint), this.filled ? null : this.endPoint.width);
25
+ const preview = new Stroke([
26
+ path.toRenderable({
27
+ fill: this.endPoint.color
28
+ }),
29
+ ]);
30
+ return preview;
31
+ }
32
+ build() {
33
+ return this.buildPreview();
34
+ }
35
+ preview(renderer) {
36
+ this.buildPreview().render(renderer);
37
+ }
38
+ addPoint(point) {
39
+ this.endPoint = point;
40
+ }
41
+ }
@@ -0,0 +1,12 @@
1
+ import Rect2 from '../../geometry/Rect2';
2
+ import AbstractRenderer from '../../rendering/AbstractRenderer';
3
+ import { StrokeDataPoint } from '../../types';
4
+ import Viewport from '../../Viewport';
5
+ import AbstractComponent from '../AbstractComponent';
6
+ export interface ComponentBuilder {
7
+ getBBox(): Rect2;
8
+ build(): AbstractComponent;
9
+ preview(renderer: AbstractRenderer): void;
10
+ addPoint(point: StrokeDataPoint): void;
11
+ }
12
+ export declare type ComponentBuilderFactory = (startPoint: StrokeDataPoint, viewport: Viewport) => ComponentBuilder;
@@ -0,0 +1 @@
1
+ export {};
@@ -46,6 +46,7 @@ export default class Path {
46
46
  intersection(line: LineSegment2): IntersectionResult[];
47
47
  transformedBy(affineTransfm: Mat33): Path;
48
48
  union(other: Path | null): Path;
49
+ static fromRect(rect: Rect2, lineWidth?: number | null): Path;
49
50
  static fromRenderable(renderable: RenderablePathSpec): Path;
50
51
  toRenderable(fill: RenderingStyle): RenderablePathSpec;
51
52
  toString(): string;
@@ -158,6 +158,38 @@ export default class Path {
158
158
  ...other.parts,
159
159
  ]);
160
160
  }
161
+ // Returns a path that outlines [rect]. If [lineWidth] is not given, the resultant path is
162
+ // the outline of [rect]. Otherwise, the resultant path represents a line of width [lineWidth]
163
+ // that traces [rect].
164
+ static fromRect(rect, lineWidth = null) {
165
+ const commands = [];
166
+ let corners;
167
+ let startPoint;
168
+ if (lineWidth !== null) {
169
+ // Vector from the top left corner or bottom right corner to the edge of the
170
+ // stroked region.
171
+ const cornerToEdge = Vec2.of(lineWidth, lineWidth).times(0.5);
172
+ const innerRect = Rect2.fromCorners(rect.topLeft.plus(cornerToEdge), rect.bottomRight.minus(cornerToEdge));
173
+ const outerRect = Rect2.fromCorners(rect.topLeft.minus(cornerToEdge), rect.bottomRight.plus(cornerToEdge));
174
+ corners = [
175
+ innerRect.corners[3],
176
+ ...innerRect.corners,
177
+ ...outerRect.corners.reverse(),
178
+ ];
179
+ startPoint = outerRect.corners[3];
180
+ }
181
+ else {
182
+ corners = rect.corners.slice(1);
183
+ startPoint = rect.corners[0];
184
+ }
185
+ for (const corner of corners) {
186
+ commands.push({
187
+ kind: PathCommandType.LineTo,
188
+ point: corner,
189
+ });
190
+ }
191
+ return new Path(startPoint, commands);
192
+ }
161
193
  static fromRenderable(renderable) {
162
194
  return new Path(renderable.startPoint, renderable.commands);
163
195
  }
@@ -9,6 +9,7 @@ export default class Vec3 {
9
9
  };
10
10
  static of(x: number, y: number, z: number): Vec3;
11
11
  at(idx: number): number;
12
+ length(): number;
12
13
  magnitude(): number;
13
14
  magnitudeSquared(): number;
14
15
  angle(): number;
@@ -18,6 +19,7 @@ export default class Vec3 {
18
19
  minus(v: Vec3): Vec3;
19
20
  dot(other: Vec3): number;
20
21
  cross(other: Vec3): Vec3;
22
+ orthog(): Vec3;
21
23
  extend(distance: number, direction: Vec3): Vec3;
22
24
  lerp(target: Vec3, fractionTo: number): Vec3;
23
25
  zip(other: Vec3, zip: (componentInThis: number, componentInOther: number) => number): Vec3;
@@ -26,6 +26,10 @@ export default class Vec3 {
26
26
  return this.z;
27
27
  throw new Error(`${idx} out of bounds!`);
28
28
  }
29
+ // Alias for this.magnitude
30
+ length() {
31
+ return this.magnitude();
32
+ }
29
33
  magnitude() {
30
34
  return Math.sqrt(this.dot(this));
31
35
  }
@@ -58,6 +62,15 @@ export default class Vec3 {
58
62
  // | x2 y2 z2|
59
63
  return Vec3.of(this.y * other.z - other.y * this.z, other.x * this.z - this.x * other.z, this.x * other.y - other.x * this.y);
60
64
  }
65
+ // Returns a vector orthogonal to this. If this is a Vec2, returns [this] rotated
66
+ // 90 degrees counter-clockwise.
67
+ orthog() {
68
+ // If parallel to the z-axis
69
+ if (this.dot(Vec3.unitX) === 0 && this.dot(Vec3.unitY) === 0) {
70
+ return this.dot(Vec3.unitX) === 0 ? Vec3.unitX : this.cross(Vec3.unitX).normalized();
71
+ }
72
+ return this.cross(Vec3.unitZ.times(-1)).normalized();
73
+ }
61
74
  // Returns this plus a vector of length [distance] in [direction]
62
75
  extend(distance, direction) {
63
76
  return this.plus(direction.normalized().times(distance));
@@ -1,6 +1,6 @@
1
1
  import { CommandLocalization } from './commands/localization';
2
2
  import { ImageComponentLocalization } from './components/localization';
3
- import { ToolbarLocalization } from './toolbar/types';
3
+ import { ToolbarLocalization } from './toolbar/localization';
4
4
  import { ToolLocalization } from './tools/localization';
5
5
  export interface EditorLocalization extends ToolbarLocalization, ToolLocalization, CommandLocalization, ImageComponentLocalization {
6
6
  undoAnnouncement: (actionDescription: string) => string;
@@ -1,5 +1,5 @@
1
1
  import { defaultCommandLocalization } from './commands/localization';
2
2
  import { defaultComponentLocalization } from './components/localization';
3
- import HTMLToolbar from './toolbar/HTMLToolbar';
3
+ import { defaultToolbarLocalization } from './toolbar/localization';
4
4
  import { defaultToolLocalization } from './tools/localization';
5
- export const defaultEditorLocalization = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, HTMLToolbar.defaultLocalization), defaultToolLocalization), defaultCommandLocalization), defaultComponentLocalization), { loading: (percentage) => `Loading ${percentage}%...`, imageEditor: 'Image Editor', doneLoading: 'Done loading', undoAnnouncement: (commandDescription) => `Undid ${commandDescription}`, redoAnnouncement: (commandDescription) => `Redid ${commandDescription}` });
5
+ export const defaultEditorLocalization = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, defaultToolbarLocalization), defaultToolLocalization), defaultCommandLocalization), defaultComponentLocalization), { loading: (percentage) => `Loading ${percentage}%...`, imageEditor: 'Image Editor', doneLoading: 'Done loading', undoAnnouncement: (commandDescription) => `Undid ${commandDescription}`, redoAnnouncement: (commandDescription) => `Redid ${commandDescription}` });
@@ -1,6 +1,4 @@
1
- import { PathCommandType } from '../geometry/Path';
2
- import Rect2 from '../geometry/Rect2';
3
- import { Vec2 } from '../geometry/Vec2';
1
+ import Path, { PathCommandType } from '../geometry/Path';
4
2
  const stylesEqual = (a, b) => {
5
3
  var _a, _b, _c, _d, _e;
6
4
  return a === b || (a.fill.eq(b.fill)
@@ -65,28 +63,8 @@ export default class AbstractRenderer {
65
63
  }
66
64
  // Draw a rectangle. Boundary lines have width [lineWidth] and are filled with [lineFill]
67
65
  drawRect(rect, lineWidth, lineFill) {
68
- const commands = [];
69
- // Vector from the top left corner or bottom right corner to the edge of the
70
- // stroked region.
71
- const cornerToEdge = Vec2.of(lineWidth, lineWidth).times(0.5);
72
- const innerRect = Rect2.fromCorners(rect.topLeft.plus(cornerToEdge), rect.bottomRight.minus(cornerToEdge));
73
- const outerRect = Rect2.fromCorners(rect.topLeft.minus(cornerToEdge), rect.bottomRight.plus(cornerToEdge));
74
- const corners = [
75
- innerRect.corners[3],
76
- ...innerRect.corners,
77
- ...outerRect.corners.reverse(),
78
- ];
79
- for (const corner of corners) {
80
- commands.push({
81
- kind: PathCommandType.LineTo,
82
- point: corner,
83
- });
84
- }
85
- this.drawPath({
86
- startPoint: outerRect.corners[3],
87
- commands,
88
- style: lineFill,
89
- });
66
+ const path = Path.fromRect(rect, lineWidth);
67
+ this.drawPath(path.toRenderable(lineFill));
90
68
  }
91
69
  // Note the start/end of an object with the given bounding box.
92
70
  startObject(_boundingBox) {
@@ -1,12 +1,14 @@
1
1
  import Editor from '../Editor';
2
- import { ToolbarLocalization } from './types';
2
+ import { ToolbarLocalization } from './localization';
3
3
  export default class HTMLToolbar {
4
4
  private editor;
5
5
  private localizationTable;
6
6
  private container;
7
- static defaultLocalization: ToolbarLocalization;
7
+ private penTypes;
8
8
  constructor(editor: Editor, parent: HTMLElement, localizationTable?: ToolbarLocalization);
9
+ setupColorPickers(): void;
9
10
  addActionButton(text: string, command: () => void, parent?: Element): HTMLButtonElement;
10
11
  private addUndoRedoButtons;
11
- private addElements;
12
+ addDefaultToolWidgets(): void;
13
+ addDefaultActionButtons(): void;
12
14
  }