js-draw 0.9.0 → 0.9.1
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 +5 -0
- package/dist/bundle.js +1 -1
- package/dist/src/Editor.d.ts +2 -0
- package/dist/src/Editor.js +31 -6
- package/dist/src/Viewport.js +2 -2
- package/dist/src/components/builders/LineBuilder.js +4 -0
- package/dist/src/components/util/StrokeSmoother.js +1 -1
- package/dist/src/rendering/renderers/SVGRenderer.js +6 -1
- package/dist/src/tools/SelectionTool/Selection.js +1 -1
- package/package.json +1 -1
- package/src/Editor.ts +43 -9
- package/src/Viewport.ts +2 -2
- package/src/components/builders/LineBuilder.ts +4 -0
- package/src/components/util/StrokeSmoother.ts +1 -1
- package/src/rendering/renderers/SVGRenderer.ts +5 -1
- package/src/tools/SelectionTool/Selection.ts +1 -1
- package/.firebase/hosting.ZG9jcw.cache +0 -338
package/dist/src/Editor.d.ts
CHANGED
@@ -197,7 +197,9 @@ export declare class Editor {
|
|
197
197
|
addStyleSheet(content: string): HTMLStyleElement;
|
198
198
|
sendKeyboardEvent(eventType: InputEvtType.KeyPressEvent | InputEvtType.KeyUpEvent, key: string, ctrlKey?: boolean, altKey?: boolean): void;
|
199
199
|
sendPenEvent(eventType: InputEvtType.PointerDownEvt | InputEvtType.PointerMoveEvt | InputEvtType.PointerUpEvt, point: Point2, allPointers?: Pointer[]): void;
|
200
|
+
toDataURL(format?: 'image/png' | 'image/jpeg' | 'image/webp'): string;
|
200
201
|
toSVG(): SVGElement;
|
202
|
+
private renderAllWithTransform;
|
201
203
|
loadFrom(loader: ImageLoader): Promise<void>;
|
202
204
|
getImportExportRect(): Rect2;
|
203
205
|
setImportExportRect(imageRect: Rect2): Command;
|
package/dist/src/Editor.js
CHANGED
@@ -43,6 +43,7 @@ import Mat33 from './math/Mat33';
|
|
43
43
|
import getLocalizationTable from './localizations/getLocalizationTable';
|
44
44
|
import IconProvider from './toolbar/IconProvider';
|
45
45
|
import { toRoundedString } from './math/rounding';
|
46
|
+
import CanvasRenderer from './rendering/renderers/CanvasRenderer';
|
46
47
|
// { @inheritDoc Editor! }
|
47
48
|
export class Editor {
|
48
49
|
/**
|
@@ -626,17 +627,29 @@ export class Editor {
|
|
626
627
|
current: mainPointer,
|
627
628
|
});
|
628
629
|
}
|
630
|
+
// Get a data URL (e.g. as produced by `HTMLCanvasElement::toDataURL`).
|
631
|
+
// If `format` is not `image/png`, a PNG image URL may still be returned (as in the
|
632
|
+
// case of `HTMLCanvasElement::toDataURL`).
|
633
|
+
//
|
634
|
+
// The export resolution is the same as the size of the drawing canvas.
|
635
|
+
toDataURL(format = 'image/png') {
|
636
|
+
const canvas = document.createElement('canvas');
|
637
|
+
const resolution = this.importExportViewport.getResolution();
|
638
|
+
canvas.width = resolution.x;
|
639
|
+
canvas.height = resolution.y;
|
640
|
+
const ctx = canvas.getContext('2d');
|
641
|
+
const renderer = new CanvasRenderer(ctx, this.importExportViewport);
|
642
|
+
// Render everything with no transform (0,0) should be (0,0) in the output image
|
643
|
+
this.renderAllWithTransform(renderer, this.importExportViewport, Mat33.identity);
|
644
|
+
const dataURL = canvas.toDataURL(format);
|
645
|
+
return dataURL;
|
646
|
+
}
|
629
647
|
toSVG() {
|
630
648
|
const importExportViewport = this.importExportViewport;
|
631
649
|
const svgNameSpace = 'http://www.w3.org/2000/svg';
|
632
650
|
const result = document.createElementNS(svgNameSpace, 'svg');
|
633
651
|
const renderer = new SVGRenderer(result, importExportViewport);
|
634
|
-
|
635
|
-
// Reset the transform to ensure that (0, 0) is (0, 0)
|
636
|
-
importExportViewport.resetTransform(Mat33.identity);
|
637
|
-
// Render **all** elements.
|
638
|
-
this.image.renderAll(renderer);
|
639
|
-
importExportViewport.resetTransform(origTransform);
|
652
|
+
this.renderAllWithTransform(renderer, importExportViewport);
|
640
653
|
// Just show the main region
|
641
654
|
const rect = importExportViewport.visibleRect;
|
642
655
|
result.setAttribute('viewBox', [rect.x, rect.y, rect.w, rect.h].map(part => toRoundedString(part)).join(' '));
|
@@ -649,6 +662,18 @@ export class Editor {
|
|
649
662
|
result.setAttribute('xmlns', svgNameSpace);
|
650
663
|
return result;
|
651
664
|
}
|
665
|
+
// Renders everything in this' image to `renderer`, but first transforming the given `viewport`
|
666
|
+
// such that its transform is `transform`. The given `viewport`'s transform is restored before this method
|
667
|
+
// returns.
|
668
|
+
//
|
669
|
+
// For example, rendering with `transform = Mat33.identity` *sets* `viewport`'s transform to `Mat33.identity`,
|
670
|
+
// renders everything in this' image to `renderer`, then restores `viewport`'s transform to whatever it was before.
|
671
|
+
renderAllWithTransform(renderer, viewport, transform = Mat33.identity) {
|
672
|
+
const origTransform = this.importExportViewport.canvasToScreenTransform;
|
673
|
+
viewport.resetTransform(transform);
|
674
|
+
this.image.renderAll(renderer);
|
675
|
+
viewport.resetTransform(origTransform);
|
676
|
+
}
|
652
677
|
loadFrom(loader) {
|
653
678
|
return __awaiter(this, void 0, void 0, function* () {
|
654
679
|
this.showLoadingWarning(0);
|
package/dist/src/Viewport.js
CHANGED
@@ -103,8 +103,8 @@ export class Viewport {
|
|
103
103
|
}
|
104
104
|
// Represent as k 10ⁿ for some n, k ∈ ℤ.
|
105
105
|
const decimalComponent = Math.pow(10, Math.floor(Math.log10(Math.abs(scaleRatio))));
|
106
|
-
const
|
107
|
-
scaleRatio = Math.round(scaleRatio / decimalComponent *
|
106
|
+
const roundAmountFactor = Math.pow(2, roundAmount);
|
107
|
+
scaleRatio = Math.round(scaleRatio / decimalComponent * roundAmountFactor) / roundAmountFactor * decimalComponent;
|
108
108
|
return scaleRatio;
|
109
109
|
}
|
110
110
|
// Computes and returns an affine transformation that makes `toMakeVisible` visible and roughly centered on the screen.
|
@@ -37,6 +37,10 @@ export default class LineBuilder {
|
|
37
37
|
kind: PathCommandType.LineTo,
|
38
38
|
point: endPoint.minus(scaledEndNormal),
|
39
39
|
},
|
40
|
+
{
|
41
|
+
kind: PathCommandType.LineTo,
|
42
|
+
point: startPoint.minus(scaledStartNormal),
|
43
|
+
},
|
40
44
|
],
|
41
45
|
style: {
|
42
46
|
fill: this.startPoint.color,
|
@@ -167,7 +167,7 @@ export class StrokeSmoother {
|
|
167
167
|
if (!controlPoint || segmentStart.eq(controlPoint) || segmentEnd.eq(controlPoint)) {
|
168
168
|
// Position the control point closer to the first -- the connecting
|
169
169
|
// segment will be roughly a line.
|
170
|
-
controlPoint = segmentStart.plus(enteringVec.times(startEndDist /
|
170
|
+
controlPoint = segmentStart.plus(enteringVec.times(startEndDist / 4));
|
171
171
|
}
|
172
172
|
console.assert(!segmentStart.eq(controlPoint, 1e-11), 'Start and control points are equal!');
|
173
173
|
console.assert(!controlPoint.eq(segmentEnd, 1e-11), 'Control and end points are equal!');
|
@@ -79,7 +79,12 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
79
79
|
const pathElem = document.createElementNS(svgNameSpace, 'path');
|
80
80
|
pathElem.setAttribute('d', this.lastPathString.join(' '));
|
81
81
|
const style = this.lastPathStyle;
|
82
|
-
|
82
|
+
if (style.fill.a > 0) {
|
83
|
+
pathElem.setAttribute('fill', style.fill.toHexString());
|
84
|
+
}
|
85
|
+
else {
|
86
|
+
pathElem.setAttribute('fill', 'none');
|
87
|
+
}
|
83
88
|
if (style.stroke) {
|
84
89
|
pathElem.setAttribute('stroke', style.stroke.color.toHexString());
|
85
90
|
pathElem.setAttribute('stroke-width', style.stroke.width.toString());
|
@@ -287,7 +287,7 @@ export default class Selection {
|
|
287
287
|
}
|
288
288
|
setSelectedObjects(objects, bbox) {
|
289
289
|
this.originalRegion = bbox;
|
290
|
-
this.selectedElems = objects;
|
290
|
+
this.selectedElems = objects.filter(object => object.isSelectable());
|
291
291
|
this.updateUI();
|
292
292
|
}
|
293
293
|
getSelectedObjects() {
|
package/package.json
CHANGED
package/src/Editor.ts
CHANGED
@@ -27,7 +27,7 @@ import EventDispatcher from './EventDispatcher';
|
|
27
27
|
import { Point2, Vec2 } from './math/Vec2';
|
28
28
|
import Vec3 from './math/Vec3';
|
29
29
|
import HTMLToolbar from './toolbar/HTMLToolbar';
|
30
|
-
import { RenderablePathSpec } from './rendering/renderers/AbstractRenderer';
|
30
|
+
import AbstractRenderer, { RenderablePathSpec } from './rendering/renderers/AbstractRenderer';
|
31
31
|
import Display, { RenderingMode } from './rendering/Display';
|
32
32
|
import SVGRenderer from './rendering/renderers/SVGRenderer';
|
33
33
|
import Color4 from './Color4';
|
@@ -39,6 +39,7 @@ import { EditorLocalization } from './localization';
|
|
39
39
|
import getLocalizationTable from './localizations/getLocalizationTable';
|
40
40
|
import IconProvider from './toolbar/IconProvider';
|
41
41
|
import { toRoundedString } from './math/rounding';
|
42
|
+
import CanvasRenderer from './rendering/renderers/CanvasRenderer';
|
42
43
|
|
43
44
|
type HTMLPointerEventType = 'pointerdown'|'pointermove'|'pointerup'|'pointercancel';
|
44
45
|
type HTMLPointerEventFilter = (eventName: HTMLPointerEventType, event: PointerEvent)=>boolean;
|
@@ -823,20 +824,36 @@ export class Editor {
|
|
823
824
|
});
|
824
825
|
}
|
825
826
|
|
827
|
+
// Get a data URL (e.g. as produced by `HTMLCanvasElement::toDataURL`).
|
828
|
+
// If `format` is not `image/png`, a PNG image URL may still be returned (as in the
|
829
|
+
// case of `HTMLCanvasElement::toDataURL`).
|
830
|
+
//
|
831
|
+
// The export resolution is the same as the size of the drawing canvas.
|
832
|
+
public toDataURL(format: 'image/png'|'image/jpeg'|'image/webp' = 'image/png'): string {
|
833
|
+
const canvas = document.createElement('canvas');
|
834
|
+
|
835
|
+
const resolution = this.importExportViewport.getResolution();
|
836
|
+
|
837
|
+
canvas.width = resolution.x;
|
838
|
+
canvas.height = resolution.y;
|
839
|
+
|
840
|
+
const ctx = canvas.getContext('2d')!;
|
841
|
+
const renderer = new CanvasRenderer(ctx, this.importExportViewport);
|
842
|
+
|
843
|
+
// Render everything with no transform (0,0) should be (0,0) in the output image
|
844
|
+
this.renderAllWithTransform(renderer, this.importExportViewport, Mat33.identity);
|
845
|
+
|
846
|
+
const dataURL = canvas.toDataURL(format);
|
847
|
+
return dataURL;
|
848
|
+
}
|
849
|
+
|
826
850
|
public toSVG(): SVGElement {
|
827
851
|
const importExportViewport = this.importExportViewport;
|
828
852
|
const svgNameSpace = 'http://www.w3.org/2000/svg';
|
829
853
|
const result = document.createElementNS(svgNameSpace, 'svg');
|
830
854
|
const renderer = new SVGRenderer(result, importExportViewport);
|
831
855
|
|
832
|
-
|
833
|
-
// Reset the transform to ensure that (0, 0) is (0, 0)
|
834
|
-
importExportViewport.resetTransform(Mat33.identity);
|
835
|
-
|
836
|
-
// Render **all** elements.
|
837
|
-
this.image.renderAll(renderer);
|
838
|
-
|
839
|
-
importExportViewport.resetTransform(origTransform);
|
856
|
+
this.renderAllWithTransform(renderer, importExportViewport);
|
840
857
|
|
841
858
|
// Just show the main region
|
842
859
|
const rect = importExportViewport.visibleRect;
|
@@ -854,6 +871,23 @@ export class Editor {
|
|
854
871
|
return result;
|
855
872
|
}
|
856
873
|
|
874
|
+
// Renders everything in this' image to `renderer`, but first transforming the given `viewport`
|
875
|
+
// such that its transform is `transform`. The given `viewport`'s transform is restored before this method
|
876
|
+
// returns.
|
877
|
+
//
|
878
|
+
// For example, rendering with `transform = Mat33.identity` *sets* `viewport`'s transform to `Mat33.identity`,
|
879
|
+
// renders everything in this' image to `renderer`, then restores `viewport`'s transform to whatever it was before.
|
880
|
+
private renderAllWithTransform(
|
881
|
+
renderer: AbstractRenderer, viewport: Viewport, transform: Mat33 = Mat33.identity
|
882
|
+
): void {
|
883
|
+
const origTransform = this.importExportViewport.canvasToScreenTransform;
|
884
|
+
viewport.resetTransform(transform);
|
885
|
+
|
886
|
+
this.image.renderAll(renderer);
|
887
|
+
|
888
|
+
viewport.resetTransform(origTransform);
|
889
|
+
}
|
890
|
+
|
857
891
|
public async loadFrom(loader: ImageLoader) {
|
858
892
|
this.showLoadingWarning(0);
|
859
893
|
this.display.setDraftMode(true);
|
package/src/Viewport.ts
CHANGED
@@ -190,8 +190,8 @@ export class Viewport {
|
|
190
190
|
|
191
191
|
// Represent as k 10ⁿ for some n, k ∈ ℤ.
|
192
192
|
const decimalComponent = 10 ** Math.floor(Math.log10(Math.abs(scaleRatio)));
|
193
|
-
const
|
194
|
-
scaleRatio = Math.round(scaleRatio / decimalComponent *
|
193
|
+
const roundAmountFactor = 2 ** roundAmount;
|
194
|
+
scaleRatio = Math.round(scaleRatio / decimalComponent * roundAmountFactor) / roundAmountFactor * decimalComponent;
|
195
195
|
|
196
196
|
return scaleRatio;
|
197
197
|
}
|
@@ -51,6 +51,10 @@ export default class LineBuilder implements ComponentBuilder {
|
|
51
51
|
kind: PathCommandType.LineTo,
|
52
52
|
point: endPoint.minus(scaledEndNormal),
|
53
53
|
},
|
54
|
+
{
|
55
|
+
kind: PathCommandType.LineTo,
|
56
|
+
point: startPoint.minus(scaledStartNormal),
|
57
|
+
},
|
54
58
|
],
|
55
59
|
style: {
|
56
60
|
fill: this.startPoint.color,
|
@@ -238,7 +238,7 @@ export class StrokeSmoother {
|
|
238
238
|
if (!controlPoint || segmentStart.eq(controlPoint) || segmentEnd.eq(controlPoint)) {
|
239
239
|
// Position the control point closer to the first -- the connecting
|
240
240
|
// segment will be roughly a line.
|
241
|
-
controlPoint = segmentStart.plus(enteringVec.times(startEndDist /
|
241
|
+
controlPoint = segmentStart.plus(enteringVec.times(startEndDist / 4));
|
242
242
|
}
|
243
243
|
|
244
244
|
console.assert(!segmentStart.eq(controlPoint, 1e-11), 'Start and control points are equal!');
|
@@ -94,7 +94,11 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
94
94
|
pathElem.setAttribute('d', this.lastPathString.join(' '));
|
95
95
|
|
96
96
|
const style = this.lastPathStyle;
|
97
|
-
|
97
|
+
if (style.fill.a > 0) {
|
98
|
+
pathElem.setAttribute('fill', style.fill.toHexString());
|
99
|
+
} else {
|
100
|
+
pathElem.setAttribute('fill', 'none');
|
101
|
+
}
|
98
102
|
|
99
103
|
if (style.stroke) {
|
100
104
|
pathElem.setAttribute('stroke', style.stroke.color.toHexString());
|
@@ -453,7 +453,7 @@ export default class Selection {
|
|
453
453
|
|
454
454
|
public setSelectedObjects(objects: AbstractComponent[], bbox: Rect2) {
|
455
455
|
this.originalRegion = bbox;
|
456
|
-
this.selectedElems = objects;
|
456
|
+
this.selectedElems = objects.filter(object => object.isSelectable());
|
457
457
|
this.updateUI();
|
458
458
|
}
|
459
459
|
|