@fieldnotes/core 0.47.0 → 0.48.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.cjs +295 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -3
- package/dist/index.d.ts +8 -3
- package/dist/index.js +294 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -107,13 +107,16 @@ interface GridElement extends BaseElement {
|
|
|
107
107
|
strokeWidth: number;
|
|
108
108
|
opacity: number;
|
|
109
109
|
}
|
|
110
|
-
type TemplateShape = 'circle' | 'cone' | 'line' | 'square';
|
|
110
|
+
type TemplateShape = 'circle' | 'cone' | 'line' | 'square' | 'rectangle';
|
|
111
111
|
type TemplateRenderStyle = 'cells' | 'geometric';
|
|
112
112
|
interface TemplateElement extends BaseElement {
|
|
113
113
|
type: 'template';
|
|
114
114
|
templateShape: TemplateShape;
|
|
115
115
|
radius: number;
|
|
116
116
|
angle: number;
|
|
117
|
+
/** Rectangle-only: full perpendicular extent in world units (centered on the aim axis).
|
|
118
|
+
* Absent for other shapes. Distinct from strokeWidth. */
|
|
119
|
+
width?: number;
|
|
117
120
|
fillColor: string;
|
|
118
121
|
strokeColor: string;
|
|
119
122
|
strokeWidth: number;
|
|
@@ -745,6 +748,7 @@ interface TemplateInput extends BaseDefaults {
|
|
|
745
748
|
templateShape: TemplateShape;
|
|
746
749
|
radius: number;
|
|
747
750
|
angle?: number;
|
|
751
|
+
width?: number;
|
|
748
752
|
fillColor?: string;
|
|
749
753
|
strokeColor?: string;
|
|
750
754
|
strokeWidth?: number;
|
|
@@ -771,6 +775,7 @@ declare function getHexDistance(a: Point, b: Point, cellSize: number, orientatio
|
|
|
771
775
|
declare function getHexCellsInRadius(center: Point, radiusCells: number, cellSize: number, orientation: HexOrientation): Point[];
|
|
772
776
|
declare function getHexCellsInCone(center: Point, angle: number, radiusCells: number, cellSize: number, orientation: HexOrientation): Point[];
|
|
773
777
|
declare function getHexCellsInLine(center: Point, angle: number, radiusCells: number, cellSize: number, orientation: HexOrientation): Point[];
|
|
778
|
+
declare function getHexCellsInRectangle(center: Point, angle: number, lengthCells: number, widthCells: number, cellSize: number, orientation: HexOrientation): Point[];
|
|
774
779
|
declare function getHexCellsInSquare(center: Point, radiusCells: number, cellSize: number, orientation: HexOrientation): Point[];
|
|
775
780
|
declare function drawHexPath(ctx: CanvasRenderingContext2D, cx: number, cy: number, cellSize: number, orientation: HexOrientation): void;
|
|
776
781
|
|
|
@@ -1115,6 +1120,6 @@ declare class LaserTool implements Tool {
|
|
|
1115
1120
|
private notifyOptionsChange;
|
|
1116
1121
|
}
|
|
1117
1122
|
|
|
1118
|
-
declare const VERSION = "0.
|
|
1123
|
+
declare const VERSION = "0.48.0";
|
|
1119
1124
|
|
|
1120
|
-
export { type ActiveFormats, type AlignEdge, type ArrowElement, type ArrowStrokeStyle, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, type BackgroundOptions, type BackgroundPattern, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, DEFAULT_NOTE_FONT_SIZE, type DistributeAxis, type ElementChangeMeta, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportImageOptions, type ExportSvgOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, IndexedDBAdapter, type IndexedDBAdapterOptions, LaserTool, type LaserToolOptions, type Layer, LayerManager, LocalStorageAdapter, MeasureTool, type MeasureToolOptions, type Measurement, MemoryAdapter, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, type RenderStatsSnapshot, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StorageAdapter, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateRenderStyle, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, VERSION, Viewport, type ViewportOptions, boundsIntersect, createArrow, createGrid, createHtmlElement, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, exportSvg, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getElementBounds, getElementStyle, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, styleToPatch, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
|
|
1125
|
+
export { type ActiveFormats, type AlignEdge, type ArrowElement, type ArrowStrokeStyle, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, type BackgroundOptions, type BackgroundPattern, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, DEFAULT_NOTE_FONT_SIZE, type DistributeAxis, type ElementChangeMeta, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportImageOptions, type ExportSvgOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, IndexedDBAdapter, type IndexedDBAdapterOptions, LaserTool, type LaserToolOptions, type Layer, LayerManager, LocalStorageAdapter, MeasureTool, type MeasureToolOptions, type Measurement, MemoryAdapter, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, type RenderStatsSnapshot, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StorageAdapter, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateRenderStyle, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, VERSION, Viewport, type ViewportOptions, boundsIntersect, createArrow, createGrid, createHtmlElement, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, exportSvg, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getElementBounds, getElementStyle, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInRectangle, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, styleToPatch, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
|
package/dist/index.d.ts
CHANGED
|
@@ -107,13 +107,16 @@ interface GridElement extends BaseElement {
|
|
|
107
107
|
strokeWidth: number;
|
|
108
108
|
opacity: number;
|
|
109
109
|
}
|
|
110
|
-
type TemplateShape = 'circle' | 'cone' | 'line' | 'square';
|
|
110
|
+
type TemplateShape = 'circle' | 'cone' | 'line' | 'square' | 'rectangle';
|
|
111
111
|
type TemplateRenderStyle = 'cells' | 'geometric';
|
|
112
112
|
interface TemplateElement extends BaseElement {
|
|
113
113
|
type: 'template';
|
|
114
114
|
templateShape: TemplateShape;
|
|
115
115
|
radius: number;
|
|
116
116
|
angle: number;
|
|
117
|
+
/** Rectangle-only: full perpendicular extent in world units (centered on the aim axis).
|
|
118
|
+
* Absent for other shapes. Distinct from strokeWidth. */
|
|
119
|
+
width?: number;
|
|
117
120
|
fillColor: string;
|
|
118
121
|
strokeColor: string;
|
|
119
122
|
strokeWidth: number;
|
|
@@ -745,6 +748,7 @@ interface TemplateInput extends BaseDefaults {
|
|
|
745
748
|
templateShape: TemplateShape;
|
|
746
749
|
radius: number;
|
|
747
750
|
angle?: number;
|
|
751
|
+
width?: number;
|
|
748
752
|
fillColor?: string;
|
|
749
753
|
strokeColor?: string;
|
|
750
754
|
strokeWidth?: number;
|
|
@@ -771,6 +775,7 @@ declare function getHexDistance(a: Point, b: Point, cellSize: number, orientatio
|
|
|
771
775
|
declare function getHexCellsInRadius(center: Point, radiusCells: number, cellSize: number, orientation: HexOrientation): Point[];
|
|
772
776
|
declare function getHexCellsInCone(center: Point, angle: number, radiusCells: number, cellSize: number, orientation: HexOrientation): Point[];
|
|
773
777
|
declare function getHexCellsInLine(center: Point, angle: number, radiusCells: number, cellSize: number, orientation: HexOrientation): Point[];
|
|
778
|
+
declare function getHexCellsInRectangle(center: Point, angle: number, lengthCells: number, widthCells: number, cellSize: number, orientation: HexOrientation): Point[];
|
|
774
779
|
declare function getHexCellsInSquare(center: Point, radiusCells: number, cellSize: number, orientation: HexOrientation): Point[];
|
|
775
780
|
declare function drawHexPath(ctx: CanvasRenderingContext2D, cx: number, cy: number, cellSize: number, orientation: HexOrientation): void;
|
|
776
781
|
|
|
@@ -1115,6 +1120,6 @@ declare class LaserTool implements Tool {
|
|
|
1115
1120
|
private notifyOptionsChange;
|
|
1116
1121
|
}
|
|
1117
1122
|
|
|
1118
|
-
declare const VERSION = "0.
|
|
1123
|
+
declare const VERSION = "0.48.0";
|
|
1119
1124
|
|
|
1120
|
-
export { type ActiveFormats, type AlignEdge, type ArrowElement, type ArrowStrokeStyle, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, type BackgroundOptions, type BackgroundPattern, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, DEFAULT_NOTE_FONT_SIZE, type DistributeAxis, type ElementChangeMeta, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportImageOptions, type ExportSvgOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, IndexedDBAdapter, type IndexedDBAdapterOptions, LaserTool, type LaserToolOptions, type Layer, LayerManager, LocalStorageAdapter, MeasureTool, type MeasureToolOptions, type Measurement, MemoryAdapter, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, type RenderStatsSnapshot, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StorageAdapter, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateRenderStyle, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, VERSION, Viewport, type ViewportOptions, boundsIntersect, createArrow, createGrid, createHtmlElement, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, exportSvg, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getElementBounds, getElementStyle, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, styleToPatch, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
|
|
1125
|
+
export { type ActiveFormats, type AlignEdge, type ArrowElement, type ArrowStrokeStyle, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, type BackgroundOptions, type BackgroundPattern, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, DEFAULT_NOTE_FONT_SIZE, type DistributeAxis, type ElementChangeMeta, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportImageOptions, type ExportSvgOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, IndexedDBAdapter, type IndexedDBAdapterOptions, LaserTool, type LaserToolOptions, type Layer, LayerManager, LocalStorageAdapter, MeasureTool, type MeasureToolOptions, type Measurement, MemoryAdapter, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, type RenderStatsSnapshot, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StorageAdapter, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateRenderStyle, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, VERSION, Viewport, type ViewportOptions, boundsIntersect, createArrow, createGrid, createHtmlElement, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, exportSvg, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getElementBounds, getElementStyle, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInRectangle, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, styleToPatch, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
|
package/dist/index.js
CHANGED
|
@@ -897,6 +897,26 @@ function getTemplateBounds(el) {
|
|
|
897
897
|
const maxY = Math.max(y0, y1, y2, y3);
|
|
898
898
|
return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };
|
|
899
899
|
}
|
|
900
|
+
case "rectangle": {
|
|
901
|
+
const halfW = (el.width ?? 0) / 2;
|
|
902
|
+
const cos = Math.cos(el.angle);
|
|
903
|
+
const sin = Math.sin(el.angle);
|
|
904
|
+
const perpX = -sin * halfW;
|
|
905
|
+
const perpY = cos * halfW;
|
|
906
|
+
const x0 = cx + perpX;
|
|
907
|
+
const y0 = cy + perpY;
|
|
908
|
+
const x1 = cx + r * cos + perpX;
|
|
909
|
+
const y1 = cy + r * sin + perpY;
|
|
910
|
+
const x2 = cx + r * cos - perpX;
|
|
911
|
+
const y2 = cy + r * sin - perpY;
|
|
912
|
+
const x3 = cx - perpX;
|
|
913
|
+
const y3 = cy - perpY;
|
|
914
|
+
const minX = Math.min(x0, x1, x2, x3);
|
|
915
|
+
const minY = Math.min(y0, y1, y2, y3);
|
|
916
|
+
const maxX = Math.max(x0, x1, x2, x3);
|
|
917
|
+
const maxY = Math.max(y0, y1, y2, y3);
|
|
918
|
+
return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };
|
|
919
|
+
}
|
|
900
920
|
}
|
|
901
921
|
}
|
|
902
922
|
function transferStrokeBounds(prev, next) {
|
|
@@ -3200,6 +3220,46 @@ function getHexCellsInLine(center2, angle, radiusCells, cellSize, orientation) {
|
|
|
3200
3220
|
}
|
|
3201
3221
|
return cells;
|
|
3202
3222
|
}
|
|
3223
|
+
function getHexCellsInRectangle(center2, angle, lengthCells, widthCells, cellSize, orientation) {
|
|
3224
|
+
const nLen = Math.round(lengthCells);
|
|
3225
|
+
const wCells = Math.max(1, Math.round(widthCells));
|
|
3226
|
+
const off = pixelToOffset(center2.x, center2.y, cellSize, orientation);
|
|
3227
|
+
const cube = offsetToCube(off.col, off.row, orientation);
|
|
3228
|
+
const centerPixel = offsetToPixel(off.col, off.row, cellSize, orientation);
|
|
3229
|
+
if (nLen <= 0) return [centerPixel];
|
|
3230
|
+
const vertexOffset = orientation === "pointy" ? Math.PI / 6 : 0;
|
|
3231
|
+
const step = Math.PI / 3;
|
|
3232
|
+
const snappedAngle = Math.round((angle - vertexOffset) / step) * step + vertexOffset;
|
|
3233
|
+
const cos = Math.cos(snappedAngle);
|
|
3234
|
+
const sin = Math.sin(snappedAngle);
|
|
3235
|
+
const snapUnit = Math.sqrt(3) * cellSize;
|
|
3236
|
+
const lineLength = nLen * snapUnit;
|
|
3237
|
+
const halfWidth = wCells * snapUnit / 2 + 1e-6;
|
|
3238
|
+
const iterM = nLen + Math.ceil(wCells / 2) + 1;
|
|
3239
|
+
const cells = [];
|
|
3240
|
+
for (let dq = -iterM; dq <= iterM; dq++) {
|
|
3241
|
+
const rMin = Math.max(-iterM, -dq - iterM);
|
|
3242
|
+
const rMax = Math.min(iterM, -dq + iterM);
|
|
3243
|
+
for (let dr = rMin; dr <= rMax; dr++) {
|
|
3244
|
+
const absQ = cube.q + dq;
|
|
3245
|
+
const absR = cube.r + dr;
|
|
3246
|
+
const pixel = offsetToPixel(
|
|
3247
|
+
cubeToOffset(absQ, absR, orientation).col,
|
|
3248
|
+
cubeToOffset(absQ, absR, orientation).row,
|
|
3249
|
+
cellSize,
|
|
3250
|
+
orientation
|
|
3251
|
+
);
|
|
3252
|
+
const dx = pixel.x - centerPixel.x;
|
|
3253
|
+
const dy = pixel.y - centerPixel.y;
|
|
3254
|
+
const along = dx * cos + dy * sin;
|
|
3255
|
+
const perp = Math.abs(-dx * sin + dy * cos);
|
|
3256
|
+
if (along >= -snapUnit * 0.1 && along <= lineLength + snapUnit * 0.1 && perp <= halfWidth) {
|
|
3257
|
+
cells.push(pixel);
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3261
|
+
return cells;
|
|
3262
|
+
}
|
|
3203
3263
|
function getHexCellsInSquare(center2, radiusCells, cellSize, orientation) {
|
|
3204
3264
|
const n2 = Math.round(radiusCells);
|
|
3205
3265
|
const off = pixelToOffset(center2.x, center2.y, cellSize, orientation);
|
|
@@ -3241,7 +3301,7 @@ function drawHexPath(ctx, cx, cy, cellSize, orientation) {
|
|
|
3241
3301
|
// src/elements/renderers/template-measure.ts
|
|
3242
3302
|
function renderTemplateFeetLabel(ctx, p) {
|
|
3243
3303
|
if (p.feet <= 0) return;
|
|
3244
|
-
const directional = p.templateShape === "cone" || p.templateShape === "line";
|
|
3304
|
+
const directional = p.templateShape === "cone" || p.templateShape === "line" || p.templateShape === "rectangle";
|
|
3245
3305
|
const dir = directional ? p.angle : 0;
|
|
3246
3306
|
const length = p.templateShape === "square" ? p.radius / 2 : p.radius;
|
|
3247
3307
|
const cos = Math.cos(dir);
|
|
@@ -3334,6 +3394,22 @@ function renderGeometricTemplate(ctx, template) {
|
|
|
3334
3394
|
ctx.stroke();
|
|
3335
3395
|
break;
|
|
3336
3396
|
}
|
|
3397
|
+
case "rectangle": {
|
|
3398
|
+
const halfW = (template.width ?? 0) / 2;
|
|
3399
|
+
const cos = Math.cos(template.angle);
|
|
3400
|
+
const sin = Math.sin(template.angle);
|
|
3401
|
+
const perpX = -sin * halfW;
|
|
3402
|
+
const perpY = cos * halfW;
|
|
3403
|
+
ctx.beginPath();
|
|
3404
|
+
ctx.moveTo(cx + perpX, cy + perpY);
|
|
3405
|
+
ctx.lineTo(cx + r * cos + perpX, cy + r * sin + perpY);
|
|
3406
|
+
ctx.lineTo(cx + r * cos - perpX, cy + r * sin - perpY);
|
|
3407
|
+
ctx.lineTo(cx - perpX, cy - perpY);
|
|
3408
|
+
ctx.closePath();
|
|
3409
|
+
ctx.fill();
|
|
3410
|
+
ctx.stroke();
|
|
3411
|
+
break;
|
|
3412
|
+
}
|
|
3337
3413
|
}
|
|
3338
3414
|
if (template.radiusFeet != null && template.radiusFeet > 0) {
|
|
3339
3415
|
renderTemplateFeetLabel(ctx, {
|
|
@@ -3365,6 +3441,18 @@ function renderHexTemplate(ctx, template, cellSize, orientation) {
|
|
|
3365
3441
|
case "square":
|
|
3366
3442
|
cells = getHexCellsInSquare(center2, radiusCells, cellSize, orientation);
|
|
3367
3443
|
break;
|
|
3444
|
+
case "rectangle": {
|
|
3445
|
+
const widthCells = (template.width ?? 0) / snapUnit;
|
|
3446
|
+
cells = getHexCellsInRectangle(
|
|
3447
|
+
center2,
|
|
3448
|
+
template.angle,
|
|
3449
|
+
radiusCells,
|
|
3450
|
+
widthCells,
|
|
3451
|
+
cellSize,
|
|
3452
|
+
orientation
|
|
3453
|
+
);
|
|
3454
|
+
break;
|
|
3455
|
+
}
|
|
3368
3456
|
}
|
|
3369
3457
|
ctx.save();
|
|
3370
3458
|
ctx.globalAlpha = template.opacity;
|
|
@@ -3941,7 +4029,8 @@ function createTemplate(input) {
|
|
|
3941
4029
|
opacity: input.opacity ?? 0.6,
|
|
3942
4030
|
feetPerCell: input.feetPerCell,
|
|
3943
4031
|
radiusFeet: input.radiusFeet,
|
|
3944
|
-
...input.renderStyle !== void 0 ? { renderStyle: input.renderStyle } : {}
|
|
4032
|
+
...input.renderStyle !== void 0 ? { renderStyle: input.renderStyle } : {},
|
|
4033
|
+
...input.width !== void 0 ? { width: input.width } : {}
|
|
3945
4034
|
};
|
|
3946
4035
|
}
|
|
3947
4036
|
|
|
@@ -5538,6 +5627,20 @@ function emitGeometricTemplate(t) {
|
|
|
5538
5627
|
].map(([px, py]) => `${n(px ?? 0)},${n(py ?? 0)}`).join(" ");
|
|
5539
5628
|
return `<polygon points="${pts}" ${attrs} />`;
|
|
5540
5629
|
}
|
|
5630
|
+
case "rectangle": {
|
|
5631
|
+
const halfW = (t.width ?? 0) / 2;
|
|
5632
|
+
const cos = Math.cos(t.angle);
|
|
5633
|
+
const sin = Math.sin(t.angle);
|
|
5634
|
+
const perpX = -sin * halfW;
|
|
5635
|
+
const perpY = cos * halfW;
|
|
5636
|
+
const pts = [
|
|
5637
|
+
[cx + perpX, cy + perpY],
|
|
5638
|
+
[cx + r * cos + perpX, cy + r * sin + perpY],
|
|
5639
|
+
[cx + r * cos - perpX, cy + r * sin - perpY],
|
|
5640
|
+
[cx - perpX, cy - perpY]
|
|
5641
|
+
].map(([px, py]) => `${n(px ?? 0)},${n(py ?? 0)}`).join(" ");
|
|
5642
|
+
return `<polygon points="${pts}" ${attrs} />`;
|
|
5643
|
+
}
|
|
5541
5644
|
}
|
|
5542
5645
|
}
|
|
5543
5646
|
function emitHexTemplate(t, grid) {
|
|
@@ -5560,6 +5663,18 @@ function emitHexTemplate(t, grid) {
|
|
|
5560
5663
|
case "square":
|
|
5561
5664
|
cells = getHexCellsInSquare(center2, radiusCells, cellSize, orientation);
|
|
5562
5665
|
break;
|
|
5666
|
+
case "rectangle": {
|
|
5667
|
+
const widthCells = (t.width ?? 0) / snapUnit;
|
|
5668
|
+
cells = getHexCellsInRectangle(
|
|
5669
|
+
center2,
|
|
5670
|
+
t.angle,
|
|
5671
|
+
radiusCells,
|
|
5672
|
+
widthCells,
|
|
5673
|
+
cellSize,
|
|
5674
|
+
orientation
|
|
5675
|
+
);
|
|
5676
|
+
break;
|
|
5677
|
+
}
|
|
5563
5678
|
}
|
|
5564
5679
|
let d = "";
|
|
5565
5680
|
for (const cell of cells) {
|
|
@@ -8250,7 +8365,8 @@ function getOverlayLayout(el, zoom) {
|
|
|
8250
8365
|
}
|
|
8251
8366
|
function templateAimKnob(el, zoom) {
|
|
8252
8367
|
if (el.type !== "template") return null;
|
|
8253
|
-
if (el.templateShape !== "cone" && el.templateShape !== "line"
|
|
8368
|
+
if (el.templateShape !== "cone" && el.templateShape !== "line" && el.templateShape !== "rectangle")
|
|
8369
|
+
return null;
|
|
8254
8370
|
const gap = ROTATE_HANDLE_OFFSET / zoom;
|
|
8255
8371
|
const dist = el.radius + gap;
|
|
8256
8372
|
const origin = el.position;
|
|
@@ -8395,22 +8511,51 @@ function renderSelectionBoxes(ctx, p) {
|
|
|
8395
8511
|
} else if (el.type === "template") {
|
|
8396
8512
|
ctx.setLineDash([]);
|
|
8397
8513
|
ctx.fillStyle = "#ffffff";
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
|
|
8401
|
-
|
|
8402
|
-
|
|
8403
|
-
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
|
|
8407
|
-
|
|
8408
|
-
|
|
8409
|
-
|
|
8410
|
-
|
|
8411
|
-
|
|
8514
|
+
if (el.templateShape === "rectangle") {
|
|
8515
|
+
if (p.selectedIds.length === 1) {
|
|
8516
|
+
const cos = Math.cos(el.angle);
|
|
8517
|
+
const sin = Math.sin(el.angle);
|
|
8518
|
+
const halfW = (el.width ?? 0) / 2;
|
|
8519
|
+
const pts = [
|
|
8520
|
+
[el.position.x + el.radius * cos, el.position.y + el.radius * sin],
|
|
8521
|
+
[
|
|
8522
|
+
el.position.x + el.radius / 2 * cos + halfW * -sin,
|
|
8523
|
+
el.position.y + el.radius / 2 * sin + halfW * cos
|
|
8524
|
+
]
|
|
8525
|
+
];
|
|
8526
|
+
for (const [hx, hy] of pts) {
|
|
8527
|
+
ctx.fillRect(
|
|
8528
|
+
hx - handleWorldSize / 2,
|
|
8529
|
+
hy - handleWorldSize / 2,
|
|
8530
|
+
handleWorldSize,
|
|
8531
|
+
handleWorldSize
|
|
8532
|
+
);
|
|
8533
|
+
ctx.strokeRect(
|
|
8534
|
+
hx - handleWorldSize / 2,
|
|
8535
|
+
hy - handleWorldSize / 2,
|
|
8536
|
+
handleWorldSize,
|
|
8537
|
+
handleWorldSize
|
|
8538
|
+
);
|
|
8539
|
+
}
|
|
8540
|
+
}
|
|
8541
|
+
} else {
|
|
8542
|
+
const hx = bounds.x + bounds.w;
|
|
8543
|
+
const hy = bounds.y + bounds.h;
|
|
8544
|
+
ctx.fillRect(
|
|
8545
|
+
hx - handleWorldSize / 2,
|
|
8546
|
+
hy - handleWorldSize / 2,
|
|
8547
|
+
handleWorldSize,
|
|
8548
|
+
handleWorldSize
|
|
8549
|
+
);
|
|
8550
|
+
ctx.strokeRect(
|
|
8551
|
+
hx - handleWorldSize / 2,
|
|
8552
|
+
hy - handleWorldSize / 2,
|
|
8553
|
+
handleWorldSize,
|
|
8554
|
+
handleWorldSize
|
|
8555
|
+
);
|
|
8556
|
+
}
|
|
8412
8557
|
ctx.setLineDash([4 / zoom, 4 / zoom]);
|
|
8413
|
-
if (p.selectedIds.length === 1 && (el.templateShape === "cone" || el.templateShape === "line")) {
|
|
8558
|
+
if (p.selectedIds.length === 1 && (el.templateShape === "cone" || el.templateShape === "line" || el.templateShape === "rectangle")) {
|
|
8414
8559
|
const aim = templateAimKnob(el, zoom);
|
|
8415
8560
|
if (aim) {
|
|
8416
8561
|
ctx.beginPath();
|
|
@@ -8571,6 +8716,7 @@ function hitTestTemplateResizeHandle(world, ctx, selectedIds) {
|
|
|
8571
8716
|
for (const id of selectedIds) {
|
|
8572
8717
|
const el = ctx.store.getById(id);
|
|
8573
8718
|
if (!el || el.type !== "template") continue;
|
|
8719
|
+
if (el.templateShape === "rectangle") continue;
|
|
8574
8720
|
const bounds = getElementBounds(el);
|
|
8575
8721
|
if (!bounds) continue;
|
|
8576
8722
|
const hx = bounds.x + bounds.w;
|
|
@@ -8594,6 +8740,37 @@ function hitTestTemplateAimHandle(world, ctx, selectedIds) {
|
|
|
8594
8740
|
const dy = world.y - knob.knob.y;
|
|
8595
8741
|
return dx * dx + dy * dy <= r * r ? { elementId: id } : null;
|
|
8596
8742
|
}
|
|
8743
|
+
function hitTestRectangleLengthHandle(world, ctx, selectedIds) {
|
|
8744
|
+
if (selectedIds.length !== 1) return null;
|
|
8745
|
+
const id = selectedIds[0];
|
|
8746
|
+
if (!id) return null;
|
|
8747
|
+
const el = ctx.store.getById(id);
|
|
8748
|
+
if (!el || el.locked || el.type !== "template" || el.templateShape !== "rectangle") return null;
|
|
8749
|
+
const zoom = ctx.camera.zoom;
|
|
8750
|
+
const r = (HANDLE_SIZE / 2 + HANDLE_HIT_PADDING2) / zoom;
|
|
8751
|
+
const hx = el.position.x + el.radius * Math.cos(el.angle);
|
|
8752
|
+
const hy = el.position.y + el.radius * Math.sin(el.angle);
|
|
8753
|
+
const dx = world.x - hx;
|
|
8754
|
+
const dy = world.y - hy;
|
|
8755
|
+
return dx * dx + dy * dy <= r * r ? { elementId: id } : null;
|
|
8756
|
+
}
|
|
8757
|
+
function hitTestRectangleWidthHandle(world, ctx, selectedIds) {
|
|
8758
|
+
if (selectedIds.length !== 1) return null;
|
|
8759
|
+
const id = selectedIds[0];
|
|
8760
|
+
if (!id) return null;
|
|
8761
|
+
const el = ctx.store.getById(id);
|
|
8762
|
+
if (!el || el.locked || el.type !== "template" || el.templateShape !== "rectangle") return null;
|
|
8763
|
+
const zoom = ctx.camera.zoom;
|
|
8764
|
+
const r = (HANDLE_SIZE / 2 + HANDLE_HIT_PADDING2) / zoom;
|
|
8765
|
+
const cos = Math.cos(el.angle);
|
|
8766
|
+
const sin = Math.sin(el.angle);
|
|
8767
|
+
const halfW = (el.width ?? 0) / 2;
|
|
8768
|
+
const hx = el.position.x + el.radius / 2 * cos + halfW * -sin;
|
|
8769
|
+
const hy = el.position.y + el.radius / 2 * sin + halfW * cos;
|
|
8770
|
+
const dx = world.x - hx;
|
|
8771
|
+
const dy = world.y - hy;
|
|
8772
|
+
return dx * dx + dy * dy <= r * r ? { elementId: id } : null;
|
|
8773
|
+
}
|
|
8597
8774
|
function findElementsInRect(marquee, ctx) {
|
|
8598
8775
|
const candidates = ctx.store.queryRect(marquee);
|
|
8599
8776
|
const ids = [];
|
|
@@ -8748,6 +8925,34 @@ function computeTemplateResize(el, world, opts) {
|
|
|
8748
8925
|
}
|
|
8749
8926
|
return updates;
|
|
8750
8927
|
}
|
|
8928
|
+
function computeRectangleLengthResize(el, world, opts) {
|
|
8929
|
+
const cos = Math.cos(el.angle);
|
|
8930
|
+
const sin = Math.sin(el.angle);
|
|
8931
|
+
let len = (world.x - el.position.x) * cos + (world.y - el.position.y) * sin;
|
|
8932
|
+
if (opts.snapToGrid && opts.gridSize && opts.gridSize > 0) {
|
|
8933
|
+
const snapUnit = opts.gridType === "hex" ? Math.sqrt(3) * opts.gridSize : opts.gridSize;
|
|
8934
|
+
len = Math.max(snapUnit, Math.round(len / snapUnit) * snapUnit);
|
|
8935
|
+
}
|
|
8936
|
+
len = Math.max(MIN_ELEMENT_SIZE, len);
|
|
8937
|
+
const updates = { radius: len };
|
|
8938
|
+
if (el.feetPerCell != null && opts.gridSize && opts.gridSize > 0) {
|
|
8939
|
+
const snapUnit = opts.gridType === "hex" ? Math.sqrt(3) * opts.gridSize : opts.gridSize;
|
|
8940
|
+
updates.radiusFeet = len / snapUnit * el.feetPerCell;
|
|
8941
|
+
}
|
|
8942
|
+
return updates;
|
|
8943
|
+
}
|
|
8944
|
+
function computeRectangleWidthResize(el, world, opts) {
|
|
8945
|
+
const cos = Math.cos(el.angle);
|
|
8946
|
+
const sin = Math.sin(el.angle);
|
|
8947
|
+
const perp = Math.abs(-(world.x - el.position.x) * sin + (world.y - el.position.y) * cos);
|
|
8948
|
+
let width = perp * 2;
|
|
8949
|
+
if (opts.snapToGrid && opts.gridSize && opts.gridSize > 0) {
|
|
8950
|
+
const snapUnit = opts.gridType === "hex" ? Math.sqrt(3) * opts.gridSize : opts.gridSize;
|
|
8951
|
+
width = Math.max(snapUnit, Math.round(width / snapUnit) * snapUnit);
|
|
8952
|
+
}
|
|
8953
|
+
width = Math.max(MIN_ELEMENT_SIZE, width);
|
|
8954
|
+
return { width };
|
|
8955
|
+
}
|
|
8751
8956
|
|
|
8752
8957
|
// src/tools/select-tool.ts
|
|
8753
8958
|
var SNAP_PX = 6;
|
|
@@ -8843,6 +9048,18 @@ var SelectTool = class {
|
|
|
8843
9048
|
ctx.requestRender();
|
|
8844
9049
|
return;
|
|
8845
9050
|
}
|
|
9051
|
+
const rectLengthHit = hitTestRectangleLengthHandle(world, ctx, this._selectedIds);
|
|
9052
|
+
if (rectLengthHit) {
|
|
9053
|
+
this.mode = { type: "resizing-rect-length", elementId: rectLengthHit.elementId };
|
|
9054
|
+
ctx.requestRender();
|
|
9055
|
+
return;
|
|
9056
|
+
}
|
|
9057
|
+
const rectWidthHit = hitTestRectangleWidthHandle(world, ctx, this._selectedIds);
|
|
9058
|
+
if (rectWidthHit) {
|
|
9059
|
+
this.mode = { type: "resizing-rect-width", elementId: rectWidthHit.elementId };
|
|
9060
|
+
ctx.requestRender();
|
|
9061
|
+
return;
|
|
9062
|
+
}
|
|
8846
9063
|
const aimHit = hitTestTemplateAimHandle(world, ctx, this._selectedIds);
|
|
8847
9064
|
if (aimHit) {
|
|
8848
9065
|
this.mode = { type: "aiming-template", elementId: aimHit.elementId };
|
|
@@ -8943,6 +9160,19 @@ var SelectTool = class {
|
|
|
8943
9160
|
}
|
|
8944
9161
|
return;
|
|
8945
9162
|
}
|
|
9163
|
+
if (this.mode.type === "resizing-rect-length" || this.mode.type === "resizing-rect-width") {
|
|
9164
|
+
ctx.setCursor?.(this.mode.type === "resizing-rect-length" ? "ew-resize" : "ns-resize");
|
|
9165
|
+
const el = ctx.store.getById(this.mode.elementId);
|
|
9166
|
+
if (el && el.type === "template" && !el.locked) {
|
|
9167
|
+
const opts = { snapToGrid: ctx.snapToGrid, gridSize: ctx.gridSize, gridType: ctx.gridType };
|
|
9168
|
+
const patch = this.mode.type === "resizing-rect-length" ? computeRectangleLengthResize(el, world, opts) : computeRectangleWidthResize(el, world, opts);
|
|
9169
|
+
if (patch) {
|
|
9170
|
+
ctx.store.update(this.mode.elementId, patch);
|
|
9171
|
+
ctx.requestRender();
|
|
9172
|
+
}
|
|
9173
|
+
}
|
|
9174
|
+
return;
|
|
9175
|
+
}
|
|
8946
9176
|
if (this.mode.type === "rotating") {
|
|
8947
9177
|
const { elementId, center: center2, startPointerAngle, startRotation } = this.mode;
|
|
8948
9178
|
const a = Math.atan2(world.y - center2.y, world.x - center2.x);
|
|
@@ -9149,6 +9379,14 @@ var SelectTool = class {
|
|
|
9149
9379
|
ctx.setCursor?.("nwse-resize");
|
|
9150
9380
|
return null;
|
|
9151
9381
|
}
|
|
9382
|
+
if (hitTestRectangleLengthHandle(world, ctx, this._selectedIds)) {
|
|
9383
|
+
ctx.setCursor?.("ew-resize");
|
|
9384
|
+
return null;
|
|
9385
|
+
}
|
|
9386
|
+
if (hitTestRectangleWidthHandle(world, ctx, this._selectedIds)) {
|
|
9387
|
+
ctx.setCursor?.("ns-resize");
|
|
9388
|
+
return null;
|
|
9389
|
+
}
|
|
9152
9390
|
if (hitTestTemplateAimHandle(world, ctx, this._selectedIds)) {
|
|
9153
9391
|
ctx.setCursor?.("grab");
|
|
9154
9392
|
return null;
|
|
@@ -9813,6 +10051,11 @@ var MeasureTool = class {
|
|
|
9813
10051
|
};
|
|
9814
10052
|
|
|
9815
10053
|
// src/tools/template-tool.ts
|
|
10054
|
+
var MIN_RECT_WIDTH = 20;
|
|
10055
|
+
function defaultRectWidth(radius, scaleUnit) {
|
|
10056
|
+
if (scaleUnit > 0) return scaleUnit;
|
|
10057
|
+
return Math.max(MIN_RECT_WIDTH, radius * 0.25);
|
|
10058
|
+
}
|
|
9816
10059
|
var TemplateTool = class {
|
|
9817
10060
|
name = "template";
|
|
9818
10061
|
drawing = false;
|
|
@@ -9891,11 +10134,13 @@ var TemplateTool = class {
|
|
|
9891
10134
|
const snapUnit = gridSize && gridSize > 0 ? ctx.gridType === "hex" ? Math.sqrt(3) * gridSize : gridSize : 0;
|
|
9892
10135
|
const cells = snapUnit > 0 ? radius / snapUnit : 0;
|
|
9893
10136
|
const radiusFeet = cells * this.feetPerCell;
|
|
10137
|
+
const width = this.templateShape === "rectangle" ? defaultRectWidth(radius, this.feetScaleUnit) : void 0;
|
|
9894
10138
|
const element = createTemplate({
|
|
9895
10139
|
position: { ...this.origin },
|
|
9896
10140
|
templateShape: this.templateShape,
|
|
9897
10141
|
radius,
|
|
9898
10142
|
angle,
|
|
10143
|
+
width,
|
|
9899
10144
|
fillColor: this.fillColor,
|
|
9900
10145
|
strokeColor: this.strokeColor,
|
|
9901
10146
|
strokeWidth: this.strokeWidth,
|
|
@@ -9970,6 +10215,22 @@ var TemplateTool = class {
|
|
|
9970
10215
|
ctx.stroke();
|
|
9971
10216
|
break;
|
|
9972
10217
|
}
|
|
10218
|
+
case "rectangle": {
|
|
10219
|
+
const halfW = defaultRectWidth(radius, this.feetScaleUnit) / 2;
|
|
10220
|
+
const cos = Math.cos(angle);
|
|
10221
|
+
const sin = Math.sin(angle);
|
|
10222
|
+
const perpX = -sin * halfW;
|
|
10223
|
+
const perpY = cos * halfW;
|
|
10224
|
+
ctx.beginPath();
|
|
10225
|
+
ctx.moveTo(cx + perpX, cy + perpY);
|
|
10226
|
+
ctx.lineTo(cx + radius * cos + perpX, cy + radius * sin + perpY);
|
|
10227
|
+
ctx.lineTo(cx + radius * cos - perpX, cy + radius * sin - perpY);
|
|
10228
|
+
ctx.lineTo(cx - perpX, cy - perpY);
|
|
10229
|
+
ctx.closePath();
|
|
10230
|
+
ctx.fill();
|
|
10231
|
+
ctx.stroke();
|
|
10232
|
+
break;
|
|
10233
|
+
}
|
|
9973
10234
|
}
|
|
9974
10235
|
this.drawFeetLabel(ctx, radius);
|
|
9975
10236
|
ctx.restore();
|
|
@@ -9996,6 +10257,18 @@ var TemplateTool = class {
|
|
|
9996
10257
|
case "square":
|
|
9997
10258
|
hexCells = getHexCellsInSquare(center2, radiusCells, cellSize, orientation);
|
|
9998
10259
|
break;
|
|
10260
|
+
case "rectangle": {
|
|
10261
|
+
const widthCells = defaultRectWidth(radius, this.feetScaleUnit) / snapUnit;
|
|
10262
|
+
hexCells = getHexCellsInRectangle(
|
|
10263
|
+
center2,
|
|
10264
|
+
angle,
|
|
10265
|
+
radiusCells,
|
|
10266
|
+
widthCells,
|
|
10267
|
+
cellSize,
|
|
10268
|
+
orientation
|
|
10269
|
+
);
|
|
10270
|
+
break;
|
|
10271
|
+
}
|
|
9999
10272
|
}
|
|
10000
10273
|
ctx.save();
|
|
10001
10274
|
ctx.globalAlpha = 0.4;
|
|
@@ -10012,7 +10285,7 @@ var TemplateTool = class {
|
|
|
10012
10285
|
ctx.strokeStyle = this.strokeColor;
|
|
10013
10286
|
ctx.lineWidth = this.strokeWidth;
|
|
10014
10287
|
ctx.stroke();
|
|
10015
|
-
if (this.templateShape === "cone" || this.templateShape === "line" || this.templateShape === "circle" || this.templateShape === "square") {
|
|
10288
|
+
if (this.templateShape === "cone" || this.templateShape === "line" || this.templateShape === "circle" || this.templateShape === "square" || this.templateShape === "rectangle") {
|
|
10016
10289
|
ctx.globalAlpha = 0.5;
|
|
10017
10290
|
ctx.beginPath();
|
|
10018
10291
|
drawHexPath(ctx, center2.x, center2.y, cellSize, orientation);
|
|
@@ -10180,7 +10453,7 @@ var LaserTool = class {
|
|
|
10180
10453
|
};
|
|
10181
10454
|
|
|
10182
10455
|
// src/index.ts
|
|
10183
|
-
var VERSION = "0.
|
|
10456
|
+
var VERSION = "0.48.0";
|
|
10184
10457
|
export {
|
|
10185
10458
|
ArrowTool,
|
|
10186
10459
|
AutoSave,
|
|
@@ -10231,6 +10504,7 @@ export {
|
|
|
10231
10504
|
getHexCellsInCone,
|
|
10232
10505
|
getHexCellsInLine,
|
|
10233
10506
|
getHexCellsInRadius,
|
|
10507
|
+
getHexCellsInRectangle,
|
|
10234
10508
|
getHexCellsInSquare,
|
|
10235
10509
|
getHexDistance,
|
|
10236
10510
|
isNearBezier,
|