js-draw 0.1.6 → 0.1.7

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 (133) hide show
  1. package/.eslintrc.js +1 -0
  2. package/CHANGELOG.md +7 -0
  3. package/README.md +2 -2
  4. package/dist/bundle.js +1 -1
  5. package/dist/src/Color4.js +6 -2
  6. package/dist/src/Editor.d.ts +1 -0
  7. package/dist/src/Editor.js +19 -8
  8. package/dist/src/EditorImage.d.ts +8 -13
  9. package/dist/src/EditorImage.js +51 -29
  10. package/dist/src/Viewport.d.ts +9 -1
  11. package/dist/src/Viewport.js +3 -1
  12. package/dist/src/commands/Command.d.ts +9 -8
  13. package/dist/src/commands/Command.js +15 -14
  14. package/dist/src/commands/Duplicate.d.ts +14 -0
  15. package/dist/src/commands/Duplicate.js +34 -0
  16. package/dist/src/commands/Erase.d.ts +5 -2
  17. package/dist/src/commands/Erase.js +28 -9
  18. package/dist/src/commands/SerializableCommand.d.ts +13 -0
  19. package/dist/src/commands/SerializableCommand.js +28 -0
  20. package/dist/src/commands/localization.d.ts +2 -0
  21. package/dist/src/commands/localization.js +2 -0
  22. package/dist/src/components/AbstractComponent.d.ts +15 -2
  23. package/dist/src/components/AbstractComponent.js +122 -26
  24. package/dist/src/components/SVGGlobalAttributesObject.d.ts +6 -1
  25. package/dist/src/components/SVGGlobalAttributesObject.js +23 -1
  26. package/dist/src/components/Stroke.d.ts +5 -0
  27. package/dist/src/components/Stroke.js +32 -1
  28. package/dist/src/components/Text.d.ts +11 -4
  29. package/dist/src/components/Text.js +57 -3
  30. package/dist/src/components/UnknownSVGObject.d.ts +2 -0
  31. package/dist/src/components/UnknownSVGObject.js +12 -1
  32. package/dist/src/components/util/describeComponentList.d.ts +4 -0
  33. package/dist/src/components/util/describeComponentList.js +14 -0
  34. package/dist/src/geometry/Path.d.ts +4 -1
  35. package/dist/src/geometry/Path.js +4 -0
  36. package/dist/src/rendering/Display.d.ts +3 -0
  37. package/dist/src/rendering/Display.js +13 -0
  38. package/dist/src/rendering/RenderingStyle.d.ts +24 -0
  39. package/dist/src/rendering/RenderingStyle.js +32 -0
  40. package/dist/src/rendering/renderers/AbstractRenderer.d.ts +1 -8
  41. package/dist/src/rendering/renderers/AbstractRenderer.js +1 -6
  42. package/dist/src/rendering/renderers/CanvasRenderer.d.ts +2 -1
  43. package/dist/src/rendering/renderers/DummyRenderer.d.ts +2 -1
  44. package/dist/src/rendering/renderers/SVGRenderer.d.ts +2 -1
  45. package/dist/src/rendering/renderers/TextOnlyRenderer.d.ts +2 -1
  46. package/dist/src/toolbar/HTMLToolbar.d.ts +1 -1
  47. package/dist/src/toolbar/HTMLToolbar.js +52 -534
  48. package/dist/src/toolbar/icons.d.ts +5 -0
  49. package/dist/src/toolbar/icons.js +186 -13
  50. package/dist/src/toolbar/localization.d.ts +4 -0
  51. package/dist/src/toolbar/localization.js +4 -0
  52. package/dist/src/toolbar/makeColorInput.d.ts +5 -0
  53. package/dist/src/toolbar/makeColorInput.js +81 -0
  54. package/dist/src/toolbar/widgets/BaseToolWidget.d.ts +12 -0
  55. package/dist/src/toolbar/widgets/BaseToolWidget.js +44 -0
  56. package/dist/src/toolbar/widgets/BaseWidget.d.ts +32 -0
  57. package/dist/src/toolbar/widgets/BaseWidget.js +148 -0
  58. package/dist/src/toolbar/widgets/EraserWidget.d.ts +6 -0
  59. package/dist/src/toolbar/widgets/EraserWidget.js +14 -0
  60. package/dist/src/toolbar/widgets/HandToolWidget.d.ts +13 -0
  61. package/dist/src/toolbar/widgets/HandToolWidget.js +133 -0
  62. package/dist/src/toolbar/widgets/PenWidget.d.ts +20 -0
  63. package/dist/src/toolbar/widgets/PenWidget.js +131 -0
  64. package/dist/src/toolbar/widgets/SelectionWidget.d.ts +11 -0
  65. package/dist/src/toolbar/widgets/SelectionWidget.js +56 -0
  66. package/dist/src/toolbar/widgets/TextToolWidget.d.ts +13 -0
  67. package/dist/src/toolbar/widgets/TextToolWidget.js +72 -0
  68. package/dist/src/tools/Pen.js +1 -1
  69. package/dist/src/tools/PipetteTool.d.ts +20 -0
  70. package/dist/src/tools/PipetteTool.js +40 -0
  71. package/dist/src/tools/SelectionTool.d.ts +2 -0
  72. package/dist/src/tools/SelectionTool.js +41 -23
  73. package/dist/src/tools/TextTool.js +1 -1
  74. package/dist/src/tools/ToolController.d.ts +3 -1
  75. package/dist/src/tools/ToolController.js +4 -0
  76. package/dist/src/tools/localization.d.ts +2 -1
  77. package/dist/src/tools/localization.js +3 -2
  78. package/dist/src/types.d.ts +7 -2
  79. package/dist/src/types.js +1 -0
  80. package/jest.config.js +2 -0
  81. package/package.json +6 -6
  82. package/src/Color4.ts +9 -3
  83. package/src/Editor.ts +23 -11
  84. package/src/EditorImage.test.ts +4 -4
  85. package/src/EditorImage.ts +61 -20
  86. package/src/SVGLoader.ts +2 -1
  87. package/src/Viewport.ts +2 -1
  88. package/src/commands/Command.ts +21 -19
  89. package/src/commands/Duplicate.ts +49 -0
  90. package/src/commands/Erase.ts +34 -13
  91. package/src/commands/SerializableCommand.ts +41 -0
  92. package/src/commands/localization.ts +5 -0
  93. package/src/components/AbstractComponent.ts +168 -26
  94. package/src/components/SVGGlobalAttributesObject.ts +34 -2
  95. package/src/components/Stroke.test.ts +53 -0
  96. package/src/components/Stroke.ts +37 -2
  97. package/src/components/Text.test.ts +38 -0
  98. package/src/components/Text.ts +80 -5
  99. package/src/components/UnknownSVGObject.test.ts +10 -0
  100. package/src/components/UnknownSVGObject.ts +15 -1
  101. package/src/components/builders/FreehandLineBuilder.ts +2 -1
  102. package/src/components/util/describeComponentList.ts +18 -0
  103. package/src/geometry/Path.ts +8 -1
  104. package/src/rendering/Display.ts +17 -1
  105. package/src/rendering/RenderingStyle.test.ts +68 -0
  106. package/src/rendering/RenderingStyle.ts +46 -0
  107. package/src/rendering/caching/RenderingCache.test.ts +1 -1
  108. package/src/rendering/renderers/AbstractRenderer.ts +1 -15
  109. package/src/rendering/renderers/CanvasRenderer.ts +2 -1
  110. package/src/rendering/renderers/DummyRenderer.ts +2 -1
  111. package/src/rendering/renderers/SVGRenderer.ts +2 -1
  112. package/src/rendering/renderers/TextOnlyRenderer.ts +2 -1
  113. package/src/toolbar/HTMLToolbar.ts +58 -660
  114. package/src/toolbar/icons.ts +205 -13
  115. package/src/toolbar/localization.ts +10 -2
  116. package/src/toolbar/makeColorInput.ts +105 -0
  117. package/src/toolbar/toolbar.css +116 -78
  118. package/src/toolbar/widgets/BaseToolWidget.ts +53 -0
  119. package/src/toolbar/widgets/BaseWidget.ts +175 -0
  120. package/src/toolbar/widgets/EraserWidget.ts +16 -0
  121. package/src/toolbar/widgets/HandToolWidget.ts +186 -0
  122. package/src/toolbar/widgets/PenWidget.ts +165 -0
  123. package/src/toolbar/widgets/SelectionWidget.ts +72 -0
  124. package/src/toolbar/widgets/TextToolWidget.ts +90 -0
  125. package/src/tools/Pen.ts +1 -1
  126. package/src/tools/PipetteTool.ts +56 -0
  127. package/src/tools/SelectionTool.test.ts +2 -4
  128. package/src/tools/SelectionTool.ts +47 -27
  129. package/src/tools/TextTool.ts +1 -1
  130. package/src/tools/ToolController.ts +10 -6
  131. package/src/tools/UndoRedoShortcut.test.ts +1 -1
  132. package/src/tools/localization.ts +6 -3
  133. package/src/types.ts +12 -1
@@ -1,5 +1,6 @@
1
1
  import { Bezier } from 'bezier-js';
2
- import { RenderingStyle, RenderablePathSpec } from '../rendering/renderers/AbstractRenderer';
2
+ import { RenderablePathSpec } from '../rendering/renderers/AbstractRenderer';
3
+ import RenderingStyle from '../rendering/RenderingStyle';
3
4
  import LineSegment2 from './LineSegment2';
4
5
  import Mat33 from './Mat33';
5
6
  import Rect2 from './Rect2';
@@ -282,6 +283,10 @@ export default class Path {
282
283
  return Path.toString(this.startPoint, this.parts);
283
284
  }
284
285
 
286
+ public serialize(): string {
287
+ return this.toString();
288
+ }
289
+
285
290
  public static toString(startPoint: Point2, parts: PathCommand[]): string {
286
291
  const result: string[] = [];
287
292
 
@@ -555,4 +560,6 @@ export default class Path {
555
560
 
556
561
  return new Path(startPos ?? Vec2.zero, commands);
557
562
  }
563
+
564
+ public static empty: Path = new Path(Vec2.zero, []);
558
565
  }
@@ -3,9 +3,10 @@ import CanvasRenderer from './renderers/CanvasRenderer';
3
3
  import { Editor } from '../Editor';
4
4
  import { EditorEventType } from '../types';
5
5
  import DummyRenderer from './renderers/DummyRenderer';
6
- import { Vec2 } from '../geometry/Vec2';
6
+ import { Point2, Vec2 } from '../geometry/Vec2';
7
7
  import RenderingCache from './caching/RenderingCache';
8
8
  import TextOnlyRenderer from './renderers/TextOnlyRenderer';
9
+ import Color4 from '../Color4';
9
10
 
10
11
  export enum RenderingMode {
11
12
  DummyRenderer,
@@ -88,6 +89,10 @@ export default class Display {
88
89
  return this.cache;
89
90
  }
90
91
 
92
+ public getColorAt = (_screenPos: Point2): Color4|null => {
93
+ return null;
94
+ };
95
+
91
96
  private initializeCanvasRendering() {
92
97
  const dryInkCanvas = document.createElement('canvas');
93
98
  const wetInkCanvas = document.createElement('canvas');
@@ -132,6 +137,17 @@ export default class Display {
132
137
  this.flattenCallback = () => {
133
138
  dryInkCtx.drawImage(wetInkCanvas, 0, 0);
134
139
  };
140
+
141
+ this.getColorAt = (screenPos: Point2) => {
142
+ const pixel = dryInkCtx.getImageData(screenPos.x, screenPos.y, 1, 1);
143
+ const data = pixel?.data;
144
+
145
+ if (data) {
146
+ const color = Color4.ofRGBA(data[0] / 255, data[1] / 255, data[2] / 255, data[3] / 255);
147
+ return color;
148
+ }
149
+ return null;
150
+ };
135
151
  }
136
152
 
137
153
  private initializeTextRendering() {
@@ -0,0 +1,68 @@
1
+
2
+ import Color4 from '../Color4';
3
+ import RenderingStyle, { styleFromJSON, stylesEqual, styleToJSON } from './RenderingStyle';
4
+
5
+
6
+ describe('RenderingStyle', () => {
7
+ it('identical styles should be equal', () => {
8
+ const redFill: RenderingStyle = {
9
+ fill: Color4.red,
10
+ };
11
+ expect(stylesEqual(redFill, redFill)).toBe(true);
12
+ expect(stylesEqual(
13
+ { fill: Color4.ofRGB(1, 0, 0.3), },
14
+ { fill: Color4.ofRGB(1, 0, 0.3), },
15
+ )).toBe(true);
16
+ expect(stylesEqual(
17
+ { fill: Color4.red },
18
+ { fill: Color4.blue },
19
+ )).toBe(false);
20
+
21
+ expect(stylesEqual(
22
+ { fill: Color4.red, stroke: { width: 1, color: Color4.red }},
23
+ { fill: Color4.red },
24
+ )).toBe(false);
25
+ expect(stylesEqual(
26
+ { fill: Color4.red, stroke: { width: 1, color: Color4.red }},
27
+ { fill: Color4.red, stroke: { width: 1, color: Color4.blue }},
28
+ )).toBe(false);
29
+ expect(stylesEqual(
30
+ { fill: Color4.red, stroke: { width: 1, color: Color4.red }},
31
+ { fill: Color4.red, stroke: { width: 1, color: Color4.red }},
32
+ )).toBe(true);
33
+ expect(stylesEqual(
34
+ { fill: Color4.red, stroke: { width: 1, color: Color4.red }},
35
+ { fill: Color4.red, stroke: { width: 2, color: Color4.red }},
36
+ )).toBe(false);
37
+ });
38
+
39
+ it('styles should be convertable to JSON', () => {
40
+ expect(styleToJSON({
41
+ fill: Color4.red,
42
+ })).toMatchObject({
43
+ fill: '#ff0000',
44
+ stroke: undefined,
45
+ });
46
+
47
+ expect(styleToJSON({
48
+ fill: Color4.blue,
49
+ stroke: {
50
+ width: 4,
51
+ color: Color4.red,
52
+ }
53
+ })).toMatchObject({
54
+ fill: '#0000ff',
55
+ stroke: {
56
+ width: 4,
57
+ color: '#ff0000'
58
+ },
59
+ });
60
+ });
61
+
62
+ it('JSON should be convertable into styles', () => {
63
+ const redFillJSON = { fill: '#ff0000', };
64
+ const redFillBlueStrokeJSON = { fill: '#ff0000', stroke: { width: 4, color: '#0000ff' }};
65
+ expect(styleToJSON(styleFromJSON(redFillJSON))).toMatchObject(redFillJSON);
66
+ expect(styleToJSON(styleFromJSON(redFillBlueStrokeJSON))).toMatchObject(redFillBlueStrokeJSON);
67
+ });
68
+ });
@@ -0,0 +1,46 @@
1
+ import Color4 from '../Color4';
2
+
3
+ interface RenderingStyle {
4
+ fill: Color4;
5
+ stroke?: {
6
+ color: Color4;
7
+ width: number;
8
+ };
9
+ }
10
+
11
+ export default RenderingStyle;
12
+
13
+ export const stylesEqual = (a: RenderingStyle, b: RenderingStyle): boolean => {
14
+ const result = a === b || (a.fill.eq(b.fill)
15
+ && (a.stroke == undefined) === (b.stroke == undefined)
16
+ && (a.stroke?.color?.eq(b.stroke?.color) ?? true)
17
+ && a.stroke?.width === b.stroke?.width);
18
+
19
+ // Map undefined/null -> false
20
+ return result ?? false;
21
+ };
22
+
23
+ // Returns an object that can be converted to a JSON string with
24
+ // JSON.stringify.
25
+ export const styleToJSON = (style: RenderingStyle) => {
26
+ const stroke = !style.stroke ? undefined : {
27
+ color: style.stroke.color.toHexString(),
28
+ width: style.stroke.width,
29
+ };
30
+
31
+ return {
32
+ fill: style.fill.toHexString(),
33
+ stroke,
34
+ };
35
+ };
36
+
37
+ export const styleFromJSON = (json: Record<string, any>) => {
38
+ const stroke = json.stroke ? {
39
+ color: Color4.fromHex(json.stroke.color),
40
+ width: json.stroke.width,
41
+ } : undefined;
42
+ return {
43
+ fill: Color4.fromHex(json.fill),
44
+ stroke,
45
+ };
46
+ };
@@ -28,7 +28,7 @@ describe('RenderingCache', () => {
28
28
  editor.image.renderWithCache(screenRenderer, cache, editor.viewport);
29
29
  expect(lastRenderer).toBeNull();
30
30
 
31
- editor.dispatch(new EditorImage.AddElementCommand(testStroke));
31
+ editor.dispatch(EditorImage.addElement(testStroke));
32
32
  editor.image.renderWithCache(screenRenderer, cache, editor.viewport);
33
33
 
34
34
  expect(allocdRenderers).toBeGreaterThanOrEqual(1);
@@ -1,4 +1,3 @@
1
- import Color4 from '../../Color4';
2
1
  import { LoadSaveDataTable } from '../../components/AbstractComponent';
3
2
  import { TextStyle } from '../../components/Text';
4
3
  import Mat33 from '../../geometry/Mat33';
@@ -6,14 +5,7 @@ import Path, { PathCommand, PathCommandType } from '../../geometry/Path';
6
5
  import Rect2 from '../../geometry/Rect2';
7
6
  import { Point2, Vec2 } from '../../geometry/Vec2';
8
7
  import Viewport from '../../Viewport';
9
-
10
- export interface RenderingStyle {
11
- fill: Color4;
12
- stroke?: {
13
- color: Color4;
14
- width: number;
15
- };
16
- }
8
+ import RenderingStyle, { stylesEqual } from '../RenderingStyle';
17
9
 
18
10
  export interface RenderablePathSpec {
19
11
  startPoint: Point2;
@@ -21,12 +13,6 @@ export interface RenderablePathSpec {
21
13
  style: RenderingStyle;
22
14
  }
23
15
 
24
- const stylesEqual = (a: RenderingStyle, b: RenderingStyle) => {
25
- return a === b || (a.fill.eq(b.fill)
26
- && a.stroke?.color?.eq(b.stroke?.color)
27
- && a.stroke?.width === b.stroke?.width);
28
- };
29
-
30
16
  export default abstract class AbstractRenderer {
31
17
  // If null, this' transformation is linked to the Viewport
32
18
  private selfTransform: Mat33|null = null;
@@ -5,7 +5,8 @@ import Rect2 from '../../geometry/Rect2';
5
5
  import { Point2, Vec2 } from '../../geometry/Vec2';
6
6
  import Vec3 from '../../geometry/Vec3';
7
7
  import Viewport from '../../Viewport';
8
- import AbstractRenderer, { RenderablePathSpec, RenderingStyle } from './AbstractRenderer';
8
+ import RenderingStyle from '../RenderingStyle';
9
+ import AbstractRenderer, { RenderablePathSpec } from './AbstractRenderer';
9
10
 
10
11
  export default class CanvasRenderer extends AbstractRenderer {
11
12
  private ignoreObjectsAboveLevel: number|null = null;
@@ -6,7 +6,8 @@ import Rect2 from '../../geometry/Rect2';
6
6
  import { Point2, Vec2 } from '../../geometry/Vec2';
7
7
  import Vec3 from '../../geometry/Vec3';
8
8
  import Viewport from '../../Viewport';
9
- import AbstractRenderer, { RenderingStyle } from './AbstractRenderer';
9
+ import RenderingStyle from '../RenderingStyle';
10
+ import AbstractRenderer from './AbstractRenderer';
10
11
 
11
12
  export default class DummyRenderer extends AbstractRenderer {
12
13
  // Variables that track the state of what's been rendered
@@ -7,7 +7,8 @@ import Rect2 from '../../geometry/Rect2';
7
7
  import { Point2, Vec2 } from '../../geometry/Vec2';
8
8
  import { svgAttributesDataKey, SVGLoaderUnknownAttribute, SVGLoaderUnknownStyleAttribute, svgStyleAttributesDataKey } from '../../SVGLoader';
9
9
  import Viewport from '../../Viewport';
10
- import AbstractRenderer, { RenderingStyle } from './AbstractRenderer';
10
+ import RenderingStyle from '../RenderingStyle';
11
+ import AbstractRenderer from './AbstractRenderer';
11
12
 
12
13
  const svgNameSpace = 'http://www.w3.org/2000/svg';
13
14
  export default class SVGRenderer extends AbstractRenderer {
@@ -5,7 +5,8 @@ import { Vec2 } from '../../geometry/Vec2';
5
5
  import Vec3 from '../../geometry/Vec3';
6
6
  import Viewport from '../../Viewport';
7
7
  import { TextRendererLocalization } from '../localization';
8
- import AbstractRenderer, { RenderingStyle } from './AbstractRenderer';
8
+ import RenderingStyle from '../RenderingStyle';
9
+ import AbstractRenderer from './AbstractRenderer';
9
10
 
10
11
  // Outputs a description of what was rendered.
11
12