@fieldnotes/core 0.46.1 → 0.47.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 +143 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +143 -63
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3324,6 +3324,48 @@ function drawHexPath(ctx, cx, cy, cellSize, orientation) {
|
|
|
3324
3324
|
ctx.closePath();
|
|
3325
3325
|
}
|
|
3326
3326
|
|
|
3327
|
+
// src/elements/renderers/template-measure.ts
|
|
3328
|
+
function renderTemplateFeetLabel(ctx, p) {
|
|
3329
|
+
if (p.feet <= 0) return;
|
|
3330
|
+
const directional = p.templateShape === "cone" || p.templateShape === "line";
|
|
3331
|
+
const dir = directional ? p.angle : 0;
|
|
3332
|
+
const length = p.templateShape === "square" ? p.radius / 2 : p.radius;
|
|
3333
|
+
const cos = Math.cos(dir);
|
|
3334
|
+
const sin = Math.sin(dir);
|
|
3335
|
+
const start = p.position;
|
|
3336
|
+
const end = { x: start.x + length * cos, y: start.y + length * sin };
|
|
3337
|
+
const midX = start.x + length / 2 * cos;
|
|
3338
|
+
const midY = start.y + length / 2 * sin;
|
|
3339
|
+
ctx.save();
|
|
3340
|
+
ctx.globalAlpha = 1;
|
|
3341
|
+
ctx.beginPath();
|
|
3342
|
+
ctx.setLineDash([4, 4]);
|
|
3343
|
+
ctx.strokeStyle = p.color;
|
|
3344
|
+
ctx.lineWidth = 1.5;
|
|
3345
|
+
ctx.moveTo(start.x, start.y);
|
|
3346
|
+
ctx.lineTo(end.x, end.y);
|
|
3347
|
+
ctx.stroke();
|
|
3348
|
+
ctx.setLineDash([]);
|
|
3349
|
+
const label = `${Math.round(p.feet)} ft`;
|
|
3350
|
+
const fontSize = Math.max(10, Math.min(14, p.radius * 0.15));
|
|
3351
|
+
ctx.font = `bold ${fontSize}px system-ui, sans-serif`;
|
|
3352
|
+
ctx.textAlign = "center";
|
|
3353
|
+
ctx.textBaseline = "bottom";
|
|
3354
|
+
const textY = midY - 4;
|
|
3355
|
+
const metrics = ctx.measureText(label);
|
|
3356
|
+
const padX = 4;
|
|
3357
|
+
const padY = 2;
|
|
3358
|
+
const textW = metrics.width + padX * 2;
|
|
3359
|
+
const textH = fontSize + padY * 2;
|
|
3360
|
+
ctx.fillStyle = "rgba(255, 255, 255, 0.85)";
|
|
3361
|
+
ctx.beginPath();
|
|
3362
|
+
ctx.roundRect(midX - textW / 2, textY - textH, textW, textH, 3);
|
|
3363
|
+
ctx.fill();
|
|
3364
|
+
ctx.fillStyle = p.color;
|
|
3365
|
+
ctx.fillText(label, midX, textY - padY);
|
|
3366
|
+
ctx.restore();
|
|
3367
|
+
}
|
|
3368
|
+
|
|
3327
3369
|
// src/elements/renderers/template-renderer.ts
|
|
3328
3370
|
function renderTemplate(ctx, template, store) {
|
|
3329
3371
|
const grid = store?.getElementsByType("grid")[0];
|
|
@@ -3347,9 +3389,6 @@ function renderGeometricTemplate(ctx, template) {
|
|
|
3347
3389
|
ctx.arc(cx, cy, r, 0, Math.PI * 2);
|
|
3348
3390
|
ctx.fill();
|
|
3349
3391
|
ctx.stroke();
|
|
3350
|
-
if (template.radiusFeet != null && template.radiusFeet > 0) {
|
|
3351
|
-
renderRadiusMarker(ctx, cx, cy, r, template.radiusFeet);
|
|
3352
|
-
}
|
|
3353
3392
|
break;
|
|
3354
3393
|
case "square":
|
|
3355
3394
|
ctx.fillRect(cx - r / 2, cy - r / 2, r, r);
|
|
@@ -3382,6 +3421,16 @@ function renderGeometricTemplate(ctx, template) {
|
|
|
3382
3421
|
break;
|
|
3383
3422
|
}
|
|
3384
3423
|
}
|
|
3424
|
+
if (template.radiusFeet != null && template.radiusFeet > 0) {
|
|
3425
|
+
renderTemplateFeetLabel(ctx, {
|
|
3426
|
+
position: template.position,
|
|
3427
|
+
radius: template.radius,
|
|
3428
|
+
angle: template.angle,
|
|
3429
|
+
templateShape: template.templateShape,
|
|
3430
|
+
feet: template.radiusFeet,
|
|
3431
|
+
color: template.strokeColor
|
|
3432
|
+
});
|
|
3433
|
+
}
|
|
3385
3434
|
ctx.restore();
|
|
3386
3435
|
}
|
|
3387
3436
|
function renderHexTemplate(ctx, template, cellSize, orientation) {
|
|
@@ -3428,44 +3477,18 @@ function renderHexTemplate(ctx, template, cellSize, orientation) {
|
|
|
3428
3477
|
ctx.lineWidth = template.strokeWidth;
|
|
3429
3478
|
ctx.stroke();
|
|
3430
3479
|
}
|
|
3431
|
-
if (template.
|
|
3432
|
-
|
|
3433
|
-
|
|
3480
|
+
if (template.radiusFeet != null && template.radiusFeet > 0) {
|
|
3481
|
+
renderTemplateFeetLabel(ctx, {
|
|
3482
|
+
position: template.position,
|
|
3483
|
+
radius: template.radius,
|
|
3484
|
+
angle: template.angle,
|
|
3485
|
+
templateShape: template.templateShape,
|
|
3486
|
+
feet: template.radiusFeet,
|
|
3487
|
+
color: template.strokeColor
|
|
3488
|
+
});
|
|
3434
3489
|
}
|
|
3435
3490
|
ctx.restore();
|
|
3436
3491
|
}
|
|
3437
|
-
function renderRadiusMarker(ctx, cx, cy, r, feet) {
|
|
3438
|
-
const markerColor = ctx.strokeStyle;
|
|
3439
|
-
ctx.save();
|
|
3440
|
-
ctx.globalAlpha = 1;
|
|
3441
|
-
ctx.beginPath();
|
|
3442
|
-
ctx.setLineDash([4, 4]);
|
|
3443
|
-
ctx.strokeStyle = markerColor;
|
|
3444
|
-
ctx.lineWidth = 1.5;
|
|
3445
|
-
ctx.moveTo(cx, cy);
|
|
3446
|
-
ctx.lineTo(cx + r, cy);
|
|
3447
|
-
ctx.stroke();
|
|
3448
|
-
ctx.setLineDash([]);
|
|
3449
|
-
const label = `${Math.round(feet)} ft`;
|
|
3450
|
-
const fontSize = Math.max(10, Math.min(14, r * 0.15));
|
|
3451
|
-
ctx.font = `bold ${fontSize}px system-ui, sans-serif`;
|
|
3452
|
-
ctx.textAlign = "center";
|
|
3453
|
-
ctx.textBaseline = "bottom";
|
|
3454
|
-
const textX = cx + r / 2;
|
|
3455
|
-
const textY = cy - 4;
|
|
3456
|
-
const metrics = ctx.measureText(label);
|
|
3457
|
-
const padX = 4;
|
|
3458
|
-
const padY = 2;
|
|
3459
|
-
const textW = metrics.width + padX * 2;
|
|
3460
|
-
const textH = fontSize + padY * 2;
|
|
3461
|
-
ctx.fillStyle = "rgba(255, 255, 255, 0.85)";
|
|
3462
|
-
ctx.beginPath();
|
|
3463
|
-
ctx.roundRect(textX - textW / 2, textY - textH, textW, textH, 3);
|
|
3464
|
-
ctx.fill();
|
|
3465
|
-
ctx.fillStyle = markerColor;
|
|
3466
|
-
ctx.fillText(label, textX, textY - padY);
|
|
3467
|
-
ctx.restore();
|
|
3468
|
-
}
|
|
3469
3492
|
|
|
3470
3493
|
// src/elements/grid-renderer.ts
|
|
3471
3494
|
function getSquareGridLines(bounds, cellSize) {
|
|
@@ -8311,6 +8334,20 @@ function getOverlayLayout(el, zoom) {
|
|
|
8311
8334
|
const rotateHandle = rotatePoint(topMid, center2, angle);
|
|
8312
8335
|
return { center: center2, corners, rotateHandle, angle };
|
|
8313
8336
|
}
|
|
8337
|
+
function templateAimKnob(el, zoom) {
|
|
8338
|
+
if (el.type !== "template") return null;
|
|
8339
|
+
if (el.templateShape !== "cone" && el.templateShape !== "line") return null;
|
|
8340
|
+
const gap = ROTATE_HANDLE_OFFSET / zoom;
|
|
8341
|
+
const dist = el.radius + gap;
|
|
8342
|
+
const origin = el.position;
|
|
8343
|
+
return {
|
|
8344
|
+
origin,
|
|
8345
|
+
knob: {
|
|
8346
|
+
x: origin.x + dist * Math.cos(el.angle),
|
|
8347
|
+
y: origin.y + dist * Math.sin(el.angle)
|
|
8348
|
+
}
|
|
8349
|
+
};
|
|
8350
|
+
}
|
|
8314
8351
|
function getHandlePositions(bounds) {
|
|
8315
8352
|
return [
|
|
8316
8353
|
["nw", { x: bounds.x, y: bounds.y }],
|
|
@@ -8459,6 +8496,22 @@ function renderSelectionBoxes(ctx, p) {
|
|
|
8459
8496
|
handleWorldSize
|
|
8460
8497
|
);
|
|
8461
8498
|
ctx.setLineDash([4 / zoom, 4 / zoom]);
|
|
8499
|
+
if (p.selectedIds.length === 1 && (el.templateShape === "cone" || el.templateShape === "line")) {
|
|
8500
|
+
const aim = templateAimKnob(el, zoom);
|
|
8501
|
+
if (aim) {
|
|
8502
|
+
ctx.beginPath();
|
|
8503
|
+
ctx.moveTo(aim.origin.x, aim.origin.y);
|
|
8504
|
+
ctx.lineTo(aim.knob.x, aim.knob.y);
|
|
8505
|
+
ctx.stroke();
|
|
8506
|
+
ctx.setLineDash([]);
|
|
8507
|
+
ctx.fillStyle = "#ffffff";
|
|
8508
|
+
ctx.beginPath();
|
|
8509
|
+
ctx.arc(aim.knob.x, aim.knob.y, handleWorldSize / 2, 0, Math.PI * 2);
|
|
8510
|
+
ctx.fill();
|
|
8511
|
+
ctx.stroke();
|
|
8512
|
+
ctx.setLineDash([4 / zoom, 4 / zoom]);
|
|
8513
|
+
}
|
|
8514
|
+
}
|
|
8462
8515
|
}
|
|
8463
8516
|
if (p.selectedIds.length === 1 && ROTATABLE_TYPES.has(el.type)) {
|
|
8464
8517
|
const stemStart = topMidpoint(layout);
|
|
@@ -8614,6 +8667,19 @@ function hitTestTemplateResizeHandle(world, ctx, selectedIds) {
|
|
|
8614
8667
|
}
|
|
8615
8668
|
return null;
|
|
8616
8669
|
}
|
|
8670
|
+
function hitTestTemplateAimHandle(world, ctx, selectedIds) {
|
|
8671
|
+
if (selectedIds.length !== 1) return null;
|
|
8672
|
+
const id = selectedIds[0];
|
|
8673
|
+
if (!id) return null;
|
|
8674
|
+
const el = ctx.store.getById(id);
|
|
8675
|
+
if (!el || el.locked) return null;
|
|
8676
|
+
const knob = templateAimKnob(el, ctx.camera.zoom);
|
|
8677
|
+
if (!knob) return null;
|
|
8678
|
+
const r = (HANDLE_SIZE / 2 + HANDLE_HIT_PADDING2) / ctx.camera.zoom;
|
|
8679
|
+
const dx = world.x - knob.knob.x;
|
|
8680
|
+
const dy = world.y - knob.knob.y;
|
|
8681
|
+
return dx * dx + dy * dy <= r * r ? { elementId: id } : null;
|
|
8682
|
+
}
|
|
8617
8683
|
function findElementsInRect(marquee, ctx) {
|
|
8618
8684
|
const candidates = ctx.store.queryRect(marquee);
|
|
8619
8685
|
const ids = [];
|
|
@@ -8863,6 +8929,12 @@ var SelectTool = class {
|
|
|
8863
8929
|
ctx.requestRender();
|
|
8864
8930
|
return;
|
|
8865
8931
|
}
|
|
8932
|
+
const aimHit = hitTestTemplateAimHandle(world, ctx, this._selectedIds);
|
|
8933
|
+
if (aimHit) {
|
|
8934
|
+
this.mode = { type: "aiming-template", elementId: aimHit.elementId };
|
|
8935
|
+
ctx.requestRender();
|
|
8936
|
+
return;
|
|
8937
|
+
}
|
|
8866
8938
|
const rotateHit = hitTestRotateHandle(world, ctx, this._selectedIds);
|
|
8867
8939
|
if (rotateHit) {
|
|
8868
8940
|
const el = ctx.store.getById(rotateHit.elementId);
|
|
@@ -8944,6 +9016,19 @@ var SelectTool = class {
|
|
|
8944
9016
|
this.handleTemplateResize(world, ctx);
|
|
8945
9017
|
return;
|
|
8946
9018
|
}
|
|
9019
|
+
if (this.mode.type === "aiming-template") {
|
|
9020
|
+
const el = ctx.store.getById(this.mode.elementId);
|
|
9021
|
+
if (el && el.type === "template" && !el.locked) {
|
|
9022
|
+
let a = Math.atan2(world.y - el.position.y, world.x - el.position.x);
|
|
9023
|
+
if (state.shiftKey) {
|
|
9024
|
+
const snap = ctx.gridType === "hex" ? Math.PI / 3 : ROTATE_SNAP;
|
|
9025
|
+
a = Math.round(a / snap) * snap;
|
|
9026
|
+
}
|
|
9027
|
+
ctx.store.update(this.mode.elementId, { angle: normalizeAngle(a) });
|
|
9028
|
+
ctx.requestRender();
|
|
9029
|
+
}
|
|
9030
|
+
return;
|
|
9031
|
+
}
|
|
8947
9032
|
if (this.mode.type === "rotating") {
|
|
8948
9033
|
const { elementId, center: center2, startPointerAngle, startRotation } = this.mode;
|
|
8949
9034
|
const a = Math.atan2(world.y - center2.y, world.x - center2.x);
|
|
@@ -9150,6 +9235,10 @@ var SelectTool = class {
|
|
|
9150
9235
|
ctx.setCursor?.("nwse-resize");
|
|
9151
9236
|
return null;
|
|
9152
9237
|
}
|
|
9238
|
+
if (hitTestTemplateAimHandle(world, ctx, this._selectedIds)) {
|
|
9239
|
+
ctx.setCursor?.("grab");
|
|
9240
|
+
return null;
|
|
9241
|
+
}
|
|
9153
9242
|
if (hitTestRotateHandle(world, ctx, this._selectedIds)) {
|
|
9154
9243
|
ctx.setCursor?.("grab");
|
|
9155
9244
|
return null;
|
|
@@ -9819,6 +9908,7 @@ var TemplateTool = class {
|
|
|
9819
9908
|
gridType;
|
|
9820
9909
|
hexOrientation;
|
|
9821
9910
|
snapEnabled = false;
|
|
9911
|
+
feetScaleUnit = 0;
|
|
9822
9912
|
templateShape;
|
|
9823
9913
|
fillColor;
|
|
9824
9914
|
strokeColor;
|
|
@@ -9867,6 +9957,7 @@ var TemplateTool = class {
|
|
|
9867
9957
|
this.gridType = ctx.gridType;
|
|
9868
9958
|
this.hexOrientation = ctx.hexOrientation;
|
|
9869
9959
|
this.snapEnabled = !!ctx.gridType || (ctx.snapToGrid ?? false);
|
|
9960
|
+
this.feetScaleUnit = ctx.gridSize && ctx.gridSize > 0 ? ctx.gridType === "hex" ? Math.sqrt(3) * ctx.gridSize : ctx.gridSize : 0;
|
|
9870
9961
|
const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
9871
9962
|
this.origin = this.snapToGrid(world, ctx);
|
|
9872
9963
|
this.current = { ...this.origin };
|
|
@@ -9966,6 +10057,7 @@ var TemplateTool = class {
|
|
|
9966
10057
|
break;
|
|
9967
10058
|
}
|
|
9968
10059
|
}
|
|
10060
|
+
this.drawFeetLabel(ctx, radius);
|
|
9969
10061
|
ctx.restore();
|
|
9970
10062
|
}
|
|
9971
10063
|
renderHexOverlay(ctx, radius) {
|
|
@@ -10016,30 +10108,7 @@ var TemplateTool = class {
|
|
|
10016
10108
|
ctx.lineWidth = this.strokeWidth;
|
|
10017
10109
|
ctx.stroke();
|
|
10018
10110
|
}
|
|
10019
|
-
|
|
10020
|
-
const feet = radiusCells * this.feetPerCell;
|
|
10021
|
-
if (feet > 0) {
|
|
10022
|
-
ctx.globalAlpha = 1;
|
|
10023
|
-
const label = `${Math.round(feet)} ft`;
|
|
10024
|
-
const fontSize = Math.max(10, Math.min(14, radius * 0.15));
|
|
10025
|
-
ctx.font = `bold ${fontSize}px system-ui, sans-serif`;
|
|
10026
|
-
ctx.textAlign = "center";
|
|
10027
|
-
ctx.textBaseline = "bottom";
|
|
10028
|
-
const textX = center2.x;
|
|
10029
|
-
const textY = center2.y - 4;
|
|
10030
|
-
const metrics = ctx.measureText(label);
|
|
10031
|
-
const padX = 4;
|
|
10032
|
-
const padY = 2;
|
|
10033
|
-
const textW = metrics.width + padX * 2;
|
|
10034
|
-
const textH = fontSize + padY * 2;
|
|
10035
|
-
ctx.fillStyle = "rgba(255, 255, 255, 0.85)";
|
|
10036
|
-
ctx.beginPath();
|
|
10037
|
-
ctx.roundRect(textX - textW / 2, textY - textH, textW, textH, 3);
|
|
10038
|
-
ctx.fill();
|
|
10039
|
-
ctx.fillStyle = this.strokeColor;
|
|
10040
|
-
ctx.fillText(label, textX, textY - padY);
|
|
10041
|
-
}
|
|
10042
|
-
}
|
|
10111
|
+
this.drawFeetLabel(ctx, radius);
|
|
10043
10112
|
ctx.restore();
|
|
10044
10113
|
}
|
|
10045
10114
|
computeRadius() {
|
|
@@ -10057,6 +10126,17 @@ var TemplateTool = class {
|
|
|
10057
10126
|
const dy = this.current.y - this.origin.y;
|
|
10058
10127
|
return Math.atan2(dy, dx);
|
|
10059
10128
|
}
|
|
10129
|
+
drawFeetLabel(ctx, radius) {
|
|
10130
|
+
const feet = this.feetScaleUnit > 0 ? radius / this.feetScaleUnit * this.feetPerCell : 0;
|
|
10131
|
+
renderTemplateFeetLabel(ctx, {
|
|
10132
|
+
position: this.origin,
|
|
10133
|
+
radius,
|
|
10134
|
+
angle: this.computeAngle(),
|
|
10135
|
+
templateShape: this.templateShape,
|
|
10136
|
+
feet,
|
|
10137
|
+
color: this.strokeColor
|
|
10138
|
+
});
|
|
10139
|
+
}
|
|
10060
10140
|
snapToGrid(point, ctx) {
|
|
10061
10141
|
if (!ctx.gridSize) return point;
|
|
10062
10142
|
if (ctx.gridType === "hex" && ctx.hexOrientation) {
|
|
@@ -10186,7 +10266,7 @@ var LaserTool = class {
|
|
|
10186
10266
|
};
|
|
10187
10267
|
|
|
10188
10268
|
// src/index.ts
|
|
10189
|
-
var VERSION = "0.
|
|
10269
|
+
var VERSION = "0.47.0";
|
|
10190
10270
|
// Annotate the CommonJS export names for ESM import in node:
|
|
10191
10271
|
0 && (module.exports = {
|
|
10192
10272
|
ArrowTool,
|