@reekon-tools/boldr-cad 1.6.0 → 1.8.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.
Files changed (87) hide show
  1. package/README.md +12 -1
  2. package/dist/data/provider.d.ts +2 -1
  3. package/dist/data/sync.js +4 -1
  4. package/dist/data/sync.js.map +1 -1
  5. package/dist/editor/actions.js +26 -2
  6. package/dist/editor/actions.js.map +1 -1
  7. package/dist/editor/advance.d.ts +2 -0
  8. package/dist/editor/advance.js +23 -0
  9. package/dist/editor/advance.js.map +1 -0
  10. package/dist/editor/dimSize.d.ts +13 -0
  11. package/dist/editor/dimSize.js +50 -0
  12. package/dist/editor/dimSize.js.map +1 -0
  13. package/dist/editor/gestures.js +30 -7
  14. package/dist/editor/gestures.js.map +1 -1
  15. package/dist/editor/keypad.d.ts +8 -0
  16. package/dist/editor/keypad.js +15 -1
  17. package/dist/editor/keypad.js.map +1 -1
  18. package/dist/editor/laser.d.ts +9 -0
  19. package/dist/editor/laser.js +38 -12
  20. package/dist/editor/laser.js.map +1 -1
  21. package/dist/editor/linkActions.d.ts +5 -1
  22. package/dist/editor/linkActions.js +27 -5
  23. package/dist/editor/linkActions.js.map +1 -1
  24. package/dist/editor/ports.d.ts +9 -0
  25. package/dist/editor/ports.js +2 -0
  26. package/dist/editor/ports.js.map +1 -1
  27. package/dist/editor/refDim.d.ts +17 -0
  28. package/dist/editor/refDim.js +62 -0
  29. package/dist/editor/refDim.js.map +1 -0
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.js +1 -1
  32. package/dist/index.js.map +1 -1
  33. package/dist/model/clockwise.d.ts +11 -0
  34. package/dist/model/clockwise.js +123 -0
  35. package/dist/model/clockwise.js.map +1 -0
  36. package/dist/model/settings.d.ts +2 -0
  37. package/dist/model/settings.js +1 -1
  38. package/dist/model/settings.js.map +1 -1
  39. package/dist/model/types.d.ts +5 -0
  40. package/dist/react/BottomBar.js +43 -3
  41. package/dist/react/BottomBar.js.map +1 -1
  42. package/dist/react/PlanCanvas.native.js +15 -9
  43. package/dist/react/PlanCanvas.native.js.map +1 -1
  44. package/dist/react/host.d.ts +6 -0
  45. package/dist/react/host.js +11 -1
  46. package/dist/react/host.js.map +1 -1
  47. package/dist/react/ports.js +2 -1
  48. package/dist/react/ports.js.map +1 -1
  49. package/dist/react/store.d.ts +1 -1
  50. package/dist/react/store.js.map +1 -1
  51. package/dist/render/draw.d.ts +8 -0
  52. package/dist/render/draw.js +164 -92
  53. package/dist/render/draw.js.map +1 -1
  54. package/dist/render/navigate.d.ts +11 -6
  55. package/dist/render/navigate.js +34 -20
  56. package/dist/render/navigate.js.map +1 -1
  57. package/dist/tools/dim.d.ts +15 -0
  58. package/dist/tools/dim.js +171 -45
  59. package/dist/tools/dim.js.map +1 -1
  60. package/dist/tools/line.js +5 -0
  61. package/dist/tools/line.js.map +1 -1
  62. package/package.json +1 -1
  63. package/src/data/provider.ts +2 -2
  64. package/src/data/sync.ts +4 -1
  65. package/src/editor/actions.ts +20 -2
  66. package/src/editor/advance.ts +23 -0
  67. package/src/editor/dimSize.ts +53 -0
  68. package/src/editor/gestures.ts +31 -9
  69. package/src/editor/keypad.ts +17 -1
  70. package/src/editor/laser.ts +38 -10
  71. package/src/editor/linkActions.ts +25 -6
  72. package/src/editor/ports.ts +10 -0
  73. package/src/editor/refDim.ts +61 -0
  74. package/src/index.ts +1 -2
  75. package/src/model/clockwise.ts +121 -0
  76. package/src/model/settings.ts +3 -1
  77. package/src/model/types.ts +5 -0
  78. package/src/react/BottomBar.tsx +46 -2
  79. package/src/react/PlanCanvas.native.tsx +25 -12
  80. package/src/react/host.ts +11 -1
  81. package/src/react/ports.ts +3 -2
  82. package/src/react/store.ts +1 -1
  83. package/src/render/draw.ts +131 -71
  84. package/src/render/navigate.ts +31 -20
  85. package/src/tools/dim.ts +168 -35
  86. package/src/tools/line.ts +4 -0
  87. package/src/ui/domPorts.ts +2 -0
@@ -46,43 +46,57 @@ export function shiftTo(s, ox, oy, to, W, H) {
46
46
  export const NAV_DOWN = 0, NAV_MOVE = 1, NAV_UP = 2, NAV_CANCEL = 3;
47
47
  /**
48
48
  * One touch event, as the UI thread sees it: the fingers after it, and the view (moved if `moved`). Fingers navigate —
49
- * pan, and zoom about their middle — once two are down, or any one once a pen has been seen or when the drawing is
50
- * view only (editor/gestures.ts decides the same on its side), until the last one lifts. A pen or a mouse never
51
- * navigates. `type` is NAV_DOWN… ; `allIds`, every pointer still down (on a down event: a finger that went without
52
- * a word, a lost touch-up, is dropped so it can't hold a gesture open).
49
+ * pan, and zoom about their middle — once two are down, or a finger lands while a Pencil is down or when the drawing is
50
+ * view only (editor/gestures.ts decides the same on its side), until the last one lifts. A finger already down and not
51
+ * navigating when a Pencil lands is set aside until it lifts. A pen or a mouse never navigates. `type` is NAV_DOWN… ;
52
+ * `kind` is the event's pointer, for a touch that doesn't say what it is itself; `allIds`, every pointer still down (on
53
+ * a down event: a finger that went without a word, a lost touch-up, is dropped so it can't hold a gesture open).
53
54
  */
54
55
  export function navTouch(st, type, kind, changed, allIds, readOnly, view, drawn) {
55
56
  'worklet';
56
- const pen = st.pen || kind === 'pen';
57
- if (kind !== 'touch')
58
- return { st: { ...st, pen }, view, moved: false };
57
+ const lost = (id) => type === NAV_DOWN && !!allIds && !allIds.includes(id);
59
58
  const fingers = {};
60
- for (const k in st.fingers) {
61
- const id = Number(k);
62
- if (type !== NAV_DOWN || !allIds || allIds.includes(id))
63
- fingers[id] = st.fingers[id];
64
- }
59
+ for (const k in st.fingers)
60
+ if (!lost(Number(k)))
61
+ fingers[Number(k)] = st.fingers[Number(k)];
62
+ let pens = st.pens.filter(id => !lost(id)), landed = false, moved = false;
65
63
  for (const t of changed) {
66
- if (type === NAV_UP || type === NAV_CANCEL)
64
+ const k = t.kind ?? kind;
65
+ if (type === NAV_UP || type === NAV_CANCEL) {
67
66
  delete fingers[t.id];
68
- else
67
+ pens = pens.filter(id => id !== t.id);
68
+ }
69
+ else if (type === NAV_DOWN && k === 'pen') {
70
+ if (!pens.includes(t.id))
71
+ pens = [...pens, t.id];
72
+ // (fingers down and not navigating are let go of: their moves and lifts are no finger's from here)
73
+ if (!st.navigating)
74
+ for (const f in fingers)
75
+ delete fingers[f];
76
+ }
77
+ else if (type === NAV_DOWN ? k === 'touch' : t.id in fingers) {
69
78
  fingers[t.id] = { x: t.x, y: t.y };
79
+ if (type === NAV_DOWN)
80
+ landed = true;
81
+ else
82
+ moved = true;
83
+ }
70
84
  }
71
85
  const pts = Object.keys(fingers).map(k => fingers[Number(k)]);
72
86
  if (!pts.length)
73
- return { st: { fingers, navigating: false, last: null, pen }, view, moved: false };
87
+ return { st: { fingers, navigating: false, last: null, pens }, view, moved: false };
74
88
  let navigating = st.navigating, v = view;
75
89
  if (!navigating) {
76
- if (pts.length < 2 && !(type === NAV_DOWN && (pen || readOnly)))
77
- return { st: { fingers, navigating: false, last: null, pen }, view, moved: false };
90
+ if (pts.length < 2 && !(landed && (pens.length > 0 || readOnly)))
91
+ return { st: { fingers, navigating: false, last: null, pens }, view, moved: false };
78
92
  navigating = true;
79
93
  // start from the view the drawing on screen was drawn at, unless the fingers' own is newer
80
94
  if (drawn && v.seq <= drawn.seq)
81
95
  v = { s: drawn.view.s, cx: drawn.view.cx, cy: drawn.view.cy, seq: drawn.seq };
82
96
  }
83
- const now = pinchOf(pts), was = st.last, next = { fingers, navigating, last: now, pen };
84
- // a finger came or went: carry on from here, no jump
85
- if (type !== NAV_MOVE || !now || !was || was.n !== now.n || !drawn)
97
+ const now = pinchOf(pts), was = st.last, next = { fingers, navigating, last: now, pens };
98
+ // a finger came or went: carry on from here, no jump (and a Pencil's move alone moves no view)
99
+ if (type !== NAV_MOVE || !moved || !now || !was || was.n !== now.n || !drawn)
86
100
  return { st: next, view: v, moved: false };
87
101
  const to = navStep(v, was, now, drawn.W, drawn.H);
88
102
  return { st: next, view: { s: to.s, cx: to.cx, cy: to.cy, seq: v.seq + 1 }, moved: true };
@@ -1 +1 @@
1
- {"version":3,"file":"navigate.js","sourceRoot":"","sources":["../../src/render/navigate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"navigate.js","sourceRoot":"","sources":["../../src/render/navigate.ts"],"names":[],"mappings":"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"}
@@ -26,6 +26,12 @@ export declare const dimTool: {
26
26
  * along its centerline where there's no space; the keypad can switch it.
27
27
  */
28
28
  export declare function dimLine(lid: string, side?: 1 | -1): void;
29
+ /**
30
+ * Whether giving dimension `c` the value `v` replaces a value measured there: a driving one, entered (not a default
31
+ * nobody measured), changed by it. Not one linked to a measurement of the job: that's asked about anyway
32
+ * (askLinkedChange). What a host warns about before a reading overwrites (laserReadingOverwrites).
33
+ */
34
+ export declare const replacesMeasured: (c: Constraint | undefined, v: number) => boolean;
29
35
  /** Why a measurement of the job can't drive a line the drawing already sets the length of. */
30
36
  export declare const LENGTH_SET_ALREADY = "The drawing already sets that line\u2019s length. Free it first (make one of its dimensions a reference), then pick the measurement.";
31
37
  /** Why a measurement of the job can't drive an angle the drawing already sets. */
@@ -52,6 +58,15 @@ export declare function onChoice(c: Constraint, ch: FaceChoice): boolean;
52
58
  export declare function setDimFace(id: string, ch: FaceChoice): void;
53
59
  /** From a wall's edge to a point: square to the wall, from that face. */
54
60
  export declare function dimEdgePoint(lid: string, side: 1 | -1, p: string): void;
61
+ /**
62
+ * What the yellow + measures for the selection, as the dimension it would make (`c`, its value unset), with the one
63
+ * already there for it (`existing`), which the + edits instead. Null where it makes no dimension of the plan: nothing it
64
+ * measures, a door's or window's sizes (their own dimensions), a wall's thickness, the wall view (wallPlus).
65
+ */
66
+ export declare function selectionDim(): {
67
+ c: Constraint;
68
+ existing?: string;
69
+ } | null;
55
70
  /**
56
71
  * Two things picked, at least one a wall's edge: the distance between them — two edges of one wall, its thickness
57
72
  * (`thickness` opens it); of two walls, face to face; an edge and a line, from that face; an edge and a point, from it.
package/dist/tools/dim.js CHANGED
@@ -20,7 +20,8 @@ import { solver } from '../solver';
20
20
  import { fmtAng, fmtLen } from '../units';
21
21
  import { addOpeningDim } from './openings';
22
22
  import { keypadFresh, keypadOpen, keypadSetReading } from '../editor/keypad';
23
- import { cornerLines } from '../editor/selection';
23
+ import { advanceAfterReading } from '../editor/advance';
24
+ import { cornerLines, edgePair, selInfo } from '../editor/selection';
24
25
  import { closeKeypad, kpTyped, openKeypad, showConflict, toast, updateCtx, updateHint } from '../editor/ports';
25
26
  import { noScaleYet, pendingLabel, startingValue } from '../editor/scaleGate';
26
27
  export const dimTool = {
@@ -237,7 +238,7 @@ export function dimLine(lid, side) {
237
238
  editDim(ex, undefined, false, wall && side ? { face: side } : undefined);
238
239
  return;
239
240
  }
240
- let face = wall ? side ?? defaultFace(sk, lid, ed.wallTap?.id === lid ? ed.wallTap.side : undefined) ?? 0 : 0;
241
+ let face = wall ? firstFace(sk, lid, side) : 0;
241
242
  const len = () => (face ? faceLength(sk, lid, face) : lineLen(sk, l));
242
243
  // (while it's being entered, what it measures is what's highlighted: the edge, or the wall)
243
244
  const was = ed.sel;
@@ -251,8 +252,13 @@ export function dimLine(lid, side) {
251
252
  keypadSetReading(len()); },
252
253
  })) : undefined,
253
254
  onClose: () => { ed.sel = was; updateCtx(); requestDraw(); },
254
- onDone: (v, capture) => { if (v > 0)
255
- addLength(v, capture !== undefined ? { capture } : {}); },
255
+ onDone: (v, capture) => {
256
+ if (!(v > 0))
257
+ return;
258
+ // a laser reading's (it comes with its record) moves on to the next line clockwise; a typed one stays
259
+ if (addLength(v, capture !== undefined ? { capture } : {}) && capture !== undefined)
260
+ advanceAfterReading(lid, side);
261
+ },
256
262
  // (or a length of the job's, from its list: the line takes it, linked to it)
257
263
  measurements: offersJobMeasurements() ? {
258
264
  kind: 'length', pick: t => {
@@ -264,22 +270,31 @@ export function dimLine(lid, side) {
264
270
  },
265
271
  } : undefined,
266
272
  };
267
- /** The line's length dimension, driving, measured where the keypad says (`extra`: what it's from). */
273
+ /** The line's length dimension, driving, measured where the keypad says (`extra`: what it's from). Whether it took. */
268
274
  function addLength(v, extra) {
269
275
  pushHistory();
270
- const r = tryAddConstraint({ type: 'dist', a: l.a, b: l.b, kind: 'al', value: v, line: lid, driving: true, off: face ? (face * 30) / V().s : 0, ...(face ? { face } : {}), ...extra });
276
+ const r = tryAddConstraint({ ...lengthDim(sk, lid, face), value: v, ...extra });
271
277
  // a measurement of the job drives a line, or nothing: not one whose length the drawing already sets
278
+ let took = r.ok;
272
279
  if (r.ok && r.ref && extra.measurementRef) {
273
280
  delete SK().cons[r.id];
274
281
  dropHistory();
275
282
  toast(LENGTH_SET_ALREADY, { warn: true });
283
+ took = false;
276
284
  }
277
285
  else if (!r.ok)
278
286
  dropHistory();
279
287
  afterEdit();
288
+ return took;
280
289
  }
281
290
  openKeypad(spec);
282
291
  }
292
+ /**
293
+ * Whether giving dimension `c` the value `v` replaces a value measured there: a driving one, entered (not a default
294
+ * nobody measured), changed by it. Not one linked to a measurement of the job: that's asked about anyway
295
+ * (askLinkedChange). What a host warns about before a reading overwrites (laserReadingOverwrites).
296
+ */
297
+ export const replacesMeasured = (c, v) => !!c && c.driving !== false && !c.auto && !c.measurementRef && changesValue(c, v);
283
298
  /** Why a measurement of the job can't drive a line the drawing already sets the length of. */
284
299
  export const LENGTH_SET_ALREADY = 'The drawing already sets that line’s length. Free it first (make one of its dimensions a reference), then pick the measurement.';
285
300
  /** Why a measurement of the job can't drive an angle the drawing already sets. */
@@ -452,8 +467,96 @@ export function dimEdgePoint(lid, side, p) {
452
467
  toast('Pick a point off the wall to measure from its edge.', { warn: true });
453
468
  return;
454
469
  }
455
- dimPointLine(p, lid, { faces: [faceToward(sk, lid, sk.points[p], side), 0], title: 'Distance from the edge' });
470
+ dimPointLine(p, lid, { faces: edgePointFaces(sk, lid, side, p), title: 'Distance from the edge' });
471
+ }
472
+ /** From a wall's edge to a point: measured from that face of the wall. */
473
+ const edgePointFaces = (sk, lid, side, p) => [faceToward(sk, lid, sk.points[p], side), 0];
474
+ /* What the + measures, as a dimension not made yet: the builders above make these, and the grey number shows them. */
475
+ /**
476
+ * The face a wall's length (a curved wall's diameter) is first measured on: `side`, the edge picked; picked whole, the
477
+ * side a space is on (the side it was tapped on, between two); its centreline with no space beside it.
478
+ */
479
+ const firstFace = (sk, id, side) => side ?? defaultFace(sk, id, ed.wallTap?.id === id ? ed.wallTap.side : undefined) ?? 0;
480
+ /** Line `lid`'s length, measured on `face` (a wall's; 0 along the line). Its label sits off the side it's measured on. */
481
+ function lengthDim(sk, lid, face) {
482
+ const l = sk.lines[lid];
483
+ return { type: 'dist', a: l.a, b: l.b, kind: 'al', line: lid, driving: true, off: face ? (face * 30) / V().s : 0, ...(face ? { face } : {}) };
484
+ }
485
+ /** A circle's or arc's diameter, across `face` (a curved wall's; 0 its centreline). */
486
+ const diamDim = (id, face) => ({ type: 'diam', c: id, driving: true, ...(face ? { face } : {}) });
487
+ /** A circle's or arc's diameter dimension, if it has one. */
488
+ const diamDimOf = (sk, id) => Object.keys(sk.cons).find(k => sk.cons[k].type === 'diam' && sk.cons[k].c === id);
489
+ /** The angle between two lines. */
490
+ const angleDim = (l1, l2) => ({ type: 'angle', l1, l2, value: 0, driving: true });
491
+ /** The straight distance between two points. */
492
+ const pointsDim = (a, b) => ({ type: 'dist', a, b, kind: 'al', driving: true, off: 0 });
493
+ /**
494
+ * Point `p`'s distance from line `lid`, square to it, on its side of it. On a wall, from `faces[0]` (see dimPointLine) —
495
+ * by default the face toward the point where a space is against it; `l2`, the wall the point is the end of.
496
+ */
497
+ function pointLineDim(sk, p, lid, opts = {}) {
498
+ const l = sk.lines[lid], P = sk.points[p], A = sk.points[l.a], B = sk.points[l.b];
499
+ const sd = ((B.x - A.x) * (P.y - A.y) - (B.y - A.y) * (P.x - A.x)) / (hyp(B.x - A.x, B.y - A.y) || 1);
500
+ const c = { type: 'pldist', p, l: lid, value: 0, sign: sd < 0 ? -1 : 1, driving: true, ...(opts.l2 ? { l2: opts.l2 } : {}) };
501
+ applyFace(c, { faces: opts.faces ?? [faceToward(sk, lid, P), 0] });
502
+ return c;
503
+ }
504
+ /**
505
+ * What the yellow + measures for the selection, as the dimension it would make (`c`, its value unset), with the one
506
+ * already there for it (`existing`), which the + edits instead. Null where it makes no dimension of the plan: nothing it
507
+ * measures, a door's or window's sizes (their own dimensions), a wall's thickness, the wall view (wallPlus).
508
+ */
509
+ export function selectionDim() {
510
+ const sk = SK(), s = selInfo();
511
+ if (s.dims.length || s.odims.length || s.openings.length || s.oanchors.length)
512
+ return null;
513
+ if (s.wfaces.length === 1 && s.n === 1) {
514
+ const [id, side] = edgeOf(s.wfaces[0]);
515
+ if (sk.lines[id])
516
+ return { c: lengthDim(sk, id, side), existing: lengthDimOf(sk, id) ?? undefined };
517
+ return { c: diamDim(id, side), existing: diamDimOf(sk, id) };
518
+ }
519
+ if (edgePair(s)) {
520
+ const edges = s.wfaces.map(edgeOf), other = ed.sel.find(x => x.type !== 'wface');
521
+ if (edges.length === 2) {
522
+ const [[a, sa], [b, sb]] = edges;
523
+ return a !== b && sk.lines[a] && sk.lines[b] ? twoLinesDim(sk, a, b, { [a]: sa, [b]: sb }) : null;
524
+ }
525
+ const [lid, side] = edges[0], l = sk.lines[lid];
526
+ if (other?.type === 'line')
527
+ return twoLinesDim(sk, lid, other.id, { [lid]: side });
528
+ if (other?.type !== 'point' || !l || other.id === l.a || other.id === l.b)
529
+ return null;
530
+ return { c: pointLineDim(sk, other.id, lid, { faces: edgePointFaces(sk, lid, side, other.id) }), existing: pointLineDimOf(sk, other.id, lid) };
531
+ }
532
+ if (s.lines.length === 1 && s.n === 1) {
533
+ const lid = s.lines[0];
534
+ return { c: lengthDim(sk, lid, faced(sk, lid) ? firstFace(sk, lid) : 0), existing: lengthDimOf(sk, lid) ?? undefined };
535
+ }
536
+ if (s.curves.length === 1 && s.n === 1) {
537
+ const id = s.curves[0];
538
+ return { c: diamDim(id, wallOf(sk, id) ? firstFace(sk, id) : 0), existing: diamDimOf(sk, id) };
539
+ }
540
+ if (s.points.length === 1 && s.n === 1)
541
+ return cornerDim(sk, s.points[0]);
542
+ if (s.n === 2 && s.lines.length === 2)
543
+ return twoLinesDim(sk, s.lines[0], s.lines[1]);
544
+ if (s.n === 2 && s.points.length === 2) {
545
+ const [a, b] = s.points;
546
+ const existing = Object.keys(sk.cons).find(id => { const c = sk.cons[id]; return c.type === 'dist' && !c.line && ((c.a === a && c.b === b) || (c.a === b && c.b === a)); });
547
+ return { c: pointsDim(a, b), existing };
548
+ }
549
+ if (s.n === 2 && s.points.length === 1 && s.lines.length === 1) {
550
+ const [p] = s.points, [lid] = s.lines, l = sk.lines[lid];
551
+ // (one of the line's own ends is no distance from it)
552
+ if (!l || p === l.a || p === l.b)
553
+ return null;
554
+ return { c: pointLineDim(sk, p, lid), existing: pointLineDimOf(sk, p, lid) };
555
+ }
556
+ return null;
456
557
  }
558
+ /** A distance from point `p` to line `lid` already in the drawing. */
559
+ const pointLineDimOf = (sk, p, lid) => Object.keys(sk.cons).find(id => sk.cons[id].type === 'pldist' && sk.cons[id].p === p && sk.cons[id].l === lid);
457
560
  /**
458
561
  * Two things picked, at least one a wall's edge: the distance between them — two edges of one wall, its thickness
459
562
  * (`thickness` opens it); of two walls, face to face; an edge and a line, from that face; an edge and a point, from it.
@@ -491,7 +594,7 @@ export function dimPoints(a, b) {
491
594
  title: 'Distance', ...startingValue(L), selectAll: true, laser: true,
492
595
  onDone: (v, capture) => {
493
596
  pushHistory();
494
- const r = tryAddConstraint({ type: 'dist', a, b, kind: 'al', value: v, driving: true, off: 0, ...(capture !== undefined ? { capture } : {}) });
597
+ const r = tryAddConstraint({ ...pointsDim(a, b), value: v, ...(capture !== undefined ? { capture } : {}) });
495
598
  if (!r.ok)
496
599
  dropHistory();
497
600
  ed.sel = [];
@@ -506,9 +609,32 @@ export function dimPoints(a, b) {
506
609
  * or, picked whole, each wall's room side (see faceToward).
507
610
  */
508
611
  export function dimTwoLines(l1, l2, sides = {}) {
509
- const sk = SK(), L1 = sk.lines[l1], L2 = sk.lines[l2];
510
- if (!L1 || !L2)
612
+ const sk = SK(), d = twoLinesDim(sk, l1, l2, sides);
613
+ if (!d)
511
614
  return;
615
+ if (d.existing) {
616
+ const c = sk.cons[d.existing];
617
+ // (the edges picked: it's measured between them now)
618
+ editDim(d.existing, undefined, false, c.type === 'pldist' && Object.keys(sides).length ? d.facesFrom(c.l, c.p) : undefined);
619
+ return;
620
+ }
621
+ if (d.c.type === 'angle') {
622
+ dimAngle(l1, l2);
623
+ return;
624
+ }
625
+ const f = d.facesFrom(l1, d.c.p);
626
+ dimPointLine(d.c.p, l1, { keepParallel: l2, title: 'Distance between lines', faces: f.faces, l2: f.l2 });
627
+ }
628
+ /**
629
+ * What the + measures between two lines: their distance if they're parallel — square to the first, from the end of the
630
+ * second that sits most squarely across from it, between the faces `sides` says (see dimTwoLines) — otherwise the angle
631
+ * between them. `existing`: a dimension already between them; `facesFrom`: the faces between line `l` and the other,
632
+ * from point `p` on the other.
633
+ */
634
+ function twoLinesDim(sk, l1, l2, sides = {}) {
635
+ const L1 = sk.lines[l1], L2 = sk.lines[l2];
636
+ if (!L1 || !L2)
637
+ return null;
512
638
  const [ax, ay] = norm2(sk.points[L1.b].x - sk.points[L1.a].x, sk.points[L1.b].y - sk.points[L1.a].y);
513
639
  const [bx, by] = norm2(sk.points[L2.b].x - sk.points[L2.a].x, sk.points[L2.b].y - sk.points[L2.a].y);
514
640
  const parallel = Math.abs(ax * by - ay * bx) < 0.02; // within about a degree
@@ -521,27 +647,17 @@ export function dimTwoLines(l1, l2, sides = {}) {
521
647
  return (c.l1 === l1 && c.l2 === l2) || (c.l1 === l2 && c.l2 === l1);
522
648
  return (c.l === l1 && (c.p === L2.a || c.p === L2.b)) || (c.l === l2 && (c.p === L1.a || c.p === L1.b));
523
649
  });
524
- /** The faces between line `l` (the one measured square to) and the other, from point `p` on the other. */
525
650
  const facesFrom = (l, p) => {
526
651
  const o = l === l1 ? l2 : l1, ol = sk.lines[o], on = sk.lines[l];
527
652
  return { faces: [faceToward(sk, l, sk.points[p], sides[l], true), faceToward(sk, o, sk.points[on.a], sides[o], true)], ...(faced(sk, o) && ol ? { l2: o } : {}) };
528
653
  };
529
- if (existing) {
530
- const c = sk.cons[existing];
531
- // (the edges picked: it's measured between them now)
532
- editDim(existing, undefined, false, c.type === 'pldist' && Object.keys(sides).length ? facesFrom(c.l, c.p) : undefined);
533
- return;
534
- }
535
- if (!parallel) {
536
- dimAngle(l1, l2);
537
- return;
538
- }
539
- // measure from the end of the second line that sits most squarely across from the first
654
+ if (!parallel)
655
+ return { c: angleDim(l1, l2), existing, facesFrom };
540
656
  const A = sk.points[L1.a], B = sk.points[L1.b], len2 = (B.x - A.x) ** 2 + (B.y - A.y) ** 2 || 1;
541
657
  const t = (pid) => { const P = sk.points[pid]; return ((P.x - A.x) * (B.x - A.x) + (P.y - A.y) * (B.y - A.y)) / len2; };
542
658
  const p = Math.abs(t(L2.a) - 0.5) <= Math.abs(t(L2.b) - 0.5) ? L2.a : L2.b;
543
659
  const f = facesFrom(l1, p);
544
- dimPointLine(p, l1, { keepParallel: l2, title: 'Distance between lines', faces: f.faces, l2: f.l2 });
660
+ return { c: pointLineDim(sk, p, l1, { faces: f.faces, l2: f.l2 }), existing, facesFrom };
545
661
  }
546
662
  /**
547
663
  * The distance from point `p` to line `lid`, square to it. On a wall it's measured from `faces[0]`, its face toward
@@ -550,11 +666,7 @@ export function dimTwoLines(l1, l2, sides = {}) {
550
666
  * switch them.
551
667
  */
552
668
  export function dimPointLine(p, lid, opts = {}) {
553
- const sk = SK(), l = sk.lines[lid], P = sk.points[p], A = sk.points[l.a], B = sk.points[l.b];
554
- const sd = ((B.x - A.x) * (P.y - A.y) - (B.y - A.y) * (P.x - A.x)) / (hyp(B.x - A.x, B.y - A.y) || 1);
555
- const sign = sd < 0 ? -1 : 1;
556
- const c = { type: 'pldist', p, l: lid, value: 0, sign, driving: true, ...(opts.l2 ? { l2: opts.l2 } : {}) };
557
- applyFace(c, { faces: opts.faces ?? [faceToward(sk, lid, P), 0] });
669
+ const sk = SK(), c = pointLineDim(sk, p, lid, opts);
558
670
  const choices = faceChoices(sk, c);
559
671
  ed.sel = [];
560
672
  ed.preview = (cx, T) => { const e = dimEnds(sk, c); if (e)
@@ -590,15 +702,13 @@ export function dimPointLine(p, lid, opts = {}) {
590
702
  */
591
703
  export function dimCurve(id, side) {
592
704
  const sk = SK(), wall = !!wallOf(sk, id);
593
- for (const k in sk.cons) {
594
- const c = sk.cons[k];
595
- if (c.type === 'diam' && c.c === id) {
596
- editDim(k, undefined, false, wall && side ? { face: side } : undefined);
597
- return;
598
- }
705
+ const ex = diamDimOf(sk, id);
706
+ if (ex) {
707
+ editDim(ex, undefined, false, wall && side ? { face: side } : undefined);
708
+ return;
599
709
  }
600
- let face = wall ? side ?? defaultFace(sk, id, ed.wallTap?.id === id ? ed.wallTap.side : undefined) ?? 0 : 0;
601
- const now = () => measure(sk, { type: 'diam', c: id, ...(face ? { face } : {}) });
710
+ let face = wall ? firstFace(sk, id, side) : 0;
711
+ const now = () => measure(sk, diamDim(id, face));
602
712
  // (while it's being entered, what it measures across is what's highlighted: the edge, or the wall)
603
713
  const was = ed.sel, type = sk.circles[id] ? 'circle' : 'arc';
604
714
  const show = () => { ed.sel = face ? [{ type: 'wface', id: edgeId(id, face) }] : [{ type, id }]; requestDraw(); };
@@ -615,7 +725,7 @@ export function dimCurve(id, side) {
615
725
  if (v <= 0)
616
726
  return;
617
727
  pushHistory();
618
- const r = tryAddConstraint({ type: 'diam', c: id, value: v, driving: true, ...(face ? { face } : {}) });
728
+ const r = tryAddConstraint({ ...diamDim(id, face), value: v });
619
729
  if (!r.ok)
620
730
  dropHistory();
621
731
  // (as a length: the dimension made, nothing left highlighted)
@@ -627,19 +737,26 @@ export function dimCurve(id, side) {
627
737
  }
628
738
  /** A corner's angle (two lines meeting at `pid`): its angle dimension's keypad, or a new one's. False if it isn't a corner. */
629
739
  export function cornerAngle(pid, done, modeless = false) {
630
- const sk = SK(), pair = cornerLines(sk, pid);
631
- if (!pair)
740
+ const sk = SK(), d = cornerDim(sk, pid);
741
+ if (!d)
632
742
  return false;
633
743
  // edit the corner's existing angle dimension rather than stacking a second one
634
- const ex = Object.keys(sk.cons).find(id => { const c = sk.cons[id]; return c.type === 'angle' && ((c.l1 === pair[0] && c.l2 === pair[1]) || (c.l1 === pair[1] && c.l2 === pair[0])); });
635
- if (ex)
636
- editDim(ex, done, modeless);
744
+ if (d.existing)
745
+ editDim(d.existing, done, modeless);
637
746
  else
638
- dimAngle(pair[0], pair[1], done, modeless);
747
+ dimAngle(d.c.l1, d.c.l2, done, modeless);
639
748
  return true;
640
749
  }
750
+ /** A corner's angle (two lines meeting at `pid`), as the + measures it; `existing`, its angle dimension. Null if it isn't a corner. */
751
+ function cornerDim(sk, pid) {
752
+ const pair = cornerLines(sk, pid);
753
+ if (!pair)
754
+ return null;
755
+ const existing = Object.keys(sk.cons).find(id => { const c = sk.cons[id]; return c.type === 'angle' && ((c.l1 === pair[0] && c.l2 === pair[1]) || (c.l1 === pair[1] && c.l2 === pair[0])); });
756
+ return { c: angleDim(pair[0], pair[1]), existing };
757
+ }
641
758
  export function dimAngle(l1, l2, done, modeless = false) {
642
- const c = { type: 'angle', l1, l2, value: 0, driving: true };
759
+ const c = angleDim(l1, l2);
643
760
  const cur = measure(SK(), c);
644
761
  /** The angle dimension, driving, at `v` degrees (`extra`: what it's from). */
645
762
  const addAngle = (v, extra) => {
@@ -700,7 +817,16 @@ export function editDim(id, done, modeless = false, pick) {
700
817
  onPress: () => { face = onChoice(c, ch) ? null : { ...ch, l2: face?.l2 ?? ch.l2 }; if (keypadFresh())
701
818
  keypadSetReading(now()); },
702
819
  })),
703
- onDone: (v, capture) => { setDimValue(id, v * k, face ?? undefined, { capture }); done?.(); },
820
+ onDone: (v, capture) => {
821
+ const took = setDimValue(id, v * k, face ?? undefined, { capture });
822
+ done?.();
823
+ // a laser reading's (it comes with its record) on a line's length or a curve's diameter moves on to the next
824
+ // line clockwise; a typed one, or one the drawing refused or asked about, stays
825
+ const of = c.type === 'dist' ? c.line : c.type === 'diam' ? c.c : undefined;
826
+ if (took && capture !== undefined && of)
827
+ advanceAfterReading(of, pick?.face || undefined);
828
+ },
829
+ overwrites: v => replacesMeasured(SK().cons[id], v * k),
704
830
  // a line's length or an angle: or one of the job's, from its list — the dimension takes it, linked to it (a
705
831
  // dimension already linked to another, re-linked)
706
832
  measurements: ((c.type === 'dist' && c.line) || isAng) && offersJobMeasurements() ? {