@tsdraw/core 0.7.0 → 0.8.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/dist/index.cjs +72 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +72 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -225,25 +225,34 @@ var StateNode = class {
|
|
|
225
225
|
|
|
226
226
|
// src/canvas/viewport.ts
|
|
227
227
|
function createViewport() {
|
|
228
|
-
return { x: 0, y: 0, zoom: 1 };
|
|
228
|
+
return { x: 0, y: 0, zoom: 1, rotation: 0 };
|
|
229
229
|
}
|
|
230
230
|
function screenToPage(viewport, screenX, screenY) {
|
|
231
|
+
const tx = screenX - viewport.x;
|
|
232
|
+
const ty = screenY - viewport.y;
|
|
233
|
+
const cos = Math.cos(viewport.rotation);
|
|
234
|
+
const sin = Math.sin(viewport.rotation);
|
|
231
235
|
return {
|
|
232
|
-
x: (
|
|
233
|
-
y: (
|
|
236
|
+
x: (tx * cos + ty * sin) / viewport.zoom,
|
|
237
|
+
y: (-tx * sin + ty * cos) / viewport.zoom
|
|
234
238
|
};
|
|
235
239
|
}
|
|
236
240
|
function pageToScreen(viewport, pageX, pageY) {
|
|
241
|
+
const scaledX = pageX * viewport.zoom;
|
|
242
|
+
const scaledY = pageY * viewport.zoom;
|
|
243
|
+
const cos = Math.cos(viewport.rotation);
|
|
244
|
+
const sin = Math.sin(viewport.rotation);
|
|
237
245
|
return {
|
|
238
|
-
x:
|
|
239
|
-
y:
|
|
246
|
+
x: scaledX * cos - scaledY * sin + viewport.x,
|
|
247
|
+
y: scaledX * sin + scaledY * cos + viewport.y
|
|
240
248
|
};
|
|
241
249
|
}
|
|
242
250
|
function setViewport(viewport, updater) {
|
|
243
251
|
return {
|
|
244
252
|
x: updater.x ?? viewport.x,
|
|
245
253
|
y: updater.y ?? viewport.y,
|
|
246
|
-
zoom: updater.zoom ?? viewport.zoom
|
|
254
|
+
zoom: updater.zoom ?? viewport.zoom,
|
|
255
|
+
rotation: updater.rotation ?? viewport.rotation
|
|
247
256
|
};
|
|
248
257
|
}
|
|
249
258
|
function panViewport(viewport, dx, dy) {
|
|
@@ -255,9 +264,23 @@ function zoomViewport(viewport, factor, centerX, centerY) {
|
|
|
255
264
|
return { ...viewport, zoom };
|
|
256
265
|
}
|
|
257
266
|
const pageBefore = screenToPage(viewport, centerX, centerY);
|
|
258
|
-
const
|
|
259
|
-
const
|
|
260
|
-
|
|
267
|
+
const cos = Math.cos(viewport.rotation);
|
|
268
|
+
const sin = Math.sin(viewport.rotation);
|
|
269
|
+
const x = centerX - (pageBefore.x * zoom * cos - pageBefore.y * zoom * sin);
|
|
270
|
+
const y = centerY - (pageBefore.x * zoom * sin + pageBefore.y * zoom * cos);
|
|
271
|
+
return { x, y, zoom, rotation: viewport.rotation };
|
|
272
|
+
}
|
|
273
|
+
function rotateViewport(viewport, delta, centerX, centerY) {
|
|
274
|
+
const rotation = viewport.rotation + delta;
|
|
275
|
+
if (centerX == null || centerY == null) {
|
|
276
|
+
return { ...viewport, rotation };
|
|
277
|
+
}
|
|
278
|
+
const pageBefore = screenToPage(viewport, centerX, centerY);
|
|
279
|
+
const cos = Math.cos(rotation);
|
|
280
|
+
const sin = Math.sin(rotation);
|
|
281
|
+
const x = centerX - (pageBefore.x * viewport.zoom * cos - pageBefore.y * viewport.zoom * sin);
|
|
282
|
+
const y = centerY - (pageBefore.x * viewport.zoom * sin + pageBefore.y * viewport.zoom * cos);
|
|
283
|
+
return { x, y, zoom: viewport.zoom, rotation };
|
|
261
284
|
}
|
|
262
285
|
|
|
263
286
|
// src/utils/colors.ts
|
|
@@ -290,6 +313,7 @@ var CanvasRenderer = class {
|
|
|
290
313
|
render(ctx, viewport, shapes) {
|
|
291
314
|
ctx.save();
|
|
292
315
|
ctx.translate(viewport.x, viewport.y);
|
|
316
|
+
ctx.rotate(viewport.rotation);
|
|
293
317
|
ctx.scale(viewport.zoom, viewport.zoom);
|
|
294
318
|
for (const shape of shapes) {
|
|
295
319
|
if (shape.type === "draw") {
|
|
@@ -450,9 +474,6 @@ function flattenSegments(shape) {
|
|
|
450
474
|
}
|
|
451
475
|
out.push(D);
|
|
452
476
|
}
|
|
453
|
-
if (out.length > 0 && !shape.props.isPen) {
|
|
454
|
-
for (const p of out) p.pressure = 0.5;
|
|
455
|
-
}
|
|
456
477
|
return out;
|
|
457
478
|
}
|
|
458
479
|
function getLineDash(dash, width) {
|
|
@@ -582,6 +603,7 @@ var ToolManager = class {
|
|
|
582
603
|
getCurrentState() {
|
|
583
604
|
return this.currentState;
|
|
584
605
|
}
|
|
606
|
+
// Transition between states within the same tool (ex. pen_idle -> pen_drawing)
|
|
585
607
|
transition(stateId, info) {
|
|
586
608
|
const next = this.states.get(stateId);
|
|
587
609
|
if (!next) return;
|
|
@@ -783,8 +805,8 @@ var PenDrawingState = class extends StateNode {
|
|
|
783
805
|
const penActive = inputs.getIsPen();
|
|
784
806
|
const z = this._startInfo?.point?.z ?? 0.5;
|
|
785
807
|
this._isPenDevice = penActive;
|
|
786
|
-
this._hasPressure = penActive
|
|
787
|
-
const pressure = this._hasPressure ? toFixed(z
|
|
808
|
+
this._hasPressure = penActive || z !== 0.5;
|
|
809
|
+
const pressure = this._hasPressure ? toFixed(z) : 0.5;
|
|
788
810
|
this._phase = inputs.getShiftKey() ? "straight" : "free";
|
|
789
811
|
this._extending = false;
|
|
790
812
|
this._lastSample = { ...origin };
|
|
@@ -879,8 +901,15 @@ var PenDrawingState = class extends StateNode {
|
|
|
879
901
|
const { id, props: { size, scale } } = target;
|
|
880
902
|
const { segments } = shape.props;
|
|
881
903
|
const curPt = inputs.getCurrentPagePoint();
|
|
904
|
+
if (!this._hasPressure) {
|
|
905
|
+
const liveZ = curPt.z ?? 0.5;
|
|
906
|
+
if (liveZ !== 0.5 || inputs.getIsPen()) {
|
|
907
|
+
this._hasPressure = true;
|
|
908
|
+
this.editor.updateShapes([{ id, type: "draw", props: { isPen: true } }]);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
882
911
|
const local = this.editor.getPointInShapeSpace(shape, curPt);
|
|
883
|
-
const pressure = this._hasPressure ? toFixed(
|
|
912
|
+
const pressure = this._hasPressure ? toFixed(curPt.z ?? 0.5) : 0.5;
|
|
884
913
|
const pt = { x: toFixed(local.x), y: toFixed(local.y), z: pressure };
|
|
885
914
|
switch (this._phase) {
|
|
886
915
|
case "starting_straight": {
|
|
@@ -1026,7 +1055,7 @@ var PenDrawingState = class extends StateNode {
|
|
|
1026
1055
|
const firstPt = {
|
|
1027
1056
|
x: 0,
|
|
1028
1057
|
y: 0,
|
|
1029
|
-
z: this._hasPressure ? toFixed(
|
|
1058
|
+
z: this._hasPressure ? toFixed(curPage.z ?? 0.5) : 0.5
|
|
1030
1059
|
};
|
|
1031
1060
|
this._activePts = [firstPt];
|
|
1032
1061
|
this.editor.createShape({
|
|
@@ -1101,6 +1130,7 @@ var GeometricDrawingState = class extends StateNode {
|
|
|
1101
1130
|
});
|
|
1102
1131
|
this.currentShapeId = nextShapeId;
|
|
1103
1132
|
}
|
|
1133
|
+
// Shift key switches between constrained and unconstrained bounds
|
|
1104
1134
|
onPointerMove() {
|
|
1105
1135
|
const activeShape = this.getActiveShape();
|
|
1106
1136
|
if (!activeShape) return;
|
|
@@ -1134,6 +1164,8 @@ var GeometricDrawingState = class extends StateNode {
|
|
|
1134
1164
|
onKeyUp() {
|
|
1135
1165
|
this.onPointerMove();
|
|
1136
1166
|
}
|
|
1167
|
+
// If user dragged, use the drag extents for the final shape
|
|
1168
|
+
// If they just clicked without dragging, use default-sized shape
|
|
1137
1169
|
completeShape() {
|
|
1138
1170
|
const activeShape = this.getActiveShape();
|
|
1139
1171
|
const config = this.getConfig();
|
|
@@ -1454,6 +1486,8 @@ var EraserErasingState = class extends StateNode {
|
|
|
1454
1486
|
onCancel() {
|
|
1455
1487
|
this.ctx.transition("eraser_idle");
|
|
1456
1488
|
}
|
|
1489
|
+
// On every pointer move, test the line from previous pointer position to current one against nearby shapes
|
|
1490
|
+
// Only select shapes whose bounding box overlaps the sweep area to avoid testing all shapes
|
|
1457
1491
|
sweep() {
|
|
1458
1492
|
const zoom = this.editor.getZoomLevel();
|
|
1459
1493
|
const tolerance = ERASER_MARGIN / zoom;
|
|
@@ -1472,6 +1506,7 @@ var EraserErasingState = class extends StateNode {
|
|
|
1472
1506
|
this._marked = [...hitIds];
|
|
1473
1507
|
this.editor.setErasingShapes(this._marked);
|
|
1474
1508
|
}
|
|
1509
|
+
// Delete marked shapes and reset, then go back to idle
|
|
1475
1510
|
finish() {
|
|
1476
1511
|
const ids = this.editor.getErasingShapeIds();
|
|
1477
1512
|
if (ids.length > 0) {
|
|
@@ -1736,7 +1771,8 @@ var Editor = class {
|
|
|
1736
1771
|
this.viewport = {
|
|
1737
1772
|
x: partial.x ?? this.viewport.x,
|
|
1738
1773
|
y: partial.y ?? this.viewport.y,
|
|
1739
|
-
zoom: Math.max(0.1, Math.min(4, rawZoom))
|
|
1774
|
+
zoom: Math.max(0.1, Math.min(4, rawZoom)),
|
|
1775
|
+
rotation: partial.rotation ?? this.viewport.rotation
|
|
1740
1776
|
};
|
|
1741
1777
|
this.emitChange();
|
|
1742
1778
|
}
|
|
@@ -1746,6 +1782,18 @@ var Editor = class {
|
|
|
1746
1782
|
y: this.viewport.y + dy
|
|
1747
1783
|
});
|
|
1748
1784
|
}
|
|
1785
|
+
zoomAt(factor, screenX, screenY) {
|
|
1786
|
+
this.viewport = zoomViewport(this.viewport, factor, screenX, screenY);
|
|
1787
|
+
this.emitChange();
|
|
1788
|
+
}
|
|
1789
|
+
rotateAt(delta, screenX, screenY) {
|
|
1790
|
+
this.viewport = rotateViewport(this.viewport, delta, screenX, screenY);
|
|
1791
|
+
this.emitChange();
|
|
1792
|
+
}
|
|
1793
|
+
deleteShapes(ids) {
|
|
1794
|
+
if (ids.length === 0) return;
|
|
1795
|
+
this.store.deleteShapes(ids);
|
|
1796
|
+
}
|
|
1749
1797
|
getDocumentSnapshot() {
|
|
1750
1798
|
return {
|
|
1751
1799
|
records: documentSnapshotToRecords(this.store.getSnapshot())
|
|
@@ -1764,7 +1812,8 @@ var Editor = class {
|
|
|
1764
1812
|
viewport: {
|
|
1765
1813
|
x: this.viewport.x,
|
|
1766
1814
|
y: this.viewport.y,
|
|
1767
|
-
zoom: this.viewport.zoom
|
|
1815
|
+
zoom: this.viewport.zoom,
|
|
1816
|
+
rotation: this.viewport.rotation
|
|
1768
1817
|
},
|
|
1769
1818
|
currentToolId: this.getCurrentToolId(),
|
|
1770
1819
|
drawStyle: this.getCurrentDrawStyle(),
|
|
@@ -1772,7 +1821,10 @@ var Editor = class {
|
|
|
1772
1821
|
};
|
|
1773
1822
|
}
|
|
1774
1823
|
loadSessionStateSnapshot(snapshot) {
|
|
1775
|
-
this.setViewport(
|
|
1824
|
+
this.setViewport({
|
|
1825
|
+
...snapshot.viewport,
|
|
1826
|
+
rotation: snapshot.viewport.rotation ?? 0
|
|
1827
|
+
});
|
|
1776
1828
|
this.setCurrentDrawStyle({
|
|
1777
1829
|
color: snapshot.drawStyle.color,
|
|
1778
1830
|
dash: snapshot.drawStyle.dash,
|
|
@@ -2213,6 +2265,7 @@ exports.pointHitsShape = pointHitsShape;
|
|
|
2213
2265
|
exports.recordsToDocumentSnapshot = recordsToDocumentSnapshot;
|
|
2214
2266
|
exports.resolveThemeColor = resolveThemeColor;
|
|
2215
2267
|
exports.rotatePoint = rotatePoint;
|
|
2268
|
+
exports.rotateViewport = rotateViewport;
|
|
2216
2269
|
exports.screenToPage = screenToPage;
|
|
2217
2270
|
exports.segmentHitsShape = segmentHitsShape;
|
|
2218
2271
|
exports.segmentTouchesPolyline = segmentTouchesPolyline;
|