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.
- package/.eslintrc.js +1 -0
- package/CHANGELOG.md +7 -0
- package/README.md +2 -2
- package/dist/bundle.js +1 -1
- package/dist/src/Color4.js +6 -2
- package/dist/src/Editor.d.ts +1 -0
- package/dist/src/Editor.js +19 -8
- package/dist/src/EditorImage.d.ts +8 -13
- package/dist/src/EditorImage.js +51 -29
- package/dist/src/Viewport.d.ts +9 -1
- package/dist/src/Viewport.js +3 -1
- package/dist/src/commands/Command.d.ts +9 -8
- package/dist/src/commands/Command.js +15 -14
- package/dist/src/commands/Duplicate.d.ts +14 -0
- package/dist/src/commands/Duplicate.js +34 -0
- package/dist/src/commands/Erase.d.ts +5 -2
- package/dist/src/commands/Erase.js +28 -9
- package/dist/src/commands/SerializableCommand.d.ts +13 -0
- package/dist/src/commands/SerializableCommand.js +28 -0
- package/dist/src/commands/localization.d.ts +2 -0
- package/dist/src/commands/localization.js +2 -0
- package/dist/src/components/AbstractComponent.d.ts +15 -2
- package/dist/src/components/AbstractComponent.js +122 -26
- package/dist/src/components/SVGGlobalAttributesObject.d.ts +6 -1
- package/dist/src/components/SVGGlobalAttributesObject.js +23 -1
- package/dist/src/components/Stroke.d.ts +5 -0
- package/dist/src/components/Stroke.js +32 -1
- package/dist/src/components/Text.d.ts +11 -4
- package/dist/src/components/Text.js +57 -3
- package/dist/src/components/UnknownSVGObject.d.ts +2 -0
- package/dist/src/components/UnknownSVGObject.js +12 -1
- package/dist/src/components/util/describeComponentList.d.ts +4 -0
- package/dist/src/components/util/describeComponentList.js +14 -0
- package/dist/src/geometry/Path.d.ts +4 -1
- package/dist/src/geometry/Path.js +4 -0
- package/dist/src/rendering/Display.d.ts +3 -0
- package/dist/src/rendering/Display.js +13 -0
- package/dist/src/rendering/RenderingStyle.d.ts +24 -0
- package/dist/src/rendering/RenderingStyle.js +32 -0
- package/dist/src/rendering/renderers/AbstractRenderer.d.ts +1 -8
- package/dist/src/rendering/renderers/AbstractRenderer.js +1 -6
- package/dist/src/rendering/renderers/CanvasRenderer.d.ts +2 -1
- package/dist/src/rendering/renderers/DummyRenderer.d.ts +2 -1
- package/dist/src/rendering/renderers/SVGRenderer.d.ts +2 -1
- package/dist/src/rendering/renderers/TextOnlyRenderer.d.ts +2 -1
- package/dist/src/toolbar/HTMLToolbar.d.ts +1 -1
- package/dist/src/toolbar/HTMLToolbar.js +52 -534
- package/dist/src/toolbar/icons.d.ts +5 -0
- package/dist/src/toolbar/icons.js +186 -13
- package/dist/src/toolbar/localization.d.ts +4 -0
- package/dist/src/toolbar/localization.js +4 -0
- package/dist/src/toolbar/makeColorInput.d.ts +5 -0
- package/dist/src/toolbar/makeColorInput.js +81 -0
- package/dist/src/toolbar/widgets/BaseToolWidget.d.ts +12 -0
- package/dist/src/toolbar/widgets/BaseToolWidget.js +44 -0
- package/dist/src/toolbar/widgets/BaseWidget.d.ts +32 -0
- package/dist/src/toolbar/widgets/BaseWidget.js +148 -0
- package/dist/src/toolbar/widgets/EraserWidget.d.ts +6 -0
- package/dist/src/toolbar/widgets/EraserWidget.js +14 -0
- package/dist/src/toolbar/widgets/HandToolWidget.d.ts +13 -0
- package/dist/src/toolbar/widgets/HandToolWidget.js +133 -0
- package/dist/src/toolbar/widgets/PenWidget.d.ts +20 -0
- package/dist/src/toolbar/widgets/PenWidget.js +131 -0
- package/dist/src/toolbar/widgets/SelectionWidget.d.ts +11 -0
- package/dist/src/toolbar/widgets/SelectionWidget.js +56 -0
- package/dist/src/toolbar/widgets/TextToolWidget.d.ts +13 -0
- package/dist/src/toolbar/widgets/TextToolWidget.js +72 -0
- package/dist/src/tools/Pen.js +1 -1
- package/dist/src/tools/PipetteTool.d.ts +20 -0
- package/dist/src/tools/PipetteTool.js +40 -0
- package/dist/src/tools/SelectionTool.d.ts +2 -0
- package/dist/src/tools/SelectionTool.js +41 -23
- package/dist/src/tools/TextTool.js +1 -1
- package/dist/src/tools/ToolController.d.ts +3 -1
- package/dist/src/tools/ToolController.js +4 -0
- package/dist/src/tools/localization.d.ts +2 -1
- package/dist/src/tools/localization.js +3 -2
- package/dist/src/types.d.ts +7 -2
- package/dist/src/types.js +1 -0
- package/jest.config.js +2 -0
- package/package.json +6 -6
- package/src/Color4.ts +9 -3
- package/src/Editor.ts +23 -11
- package/src/EditorImage.test.ts +4 -4
- package/src/EditorImage.ts +61 -20
- package/src/SVGLoader.ts +2 -1
- package/src/Viewport.ts +2 -1
- package/src/commands/Command.ts +21 -19
- package/src/commands/Duplicate.ts +49 -0
- package/src/commands/Erase.ts +34 -13
- package/src/commands/SerializableCommand.ts +41 -0
- package/src/commands/localization.ts +5 -0
- package/src/components/AbstractComponent.ts +168 -26
- package/src/components/SVGGlobalAttributesObject.ts +34 -2
- package/src/components/Stroke.test.ts +53 -0
- package/src/components/Stroke.ts +37 -2
- package/src/components/Text.test.ts +38 -0
- package/src/components/Text.ts +80 -5
- package/src/components/UnknownSVGObject.test.ts +10 -0
- package/src/components/UnknownSVGObject.ts +15 -1
- package/src/components/builders/FreehandLineBuilder.ts +2 -1
- package/src/components/util/describeComponentList.ts +18 -0
- package/src/geometry/Path.ts +8 -1
- package/src/rendering/Display.ts +17 -1
- package/src/rendering/RenderingStyle.test.ts +68 -0
- package/src/rendering/RenderingStyle.ts +46 -0
- package/src/rendering/caching/RenderingCache.test.ts +1 -1
- package/src/rendering/renderers/AbstractRenderer.ts +1 -15
- package/src/rendering/renderers/CanvasRenderer.ts +2 -1
- package/src/rendering/renderers/DummyRenderer.ts +2 -1
- package/src/rendering/renderers/SVGRenderer.ts +2 -1
- package/src/rendering/renderers/TextOnlyRenderer.ts +2 -1
- package/src/toolbar/HTMLToolbar.ts +58 -660
- package/src/toolbar/icons.ts +205 -13
- package/src/toolbar/localization.ts +10 -2
- package/src/toolbar/makeColorInput.ts +105 -0
- package/src/toolbar/toolbar.css +116 -78
- package/src/toolbar/widgets/BaseToolWidget.ts +53 -0
- package/src/toolbar/widgets/BaseWidget.ts +175 -0
- package/src/toolbar/widgets/EraserWidget.ts +16 -0
- package/src/toolbar/widgets/HandToolWidget.ts +186 -0
- package/src/toolbar/widgets/PenWidget.ts +165 -0
- package/src/toolbar/widgets/SelectionWidget.ts +72 -0
- package/src/toolbar/widgets/TextToolWidget.ts +90 -0
- package/src/tools/Pen.ts +1 -1
- package/src/tools/PipetteTool.ts +56 -0
- package/src/tools/SelectionTool.test.ts +2 -4
- package/src/tools/SelectionTool.ts +47 -27
- package/src/tools/TextTool.ts +1 -1
- package/src/tools/ToolController.ts +10 -6
- package/src/tools/UndoRedoShortcut.test.ts +1 -1
- package/src/tools/localization.ts +6 -3
- package/src/types.ts +12 -1
package/src/geometry/Path.ts
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import { Bezier } from 'bezier-js';
|
2
|
-
import {
|
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
|
}
|
package/src/rendering/Display.ts
CHANGED
@@ -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(
|
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
|
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
|
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
|
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
|
8
|
+
import RenderingStyle from '../RenderingStyle';
|
9
|
+
import AbstractRenderer from './AbstractRenderer';
|
9
10
|
|
10
11
|
// Outputs a description of what was rendered.
|
11
12
|
|