@tsdraw/core 0.6.2 → 0.7.0

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.d.cts CHANGED
@@ -11,6 +11,7 @@ interface DrawSegment {
11
11
  }
12
12
  type SizeStyle = 's' | 'm' | 'l' | 'xl';
13
13
  type DashStyle = 'draw' | 'solid' | 'dashed' | 'dotted';
14
+ type FillStyle = 'none' | 'semi' | 'solid' | 'blank';
14
15
  type ColorStyle = string;
15
16
  interface DrawShape {
16
17
  id: ShapeId;
@@ -20,6 +21,7 @@ interface DrawShape {
20
21
  props: {
21
22
  color: ColorStyle;
22
23
  dash: DashStyle;
24
+ fill?: FillStyle;
23
25
  size: SizeStyle;
24
26
  scale: number;
25
27
  isPen: boolean;
@@ -66,6 +68,7 @@ interface TsdrawSessionStateSnapshot {
66
68
  drawStyle: {
67
69
  color: ColorStyle;
68
70
  dash: DashStyle;
71
+ fill?: FillStyle;
69
72
  size: SizeStyle;
70
73
  };
71
74
  selectedShapeIds: ShapeId[];
@@ -168,11 +171,13 @@ interface IEditor {
168
171
  getCurrentDrawStyle(): {
169
172
  color: ColorStyle;
170
173
  dash: DashStyle;
174
+ fill: FillStyle;
171
175
  size: SizeStyle;
172
176
  };
173
177
  setCurrentDrawStyle(partial: Partial<{
174
178
  color: ColorStyle;
175
179
  dash: DashStyle;
180
+ fill: FillStyle;
176
181
  size: SizeStyle;
177
182
  }>): void;
178
183
  panBy(dx: number, dy: number): void;
@@ -246,9 +251,10 @@ declare class CanvasRenderer implements ICanvasRenderer {
246
251
  render(ctx: CanvasRenderingContext2D, viewport: Viewport, shapes: Shape[]): void;
247
252
  private paintStroke;
248
253
  private paintDashedStroke;
254
+ private paintClosedShapeFill;
249
255
  }
250
256
 
251
- type DefaultToolId = 'pen' | 'eraser' | 'select' | 'hand';
257
+ type DefaultToolId = 'pen' | 'eraser' | 'select' | 'hand' | 'square' | 'circle';
252
258
  type ToolId = DefaultToolId | (string & {});
253
259
  interface ToolDefinition {
254
260
  id: ToolId;
@@ -326,11 +332,13 @@ declare class Editor {
326
332
  getCurrentDrawStyle(): {
327
333
  color: ColorStyle;
328
334
  dash: DashStyle;
335
+ fill: FillStyle;
329
336
  size: SizeStyle;
330
337
  };
331
338
  setCurrentDrawStyle(partial: Partial<{
332
339
  color: ColorStyle;
333
340
  dash: DashStyle;
341
+ fill: FillStyle;
334
342
  size: SizeStyle;
335
343
  }>): void;
336
344
  setViewport(partial: Partial<Viewport>): void;
@@ -401,6 +409,76 @@ declare class PenDrawingState extends StateNode {
401
409
  private endStroke;
402
410
  }
403
411
 
412
+ declare class SquareIdleState extends StateNode {
413
+ static id: string;
414
+ onPointerDown(info?: ToolPointerDownInfo): void;
415
+ }
416
+
417
+ interface ShapeBounds {
418
+ x: number;
419
+ y: number;
420
+ width: number;
421
+ height: number;
422
+ }
423
+ declare function buildSquareBounds(anchorX: number, anchorY: number, cursorX: number, cursorY: number): ShapeBounds;
424
+ declare function buildRectangleBounds(anchorX: number, anchorY: number, cursorX: number, cursorY: number): ShapeBounds;
425
+ declare function buildDefaultCenteredRectangleBounds(centerX: number, centerY: number): ShapeBounds;
426
+ declare function buildRectangleSegments(width: number, height: number): DrawSegment[];
427
+ declare function buildCircleBounds(anchorX: number, anchorY: number, cursorX: number, cursorY: number): ShapeBounds;
428
+ declare function buildEllipseBounds(anchorX: number, anchorY: number, cursorX: number, cursorY: number): ShapeBounds;
429
+ declare function buildDefaultCenteredEllipseBounds(centerX: number, centerY: number): ShapeBounds;
430
+ declare function buildEllipseSegments(width: number, height: number): DrawSegment[];
431
+
432
+ interface GeometricDrawingStateConfig {
433
+ idleStateId: string;
434
+ buildConstrainedBounds: (anchorX: number, anchorY: number, cursorX: number, cursorY: number) => ShapeBounds;
435
+ buildUnconstrainedBounds: (anchorX: number, anchorY: number, cursorX: number, cursorY: number) => ShapeBounds;
436
+ buildDefaultBounds: (centerX: number, centerY: number) => ShapeBounds;
437
+ buildSegments: (width: number, height: number) => DrawSegment[];
438
+ }
439
+ declare abstract class GeometricDrawingState extends StateNode {
440
+ private currentShapeId;
441
+ private startedAt;
442
+ protected abstract getConfig(): GeometricDrawingStateConfig;
443
+ onEnter(info?: ToolPointerDownInfo): void;
444
+ onPointerMove(): void;
445
+ onPointerUp(): void;
446
+ onCancel(): void;
447
+ onInterrupt(): void;
448
+ onKeyDown(): void;
449
+ onKeyUp(): void;
450
+ private completeShape;
451
+ private removeCurrentShape;
452
+ private getActiveShape;
453
+ }
454
+
455
+ declare class SquareDrawingState extends GeometricDrawingState {
456
+ static id: string;
457
+ protected getConfig(): {
458
+ idleStateId: string;
459
+ buildConstrainedBounds: typeof buildSquareBounds;
460
+ buildUnconstrainedBounds: typeof buildRectangleBounds;
461
+ buildDefaultBounds: typeof buildDefaultCenteredRectangleBounds;
462
+ buildSegments: typeof buildRectangleSegments;
463
+ };
464
+ }
465
+
466
+ declare class CircleIdleState extends StateNode {
467
+ static id: string;
468
+ onPointerDown(info?: ToolPointerDownInfo): void;
469
+ }
470
+
471
+ declare class CircleDrawingState extends GeometricDrawingState {
472
+ static id: string;
473
+ protected getConfig(): {
474
+ idleStateId: string;
475
+ buildConstrainedBounds: typeof buildCircleBounds;
476
+ buildUnconstrainedBounds: typeof buildEllipseBounds;
477
+ buildDefaultBounds: typeof buildDefaultCenteredEllipseBounds;
478
+ buildSegments: typeof buildEllipseSegments;
479
+ };
480
+ }
481
+
404
482
  declare class EraserIdleState extends StateNode {
405
483
  static id: string;
406
484
  onPointerDown(info?: ToolPointerDownInfo): void;
@@ -550,4 +628,4 @@ declare function decodePathToPoints(segments: {
550
628
  y: number;
551
629
  }[];
552
630
 
553
- export { type Bounds, CanvasRenderer, type ColorStyle, DEFAULT_COLORS, DRAG_DISTANCE_SQUARED, type DashStyle, type DefaultToolId, DocumentStore, type DocumentStoreSnapshot, type DrawSegment, type DrawShape, ERASER_MARGIN, Editor, type EditorOptions, EraserErasingState, EraserIdleState, EraserPointingState, HandDraggingState, HandIdleState, type ICanvasRenderer, type IEditor, InputManager, MAX_POINTS_PER_SHAPE, type PageState, PenDrawingState, PenIdleState, type PointerInput, type ResizeHandle, STROKE_WIDTHS, type SegmentType, SelectIdleState, type SelectionBounds, type Shape, type ShapeId, type SizeStyle, StateNode, type StateNodeConstructor, type ToolDefinition, type ToolId, type ToolKeyInfo, ToolManager, type ToolPointerDownInfo, type ToolPointerMoveInfo, type ToolStateContext, type ToolStateTransitionInfo, type TransformSnapshot, type TsdrawDocumentSnapshot, type TsdrawEditorSnapshot, type TsdrawHistorySnapshot, type TsdrawPageRecord, type TsdrawPersistedRecord, type TsdrawRenderTheme, type TsdrawSessionStateSnapshot, type TsdrawShapeRecord, type Vec3, type Viewport, applyMove, applyResize, applyRotation, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, resolveThemeColor, rotatePoint, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, zoomViewport };
631
+ export { type Bounds, CanvasRenderer, CircleDrawingState, CircleIdleState, type ColorStyle, DEFAULT_COLORS, DRAG_DISTANCE_SQUARED, type DashStyle, type DefaultToolId, DocumentStore, type DocumentStoreSnapshot, type DrawSegment, type DrawShape, ERASER_MARGIN, Editor, type EditorOptions, EraserErasingState, EraserIdleState, EraserPointingState, type FillStyle, HandDraggingState, HandIdleState, type ICanvasRenderer, type IEditor, InputManager, MAX_POINTS_PER_SHAPE, type PageState, PenDrawingState, PenIdleState, type PointerInput, type ResizeHandle, STROKE_WIDTHS, type SegmentType, SelectIdleState, type SelectionBounds, type Shape, type ShapeId, type SizeStyle, SquareDrawingState, SquareIdleState, StateNode, type StateNodeConstructor, type ToolDefinition, type ToolId, type ToolKeyInfo, ToolManager, type ToolPointerDownInfo, type ToolPointerMoveInfo, type ToolStateContext, type ToolStateTransitionInfo, type TransformSnapshot, type TsdrawDocumentSnapshot, type TsdrawEditorSnapshot, type TsdrawHistorySnapshot, type TsdrawPageRecord, type TsdrawPersistedRecord, type TsdrawRenderTheme, type TsdrawSessionStateSnapshot, type TsdrawShapeRecord, type Vec3, type Viewport, applyMove, applyResize, applyRotation, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, resolveThemeColor, rotatePoint, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, zoomViewport };
package/dist/index.d.ts CHANGED
@@ -11,6 +11,7 @@ interface DrawSegment {
11
11
  }
12
12
  type SizeStyle = 's' | 'm' | 'l' | 'xl';
13
13
  type DashStyle = 'draw' | 'solid' | 'dashed' | 'dotted';
14
+ type FillStyle = 'none' | 'semi' | 'solid' | 'blank';
14
15
  type ColorStyle = string;
15
16
  interface DrawShape {
16
17
  id: ShapeId;
@@ -20,6 +21,7 @@ interface DrawShape {
20
21
  props: {
21
22
  color: ColorStyle;
22
23
  dash: DashStyle;
24
+ fill?: FillStyle;
23
25
  size: SizeStyle;
24
26
  scale: number;
25
27
  isPen: boolean;
@@ -66,6 +68,7 @@ interface TsdrawSessionStateSnapshot {
66
68
  drawStyle: {
67
69
  color: ColorStyle;
68
70
  dash: DashStyle;
71
+ fill?: FillStyle;
69
72
  size: SizeStyle;
70
73
  };
71
74
  selectedShapeIds: ShapeId[];
@@ -168,11 +171,13 @@ interface IEditor {
168
171
  getCurrentDrawStyle(): {
169
172
  color: ColorStyle;
170
173
  dash: DashStyle;
174
+ fill: FillStyle;
171
175
  size: SizeStyle;
172
176
  };
173
177
  setCurrentDrawStyle(partial: Partial<{
174
178
  color: ColorStyle;
175
179
  dash: DashStyle;
180
+ fill: FillStyle;
176
181
  size: SizeStyle;
177
182
  }>): void;
178
183
  panBy(dx: number, dy: number): void;
@@ -246,9 +251,10 @@ declare class CanvasRenderer implements ICanvasRenderer {
246
251
  render(ctx: CanvasRenderingContext2D, viewport: Viewport, shapes: Shape[]): void;
247
252
  private paintStroke;
248
253
  private paintDashedStroke;
254
+ private paintClosedShapeFill;
249
255
  }
250
256
 
251
- type DefaultToolId = 'pen' | 'eraser' | 'select' | 'hand';
257
+ type DefaultToolId = 'pen' | 'eraser' | 'select' | 'hand' | 'square' | 'circle';
252
258
  type ToolId = DefaultToolId | (string & {});
253
259
  interface ToolDefinition {
254
260
  id: ToolId;
@@ -326,11 +332,13 @@ declare class Editor {
326
332
  getCurrentDrawStyle(): {
327
333
  color: ColorStyle;
328
334
  dash: DashStyle;
335
+ fill: FillStyle;
329
336
  size: SizeStyle;
330
337
  };
331
338
  setCurrentDrawStyle(partial: Partial<{
332
339
  color: ColorStyle;
333
340
  dash: DashStyle;
341
+ fill: FillStyle;
334
342
  size: SizeStyle;
335
343
  }>): void;
336
344
  setViewport(partial: Partial<Viewport>): void;
@@ -401,6 +409,76 @@ declare class PenDrawingState extends StateNode {
401
409
  private endStroke;
402
410
  }
403
411
 
412
+ declare class SquareIdleState extends StateNode {
413
+ static id: string;
414
+ onPointerDown(info?: ToolPointerDownInfo): void;
415
+ }
416
+
417
+ interface ShapeBounds {
418
+ x: number;
419
+ y: number;
420
+ width: number;
421
+ height: number;
422
+ }
423
+ declare function buildSquareBounds(anchorX: number, anchorY: number, cursorX: number, cursorY: number): ShapeBounds;
424
+ declare function buildRectangleBounds(anchorX: number, anchorY: number, cursorX: number, cursorY: number): ShapeBounds;
425
+ declare function buildDefaultCenteredRectangleBounds(centerX: number, centerY: number): ShapeBounds;
426
+ declare function buildRectangleSegments(width: number, height: number): DrawSegment[];
427
+ declare function buildCircleBounds(anchorX: number, anchorY: number, cursorX: number, cursorY: number): ShapeBounds;
428
+ declare function buildEllipseBounds(anchorX: number, anchorY: number, cursorX: number, cursorY: number): ShapeBounds;
429
+ declare function buildDefaultCenteredEllipseBounds(centerX: number, centerY: number): ShapeBounds;
430
+ declare function buildEllipseSegments(width: number, height: number): DrawSegment[];
431
+
432
+ interface GeometricDrawingStateConfig {
433
+ idleStateId: string;
434
+ buildConstrainedBounds: (anchorX: number, anchorY: number, cursorX: number, cursorY: number) => ShapeBounds;
435
+ buildUnconstrainedBounds: (anchorX: number, anchorY: number, cursorX: number, cursorY: number) => ShapeBounds;
436
+ buildDefaultBounds: (centerX: number, centerY: number) => ShapeBounds;
437
+ buildSegments: (width: number, height: number) => DrawSegment[];
438
+ }
439
+ declare abstract class GeometricDrawingState extends StateNode {
440
+ private currentShapeId;
441
+ private startedAt;
442
+ protected abstract getConfig(): GeometricDrawingStateConfig;
443
+ onEnter(info?: ToolPointerDownInfo): void;
444
+ onPointerMove(): void;
445
+ onPointerUp(): void;
446
+ onCancel(): void;
447
+ onInterrupt(): void;
448
+ onKeyDown(): void;
449
+ onKeyUp(): void;
450
+ private completeShape;
451
+ private removeCurrentShape;
452
+ private getActiveShape;
453
+ }
454
+
455
+ declare class SquareDrawingState extends GeometricDrawingState {
456
+ static id: string;
457
+ protected getConfig(): {
458
+ idleStateId: string;
459
+ buildConstrainedBounds: typeof buildSquareBounds;
460
+ buildUnconstrainedBounds: typeof buildRectangleBounds;
461
+ buildDefaultBounds: typeof buildDefaultCenteredRectangleBounds;
462
+ buildSegments: typeof buildRectangleSegments;
463
+ };
464
+ }
465
+
466
+ declare class CircleIdleState extends StateNode {
467
+ static id: string;
468
+ onPointerDown(info?: ToolPointerDownInfo): void;
469
+ }
470
+
471
+ declare class CircleDrawingState extends GeometricDrawingState {
472
+ static id: string;
473
+ protected getConfig(): {
474
+ idleStateId: string;
475
+ buildConstrainedBounds: typeof buildCircleBounds;
476
+ buildUnconstrainedBounds: typeof buildEllipseBounds;
477
+ buildDefaultBounds: typeof buildDefaultCenteredEllipseBounds;
478
+ buildSegments: typeof buildEllipseSegments;
479
+ };
480
+ }
481
+
404
482
  declare class EraserIdleState extends StateNode {
405
483
  static id: string;
406
484
  onPointerDown(info?: ToolPointerDownInfo): void;
@@ -550,4 +628,4 @@ declare function decodePathToPoints(segments: {
550
628
  y: number;
551
629
  }[];
552
630
 
553
- export { type Bounds, CanvasRenderer, type ColorStyle, DEFAULT_COLORS, DRAG_DISTANCE_SQUARED, type DashStyle, type DefaultToolId, DocumentStore, type DocumentStoreSnapshot, type DrawSegment, type DrawShape, ERASER_MARGIN, Editor, type EditorOptions, EraserErasingState, EraserIdleState, EraserPointingState, HandDraggingState, HandIdleState, type ICanvasRenderer, type IEditor, InputManager, MAX_POINTS_PER_SHAPE, type PageState, PenDrawingState, PenIdleState, type PointerInput, type ResizeHandle, STROKE_WIDTHS, type SegmentType, SelectIdleState, type SelectionBounds, type Shape, type ShapeId, type SizeStyle, StateNode, type StateNodeConstructor, type ToolDefinition, type ToolId, type ToolKeyInfo, ToolManager, type ToolPointerDownInfo, type ToolPointerMoveInfo, type ToolStateContext, type ToolStateTransitionInfo, type TransformSnapshot, type TsdrawDocumentSnapshot, type TsdrawEditorSnapshot, type TsdrawHistorySnapshot, type TsdrawPageRecord, type TsdrawPersistedRecord, type TsdrawRenderTheme, type TsdrawSessionStateSnapshot, type TsdrawShapeRecord, type Vec3, type Viewport, applyMove, applyResize, applyRotation, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, resolveThemeColor, rotatePoint, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, zoomViewport };
631
+ export { type Bounds, CanvasRenderer, CircleDrawingState, CircleIdleState, type ColorStyle, DEFAULT_COLORS, DRAG_DISTANCE_SQUARED, type DashStyle, type DefaultToolId, DocumentStore, type DocumentStoreSnapshot, type DrawSegment, type DrawShape, ERASER_MARGIN, Editor, type EditorOptions, EraserErasingState, EraserIdleState, EraserPointingState, type FillStyle, HandDraggingState, HandIdleState, type ICanvasRenderer, type IEditor, InputManager, MAX_POINTS_PER_SHAPE, type PageState, PenDrawingState, PenIdleState, type PointerInput, type ResizeHandle, STROKE_WIDTHS, type SegmentType, SelectIdleState, type SelectionBounds, type Shape, type ShapeId, type SizeStyle, SquareDrawingState, SquareIdleState, StateNode, type StateNodeConstructor, type ToolDefinition, type ToolId, type ToolKeyInfo, ToolManager, type ToolPointerDownInfo, type ToolPointerMoveInfo, type ToolStateContext, type ToolStateTransitionInfo, type TransformSnapshot, type TsdrawDocumentSnapshot, type TsdrawEditorSnapshot, type TsdrawHistorySnapshot, type TsdrawPageRecord, type TsdrawPersistedRecord, type TsdrawRenderTheme, type TsdrawSessionStateSnapshot, type TsdrawShapeRecord, type Vec3, type Viewport, applyMove, applyResize, applyRotation, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, resolveThemeColor, rotatePoint, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, zoomViewport };
package/dist/index.js CHANGED
@@ -296,11 +296,16 @@ var CanvasRenderer = class {
296
296
  }
297
297
  ctx.restore();
298
298
  }
299
+ // Paints a single stroke
299
300
  paintStroke(ctx, shape) {
300
301
  const width = (STROKE_WIDTHS[shape.props.size] ?? 3.5) * shape.props.scale;
301
302
  const samples = flattenSegments(shape);
302
303
  if (samples.length === 0) return;
303
304
  const color = resolveThemeColor(shape.props.color, this.theme);
305
+ const fillStyle = shape.props.fill ?? "none";
306
+ if (shape.props.isClosed && fillStyle !== "none") {
307
+ this.paintClosedShapeFill(ctx, samples, color, fillStyle);
308
+ }
304
309
  if (shape.props.dash !== "draw") {
305
310
  this.paintDashedStroke(ctx, samples, width, color, shape.props.dash);
306
311
  return;
@@ -347,6 +352,30 @@ var CanvasRenderer = class {
347
352
  ctx.stroke();
348
353
  ctx.restore();
349
354
  }
355
+ // Closed shapes are shapes where their start and end point are the same
356
+ paintClosedShapeFill(ctx, samples, color, fillStyle) {
357
+ if (samples.length < 3) return;
358
+ ctx.save();
359
+ ctx.beginPath();
360
+ ctx.moveTo(samples[0].x, samples[0].y);
361
+ for (let i = 1; i < samples.length; i++) {
362
+ const sample = samples[i];
363
+ ctx.lineTo(sample.x, sample.y);
364
+ }
365
+ ctx.closePath();
366
+ if (fillStyle === "solid") {
367
+ ctx.fillStyle = color;
368
+ ctx.globalAlpha = 0.55;
369
+ } else if (fillStyle === "none") {
370
+ ctx.fillStyle = this.theme === "dark" ? "#0f0f0f" : "#fafafa";
371
+ ctx.globalAlpha = 1;
372
+ } else {
373
+ ctx.fillStyle = color;
374
+ ctx.globalAlpha = 0.28;
375
+ }
376
+ ctx.fill();
377
+ ctx.restore();
378
+ }
350
379
  };
351
380
  var PRESSURE_FLOOR = 0.025;
352
381
  var STYLUS_CURVE = (t) => t * 0.65 + Math.sin(t * Math.PI / 2) * 0.35;
@@ -360,7 +389,7 @@ function remap(value, inRange, outRange, clamp = false) {
360
389
  return outLo + (outHi - outLo) * clamped;
361
390
  }
362
391
  function strokeConfig(shape, width) {
363
- const done = shape.props.isComplete;
392
+ const done = shape.props.isComplete || shape.props.isClosed === true;
364
393
  if (shape.props.isPen) {
365
394
  return {
366
395
  size: 1 + width * 1.2,
@@ -1033,6 +1062,236 @@ var PenDrawingState = class extends StateNode {
1033
1062
  }
1034
1063
  };
1035
1064
 
1065
+ // src/tools/square/states/SquareIdleState.ts
1066
+ var SquareIdleState = class extends StateNode {
1067
+ static id = "square_idle";
1068
+ onPointerDown(info) {
1069
+ this.ctx.transition("square_drawing", info);
1070
+ }
1071
+ };
1072
+
1073
+ // src/tools/geometric/states/GeometricDrawingState.ts
1074
+ var GeometricDrawingState = class extends StateNode {
1075
+ currentShapeId = null;
1076
+ startedAt = { point: { x: 0, y: 0, z: 0.5 } };
1077
+ onEnter(info) {
1078
+ this.startedAt = info ?? { point: { x: 0, y: 0, z: 0.5 } };
1079
+ const originPoint = this.editor.input.getOriginPagePoint();
1080
+ const drawStyle = this.editor.getCurrentDrawStyle();
1081
+ const nextShapeId = this.editor.createShapeId();
1082
+ const config = this.getConfig();
1083
+ this.editor.createShape({
1084
+ id: nextShapeId,
1085
+ type: "draw",
1086
+ x: originPoint.x,
1087
+ y: originPoint.y,
1088
+ props: {
1089
+ color: drawStyle.color,
1090
+ dash: drawStyle.dash,
1091
+ fill: drawStyle.fill,
1092
+ size: drawStyle.size,
1093
+ scale: 1,
1094
+ isPen: false,
1095
+ isComplete: false,
1096
+ isClosed: true,
1097
+ segments: config.buildSegments(1, 1)
1098
+ }
1099
+ });
1100
+ this.currentShapeId = nextShapeId;
1101
+ }
1102
+ onPointerMove() {
1103
+ const activeShape = this.getActiveShape();
1104
+ if (!activeShape) return;
1105
+ const config = this.getConfig();
1106
+ const originPoint = this.editor.input.getOriginPagePoint();
1107
+ const cursorPoint = this.editor.input.getCurrentPagePoint();
1108
+ const shapeBounds = this.editor.input.getShiftKey() ? config.buildConstrainedBounds(originPoint.x, originPoint.y, cursorPoint.x, cursorPoint.y) : config.buildUnconstrainedBounds(originPoint.x, originPoint.y, cursorPoint.x, cursorPoint.y);
1109
+ this.editor.store.updateShape(activeShape.id, {
1110
+ x: shapeBounds.x,
1111
+ y: shapeBounds.y,
1112
+ props: {
1113
+ ...activeShape.props,
1114
+ segments: config.buildSegments(shapeBounds.width, shapeBounds.height),
1115
+ isClosed: true
1116
+ }
1117
+ });
1118
+ }
1119
+ onPointerUp() {
1120
+ this.completeShape();
1121
+ }
1122
+ onCancel() {
1123
+ this.removeCurrentShape();
1124
+ this.ctx.transition(this.getConfig().idleStateId, this.startedAt);
1125
+ }
1126
+ onInterrupt() {
1127
+ this.completeShape();
1128
+ }
1129
+ onKeyDown() {
1130
+ this.onPointerMove();
1131
+ }
1132
+ onKeyUp() {
1133
+ this.onPointerMove();
1134
+ }
1135
+ completeShape() {
1136
+ const activeShape = this.getActiveShape();
1137
+ const config = this.getConfig();
1138
+ if (!activeShape) {
1139
+ this.ctx.transition(config.idleStateId, this.startedAt);
1140
+ return;
1141
+ }
1142
+ const originPoint = this.editor.input.getOriginPagePoint();
1143
+ const cursorPoint = this.editor.input.getCurrentPagePoint();
1144
+ const finalizedBounds = this.editor.input.getIsDragging() ? this.editor.input.getShiftKey() ? config.buildConstrainedBounds(originPoint.x, originPoint.y, cursorPoint.x, cursorPoint.y) : config.buildUnconstrainedBounds(originPoint.x, originPoint.y, cursorPoint.x, cursorPoint.y) : config.buildDefaultBounds(originPoint.x, originPoint.y);
1145
+ this.editor.store.updateShape(activeShape.id, {
1146
+ x: finalizedBounds.x,
1147
+ y: finalizedBounds.y,
1148
+ props: {
1149
+ ...activeShape.props,
1150
+ fill: this.editor.getCurrentDrawStyle().fill,
1151
+ isComplete: true,
1152
+ isClosed: true,
1153
+ segments: config.buildSegments(finalizedBounds.width, finalizedBounds.height)
1154
+ }
1155
+ });
1156
+ this.currentShapeId = null;
1157
+ this.ctx.transition(config.idleStateId);
1158
+ }
1159
+ removeCurrentShape() {
1160
+ if (!this.currentShapeId) return;
1161
+ this.editor.store.deleteShapes([this.currentShapeId]);
1162
+ this.currentShapeId = null;
1163
+ }
1164
+ getActiveShape() {
1165
+ if (!this.currentShapeId) return null;
1166
+ const shape = this.editor.getShape(this.currentShapeId);
1167
+ if (!shape || shape.type !== "draw") return null;
1168
+ return shape;
1169
+ }
1170
+ };
1171
+
1172
+ // src/tools/geometric/geometricShapeHelpers.ts
1173
+ var MIN_SIDE_LENGTH = 1;
1174
+ var DEFAULT_RECTANGLE_WIDTH = 180;
1175
+ var DEFAULT_RECTANGLE_HEIGHT = 120;
1176
+ var DEFAULT_ELLIPSE_WIDTH = 180;
1177
+ var DEFAULT_ELLIPSE_HEIGHT = 120;
1178
+ function toSizedBounds(anchorX, anchorY, cursorX, cursorY, forceEqualSides) {
1179
+ const rawDeltaX = cursorX - anchorX;
1180
+ const rawDeltaY = cursorY - anchorY;
1181
+ const sideLength = Math.max(Math.abs(rawDeltaX), Math.abs(rawDeltaY), MIN_SIDE_LENGTH);
1182
+ if (forceEqualSides) {
1183
+ const nextDeltaX = rawDeltaX < 0 ? -sideLength : sideLength;
1184
+ const nextDeltaY = rawDeltaY < 0 ? -sideLength : sideLength;
1185
+ return normalizeBounds(anchorX, anchorY, anchorX + nextDeltaX, anchorY + nextDeltaY);
1186
+ }
1187
+ return normalizeBounds(anchorX, anchorY, cursorX, cursorY);
1188
+ }
1189
+ function normalizeBounds(startX, startY, endX, endY) {
1190
+ const x = Math.min(startX, endX);
1191
+ const y = Math.min(startY, endY);
1192
+ const width = Math.max(Math.abs(endX - startX), MIN_SIDE_LENGTH);
1193
+ const height = Math.max(Math.abs(endY - startY), MIN_SIDE_LENGTH);
1194
+ return { x, y, width, height };
1195
+ }
1196
+ function buildSquareBounds(anchorX, anchorY, cursorX, cursorY) {
1197
+ return toSizedBounds(anchorX, anchorY, cursorX, cursorY, true);
1198
+ }
1199
+ function buildRectangleBounds(anchorX, anchorY, cursorX, cursorY) {
1200
+ return toSizedBounds(anchorX, anchorY, cursorX, cursorY, false);
1201
+ }
1202
+ function buildDefaultCenteredRectangleBounds(centerX, centerY) {
1203
+ const halfWidth = DEFAULT_RECTANGLE_WIDTH / 2;
1204
+ const halfHeight = DEFAULT_RECTANGLE_HEIGHT / 2;
1205
+ return {
1206
+ x: centerX - halfWidth,
1207
+ y: centerY - halfHeight,
1208
+ width: DEFAULT_RECTANGLE_WIDTH,
1209
+ height: DEFAULT_RECTANGLE_HEIGHT
1210
+ };
1211
+ }
1212
+ function buildRectangleSegments(width, height) {
1213
+ const topLeft = { x: 0, y: 0, z: 0.5 };
1214
+ const topRight = { x: width, y: 0, z: 0.5 };
1215
+ const bottomRight = { x: width, y: height, z: 0.5 };
1216
+ const bottomLeft = { x: 0, y: height, z: 0.5 };
1217
+ return [
1218
+ { type: "straight", path: encodePoints([topLeft, topRight]) },
1219
+ { type: "straight", path: encodePoints([topRight, bottomRight]) },
1220
+ { type: "straight", path: encodePoints([bottomRight, bottomLeft]) },
1221
+ { type: "straight", path: encodePoints([bottomLeft, topLeft]) }
1222
+ ];
1223
+ }
1224
+ function buildCircleBounds(anchorX, anchorY, cursorX, cursorY) {
1225
+ return toSizedBounds(anchorX, anchorY, cursorX, cursorY, true);
1226
+ }
1227
+ function buildEllipseBounds(anchorX, anchorY, cursorX, cursorY) {
1228
+ return toSizedBounds(anchorX, anchorY, cursorX, cursorY, false);
1229
+ }
1230
+ function buildDefaultCenteredEllipseBounds(centerX, centerY) {
1231
+ const halfWidth = DEFAULT_ELLIPSE_WIDTH / 2;
1232
+ const halfHeight = DEFAULT_ELLIPSE_HEIGHT / 2;
1233
+ return {
1234
+ x: centerX - halfWidth,
1235
+ y: centerY - halfHeight,
1236
+ width: DEFAULT_ELLIPSE_WIDTH,
1237
+ height: DEFAULT_ELLIPSE_HEIGHT
1238
+ };
1239
+ }
1240
+ function buildEllipseSegments(width, height) {
1241
+ const centerX = width / 2;
1242
+ const centerY = height / 2;
1243
+ const radiusX = width / 2;
1244
+ const radiusY = height / 2;
1245
+ const sampleCount = 64;
1246
+ const sampledPoints = [];
1247
+ for (let sampleIndex = 0; sampleIndex <= sampleCount; sampleIndex += 1) {
1248
+ const progress = sampleIndex / sampleCount;
1249
+ const angle = progress * Math.PI * 2;
1250
+ sampledPoints.push({
1251
+ x: centerX + Math.cos(angle) * radiusX,
1252
+ y: centerY + Math.sin(angle) * radiusY,
1253
+ z: 0.5
1254
+ });
1255
+ }
1256
+ return [{ type: "free", path: encodePoints(sampledPoints) }];
1257
+ }
1258
+
1259
+ // src/tools/square/states/SquareDrawingState.ts
1260
+ var SquareDrawingState = class extends GeometricDrawingState {
1261
+ static id = "square_drawing";
1262
+ getConfig() {
1263
+ return {
1264
+ idleStateId: "square_idle",
1265
+ buildConstrainedBounds: buildSquareBounds,
1266
+ buildUnconstrainedBounds: buildRectangleBounds,
1267
+ buildDefaultBounds: buildDefaultCenteredRectangleBounds,
1268
+ buildSegments: buildRectangleSegments
1269
+ };
1270
+ }
1271
+ };
1272
+
1273
+ // src/tools/circle/states/CircleIdleState.ts
1274
+ var CircleIdleState = class extends StateNode {
1275
+ static id = "circle_idle";
1276
+ onPointerDown(info) {
1277
+ this.ctx.transition("circle_drawing", info);
1278
+ }
1279
+ };
1280
+
1281
+ // src/tools/circle/states/CircleDrawingState.ts
1282
+ var CircleDrawingState = class extends GeometricDrawingState {
1283
+ static id = "circle_drawing";
1284
+ getConfig() {
1285
+ return {
1286
+ idleStateId: "circle_idle",
1287
+ buildConstrainedBounds: buildCircleBounds,
1288
+ buildUnconstrainedBounds: buildEllipseBounds,
1289
+ buildDefaultBounds: buildDefaultCenteredEllipseBounds,
1290
+ buildSegments: buildEllipseSegments
1291
+ };
1292
+ }
1293
+ };
1294
+
1036
1295
  // src/tools/eraser/states/EraserIdleState.ts
1037
1296
  var EraserIdleState = class extends StateNode {
1038
1297
  static id = "eraser_idle";
@@ -1346,6 +1605,7 @@ var Editor = class {
1346
1605
  drawStyle = {
1347
1606
  color: "black",
1348
1607
  dash: "draw",
1608
+ fill: "none",
1349
1609
  size: "m"
1350
1610
  };
1351
1611
  toolStateContext;
@@ -1405,6 +1665,8 @@ var Editor = class {
1405
1665
  getDefaultToolDefinitions() {
1406
1666
  return [
1407
1667
  { id: "pen", initialStateId: PenIdleState.id, stateConstructors: [PenIdleState, PenDrawingState] },
1668
+ { id: "square", initialStateId: SquareIdleState.id, stateConstructors: [SquareIdleState, SquareDrawingState] },
1669
+ { id: "circle", initialStateId: CircleIdleState.id, stateConstructors: [CircleIdleState, CircleDrawingState] },
1408
1670
  { id: "eraser", initialStateId: EraserIdleState.id, stateConstructors: [EraserIdleState, EraserPointingState, EraserErasingState] },
1409
1671
  { id: "select", initialStateId: SelectIdleState.id, stateConstructors: [SelectIdleState] },
1410
1672
  { id: "hand", initialStateId: HandIdleState.id, stateConstructors: [HandIdleState, HandDraggingState] }
@@ -1509,7 +1771,12 @@ var Editor = class {
1509
1771
  }
1510
1772
  loadSessionStateSnapshot(snapshot) {
1511
1773
  this.setViewport(snapshot.viewport);
1512
- this.setCurrentDrawStyle(snapshot.drawStyle);
1774
+ this.setCurrentDrawStyle({
1775
+ color: snapshot.drawStyle.color,
1776
+ dash: snapshot.drawStyle.dash,
1777
+ fill: snapshot.drawStyle.fill ?? "none",
1778
+ size: snapshot.drawStyle.size
1779
+ });
1513
1780
  if (this.tools.hasTool(snapshot.currentToolId)) {
1514
1781
  this.setCurrentTool(snapshot.currentToolId);
1515
1782
  }
@@ -1890,6 +2157,6 @@ function applyResize(editor, handle, startBounds, startShapes, pointer, lockAspe
1890
2157
  }
1891
2158
  }
1892
2159
 
1893
- export { CanvasRenderer, DEFAULT_COLORS, DRAG_DISTANCE_SQUARED, DocumentStore, ERASER_MARGIN, Editor, EraserErasingState, EraserIdleState, EraserPointingState, HandDraggingState, HandIdleState, InputManager, MAX_POINTS_PER_SHAPE, PenDrawingState, PenIdleState, STROKE_WIDTHS, SelectIdleState, StateNode, ToolManager, applyMove, applyResize, applyRotation, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds2 as getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, resolveThemeColor, rotatePoint, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, zoomViewport };
2160
+ export { CanvasRenderer, CircleDrawingState, CircleIdleState, DEFAULT_COLORS, DRAG_DISTANCE_SQUARED, DocumentStore, ERASER_MARGIN, Editor, EraserErasingState, EraserIdleState, EraserPointingState, HandDraggingState, HandIdleState, InputManager, MAX_POINTS_PER_SHAPE, PenDrawingState, PenIdleState, STROKE_WIDTHS, SelectIdleState, SquareDrawingState, SquareIdleState, StateNode, ToolManager, applyMove, applyResize, applyRotation, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds2 as getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, resolveThemeColor, rotatePoint, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, zoomViewport };
1894
2161
  //# sourceMappingURL=index.js.map
1895
2162
  //# sourceMappingURL=index.js.map