@reekon-tools/boldr-cad 1.3.0 → 1.5.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/README.md +21 -4
- package/dist/app/edit.d.ts +3 -3
- package/dist/app/edit.js +26 -1
- package/dist/app/edit.js.map +1 -1
- package/dist/app/state.d.ts +3 -5
- package/dist/app/state.js +0 -1
- package/dist/app/state.js.map +1 -1
- package/dist/data/sync.d.ts +1 -1
- package/dist/data/sync.js +7 -2
- package/dist/data/sync.js.map +1 -1
- package/dist/editor/actions.d.ts +8 -0
- package/dist/editor/actions.js +149 -169
- package/dist/editor/actions.js.map +1 -1
- package/dist/editor/elevation.d.ts +4 -1
- package/dist/editor/elevation.js +16 -17
- package/dist/editor/elevation.js.map +1 -1
- package/dist/editor/gestures.d.ts +21 -0
- package/dist/editor/gestures.js +92 -14
- package/dist/editor/gestures.js.map +1 -1
- package/dist/editor/keys.js +2 -2
- package/dist/editor/keys.js.map +1 -1
- package/dist/editor/linkActions.d.ts +22 -3
- package/dist/editor/linkActions.js +95 -13
- package/dist/editor/linkActions.js.map +1 -1
- package/dist/editor/mode.js +4 -3
- package/dist/editor/mode.js.map +1 -1
- package/dist/editor/project.js +1 -2
- package/dist/editor/project.js.map +1 -1
- package/dist/editor/scaleGate.d.ts +13 -0
- package/dist/editor/scaleGate.js +27 -0
- package/dist/editor/scaleGate.js.map +1 -0
- package/dist/editor/selection.js +13 -9
- package/dist/editor/selection.js.map +1 -1
- package/dist/editor/tooltips.js +9 -0
- package/dist/editor/tooltips.js.map +1 -1
- package/dist/editor/view3d.d.ts +4 -1
- package/dist/editor/view3d.js +6 -2
- package/dist/editor/view3d.js.map +1 -1
- package/dist/export/pdf.d.ts +5 -2
- package/dist/export/pdf.js +12 -8
- package/dist/export/pdf.js.map +1 -1
- package/dist/export/plan.d.ts +1 -1
- package/dist/export/plan.js +14 -9
- package/dist/export/plan.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/model/demo.d.ts +1 -0
- package/dist/model/demo.js +2 -1
- package/dist/model/demo.js.map +1 -1
- package/dist/model/elevation.d.ts +1 -1
- package/dist/model/elevation.js +3 -3
- package/dist/model/elevation.js.map +1 -1
- package/dist/model/history.js +4 -1
- package/dist/model/history.js.map +1 -1
- package/dist/model/links.d.ts +4 -1
- package/dist/model/links.js +9 -2
- package/dist/model/links.js.map +1 -1
- package/dist/model/measurement.d.ts +2 -2
- package/dist/model/measurement.js +3 -3
- package/dist/model/measurement.js.map +1 -1
- package/dist/model/openings.d.ts +10 -0
- package/dist/model/openings.js +18 -0
- package/dist/model/openings.js.map +1 -1
- package/dist/model/planGeom.d.ts +37 -2
- package/dist/model/planGeom.js +72 -9
- package/dist/model/planGeom.js.map +1 -1
- package/dist/model/project.js +5 -2
- package/dist/model/project.js.map +1 -1
- package/dist/model/rooms.d.ts +18 -5
- package/dist/model/rooms.js +178 -73
- package/dist/model/rooms.js.map +1 -1
- package/dist/model/scale.d.ts +30 -0
- package/dist/model/scale.js +120 -0
- package/dist/model/scale.js.map +1 -0
- package/dist/model/settings.d.ts +0 -1
- package/dist/model/settings.js +1 -1
- package/dist/model/settings.js.map +1 -1
- package/dist/model/sketch.d.ts +21 -1
- package/dist/model/sketch.js +58 -4
- package/dist/model/sketch.js.map +1 -1
- package/dist/model/stroke.js +2 -2
- package/dist/model/types.d.ts +38 -7
- package/dist/react/BottomBar.d.ts +2 -1
- package/dist/react/BottomBar.js +94 -25
- package/dist/react/BottomBar.js.map +1 -1
- package/dist/react/Drawer.js +3 -2
- package/dist/react/Drawer.js.map +1 -1
- package/dist/react/Duck.js.map +1 -1
- package/dist/react/KeypadSheet.js.map +1 -1
- package/dist/react/MarkupPanel.js +1 -1
- package/dist/react/PlanCanvas.native.js +54 -7
- package/dist/react/PlanCanvas.native.js.map +1 -1
- package/dist/react/Popovers.js +14 -3
- package/dist/react/Popovers.js.map +1 -1
- package/dist/react/Rail.js +1 -1
- package/dist/react/Sheet.d.ts +2 -2
- package/dist/react/Sheet.js.map +1 -1
- package/dist/react/barScroll.d.ts +23 -0
- package/dist/react/barScroll.js +47 -0
- package/dist/react/barScroll.js.map +1 -0
- package/dist/react/corners.d.ts +6 -2
- package/dist/react/corners.js.map +1 -1
- package/dist/react/host.d.ts +7 -0
- package/dist/react/host.js +14 -1
- package/dist/react/host.js.map +1 -1
- package/dist/react/motion.d.ts +1 -1
- package/dist/react/motion.js +1 -1
- package/dist/react/motion.js.map +1 -1
- package/dist/render/draw.d.ts +21 -4
- package/dist/render/draw.js +184 -149
- package/dist/render/draw.js.map +1 -1
- package/dist/render/elevation.js +3 -3
- package/dist/render/elevation.js.map +1 -1
- package/dist/render/figmaIcons.d.ts +6 -0
- package/dist/render/figmaIcons.js +16 -1
- package/dist/render/figmaIcons.js.map +1 -1
- package/dist/render/hit.d.ts +8 -1
- package/dist/render/hit.js +23 -4
- package/dist/render/hit.js.map +1 -1
- package/dist/render/icons.js +27 -20
- package/dist/render/icons.js.map +1 -1
- package/dist/render/lens.d.ts +27 -0
- package/dist/render/lens.js +41 -0
- package/dist/render/lens.js.map +1 -0
- package/dist/render/linkTile.d.ts +20 -2
- package/dist/render/linkTile.js +476 -246
- package/dist/render/linkTile.js.map +1 -1
- package/dist/render/snap.d.ts +5 -1
- package/dist/render/snap.js +3 -1
- package/dist/render/snap.js.map +1 -1
- package/dist/render/svgPath.d.ts +29 -0
- package/dist/render/svgPath.js +114 -0
- package/dist/render/svgPath.js.map +1 -0
- package/dist/render/tileIcons.d.ts +18 -0
- package/dist/render/tileIcons.js +67 -0
- package/dist/render/tileIcons.js.map +1 -0
- package/dist/render/view.d.ts +18 -1
- package/dist/render/view.js +31 -4
- package/dist/render/view.js.map +1 -1
- package/dist/skia/SkiaDrawCtx.d.ts +8 -0
- package/dist/skia/SkiaDrawCtx.js +27 -5
- package/dist/skia/SkiaDrawCtx.js.map +1 -1
- package/dist/solver/equations.d.ts +6 -0
- package/dist/solver/equations.js +23 -6
- package/dist/solver/equations.js.map +1 -1
- package/dist/solver/gauss-newton.js +6 -1
- package/dist/solver/gauss-newton.js.map +1 -1
- package/dist/tools/annotate.d.ts +0 -2
- package/dist/tools/annotate.js +1 -22
- package/dist/tools/annotate.js.map +1 -1
- package/dist/tools/chamfer.js +4 -0
- package/dist/tools/chamfer.js.map +1 -1
- package/dist/tools/dim.d.ts +3 -3
- package/dist/tools/dim.js +26 -65
- package/dist/tools/dim.js.map +1 -1
- package/dist/tools/fillet.js +4 -0
- package/dist/tools/fillet.js.map +1 -1
- package/dist/tools/freehand.js +1 -4
- package/dist/tools/freehand.js.map +1 -1
- package/dist/tools/line.js +15 -11
- package/dist/tools/line.js.map +1 -1
- package/dist/tools/modify.js +6 -1
- package/dist/tools/modify.js.map +1 -1
- package/dist/tools/registry.d.ts +5 -0
- package/dist/tools/registry.js +6 -1
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/select.d.ts +19 -0
- package/dist/tools/select.js +116 -17
- package/dist/tools/select.js.map +1 -1
- package/dist/tools/shapes.d.ts +1 -1
- package/dist/tools/shapes.js +12 -5
- package/dist/tools/shapes.js.map +1 -1
- package/dist/tools/survey.d.ts +6 -0
- package/dist/tools/survey.js +28 -0
- package/dist/tools/survey.js.map +1 -1
- package/dist/view3d/build.js +9 -8
- package/dist/view3d/build.js.map +1 -1
- package/package.json +9 -9
- package/src/app/edit.ts +25 -1
- package/src/app/state.ts +3 -5
- package/src/data/sync.ts +7 -2
- package/src/editor/actions.ts +126 -112
- package/src/editor/elevation.ts +13 -16
- package/src/editor/gestures.ts +83 -13
- package/src/editor/keys.ts +2 -2
- package/src/editor/linkActions.ts +90 -13
- package/src/editor/mode.ts +4 -3
- package/src/editor/project.ts +1 -2
- package/src/editor/scaleGate.ts +32 -0
- package/src/editor/selection.ts +13 -7
- package/src/editor/tooltips.ts +8 -0
- package/src/editor/view3d.ts +6 -2
- package/src/export/pdf.ts +7 -4
- package/src/export/plan.ts +14 -10
- package/src/index.ts +1 -1
- package/src/model/demo.ts +2 -1
- package/src/model/elevation.ts +4 -4
- package/src/model/history.ts +4 -1
- package/src/model/links.ts +8 -2
- package/src/model/measurement.ts +3 -3
- package/src/model/openings.ts +18 -0
- package/src/model/planGeom.ts +70 -10
- package/src/model/project.ts +6 -2
- package/src/model/rooms.ts +151 -63
- package/src/model/scale.ts +105 -0
- package/src/model/settings.ts +1 -2
- package/src/model/sketch.ts +48 -4
- package/src/model/stroke.ts +2 -2
- package/src/model/types.ts +38 -7
- package/src/react/BottomBar.tsx +114 -29
- package/src/react/Drawer.tsx +14 -10
- package/src/react/Duck.tsx +3 -3
- package/src/react/KeypadSheet.tsx +2 -2
- package/src/react/MarkupPanel.tsx +1 -1
- package/src/react/PlanCanvas.native.tsx +60 -6
- package/src/react/Popovers.tsx +24 -9
- package/src/react/Rail.tsx +1 -1
- package/src/react/Sheet.tsx +3 -3
- package/src/react/barScroll.ts +57 -0
- package/src/react/corners.ts +6 -3
- package/src/react/host.ts +14 -1
- package/src/react/motion.tsx +6 -6
- package/src/render/draw.ts +156 -116
- package/src/render/elevation.ts +5 -4
- package/src/render/figmaIcons.ts +17 -1
- package/src/render/hit.ts +21 -4
- package/src/render/icons.ts +27 -20
- package/src/render/lens.ts +52 -0
- package/src/render/linkTile.ts +436 -190
- package/src/render/snap.ts +7 -2
- package/src/render/svgPath.ts +95 -0
- package/src/render/tileIcons.ts +75 -0
- package/src/render/view.ts +37 -5
- package/src/skia/SkiaDrawCtx.ts +26 -4
- package/src/solver/equations.ts +22 -6
- package/src/solver/gauss-newton.ts +4 -1
- package/src/styles.css +3 -2
- package/src/tools/annotate.ts +1 -18
- package/src/tools/chamfer.ts +3 -0
- package/src/tools/dim.ts +26 -52
- package/src/tools/fillet.ts +3 -0
- package/src/tools/freehand.ts +1 -3
- package/src/tools/line.ts +12 -11
- package/src/tools/modify.ts +5 -1
- package/src/tools/registry.ts +10 -1
- package/src/tools/select.ts +99 -18
- package/src/tools/shapes.ts +9 -4
- package/src/tools/survey.ts +21 -0
- package/src/ui/ctx.ts +4 -3
- package/src/ui/drawer.ts +8 -2
- package/src/view3d/build.ts +11 -10
package/src/tools/shapes.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { mainT, requestDraw, requestOverlayDraw, S } from '../render/view';
|
|
|
15
15
|
import { fmtLen } from '../units';
|
|
16
16
|
import { anchorMark, applyPending, commitPoint, lengthPreview, PT, rememberHover, wallBand, type PEv } from './registry';
|
|
17
17
|
import { toast, updateHint } from '../editor/ports';
|
|
18
|
+
import { noScaleYet } from '../editor/scaleGate';
|
|
18
19
|
import { wallBounds } from '../editor/elevation';
|
|
19
20
|
import { drawsWalls } from '../editor/mode';
|
|
20
21
|
import { splitWallsAtEnds } from './modify';
|
|
@@ -93,13 +94,15 @@ export const rectTool = {
|
|
|
93
94
|
// (a wall corner placed partway along another wall splits that one there)
|
|
94
95
|
if (wall) for (const id of sides) splitWallsAtEnds(sk, id);
|
|
95
96
|
applyPending(a.cons); applyPending(c.cons);
|
|
96
|
-
|
|
97
|
+
// its sides held level and plumb only with auto-constrain on, as any drawing's are (the user's call, 2026-10-26:
|
|
98
|
+
// "if auto constraint is disabled - it should not work on squares either"); off, it's four lines like any others
|
|
99
|
+
if (settings.autoCons) for (const k of [{ type: 'h' as const, a: a.id, b }, { type: 'v' as const, a: b, b: c.id }, { type: 'h' as const, a: d, b: c.id }, { type: 'v' as const, a: a.id, b: d }]) tryAddConstraint(k, { silent: true });
|
|
97
100
|
this.reset();
|
|
98
101
|
// (drawn, not selected: nothing new is highlighted — tap it to select it)
|
|
99
102
|
ed.sel = [];
|
|
100
103
|
afterEdit();
|
|
101
104
|
},
|
|
102
|
-
hint() { return this.st.p1 ? 'Tap the opposite corner.' : 'Tap or drag two opposite corners. Edges stay square.'; },
|
|
105
|
+
hint() { return this.st.p1 ? 'Tap the opposite corner.' : 'Tap or drag two opposite corners.' + (settings.autoCons ? ' Edges stay square.' : ''); },
|
|
103
106
|
};
|
|
104
107
|
|
|
105
108
|
export const circleTool = {
|
|
@@ -128,7 +131,8 @@ export const circleTool = {
|
|
|
128
131
|
c.restore();
|
|
129
132
|
// centre marker: crosshair + dot, always visible
|
|
130
133
|
anchorMark(c, x, y);
|
|
131
|
-
|
|
134
|
+
// (its diameter once the drawing has its scale: before, only a placeholder)
|
|
135
|
+
if (!noScaleYet()) chip(c, 'Ø ' + fmtLen(2 * r), x, y - r * T.s - 16, { bg: '#fff', fg: '#0066FF', border: ['#0066FF', 1.5] });
|
|
132
136
|
};
|
|
133
137
|
} else ed.preview = null;
|
|
134
138
|
requestOverlayDraw();
|
|
@@ -151,7 +155,8 @@ export const circleTool = {
|
|
|
151
155
|
applyPending(cp.cons);
|
|
152
156
|
const id = uid('ci');
|
|
153
157
|
SK().circles[id] = { c: cp.id, r, ...asDrawn() };
|
|
154
|
-
|
|
158
|
+
// (its edge on a point: held there with auto-constrain on, as a snap is when drawing)
|
|
159
|
+
if (s.kind === 'point' && settings.autoCons) tryAddConstraint({ type: 'oncircle', p: s.ref!, c: id }, { silent: true });
|
|
155
160
|
this.reset();
|
|
156
161
|
ed.sel = [];
|
|
157
162
|
afterEdit();
|
package/src/tools/survey.ts
CHANGED
|
@@ -256,5 +256,26 @@ export function takeSurveyPoint(x: number, y: number, capture?: unknown): boolea
|
|
|
256
256
|
return true;
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
+
/**
|
|
260
|
+
* Close the open run from the host's device (a button that means "close": BOLDR's T1R's D), as the bar's Close shape
|
|
261
|
+
* does. No point is taken: the last edge goes back to the first point from wherever the run ended. False when there's
|
|
262
|
+
* no run of 3 points or more to close, or the drawing can't take it now (the toast says which).
|
|
263
|
+
*/
|
|
264
|
+
export function closeSurveyRun(): boolean {
|
|
265
|
+
if (ed.readOnly) return false;
|
|
266
|
+
if (elevOpen()) { toast('Back out of the wall view to close the shape.', { warn: true }); return false; }
|
|
267
|
+
if (keypadOpen()) { toast('Close the keypad, then close the shape again.', { warn: true }); return false; }
|
|
268
|
+
// (a run ends when another tool is picked up, or on Annotate: its chain is kept only for the next pick-up to clear)
|
|
269
|
+
const n = ed.tool === 'survey' ? liveChain().length : 0;
|
|
270
|
+
if (n < 3) {
|
|
271
|
+
toast(n ? 'Take ' + (n === 2 ? 'one more point' : '2 more points') + ', then close the shape.' : 'No shape to close — take points with your templator first.', { warn: true });
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
// (the 3D tab keeps the run, as it keeps the tool in hand: closed on the plan, as a point there would be)
|
|
275
|
+
if (is3d()) close3d();
|
|
276
|
+
surveyTool.closeShape();
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
|
|
259
280
|
// a new session (another drawing): every page's frame and the run start again
|
|
260
281
|
onSessionReset(() => { surveyTool.st = fresh(); });
|
package/src/ui/ctx.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ed } from '../app/state';
|
|
2
2
|
import { markupTarget } from '../editor/markup';
|
|
3
|
-
import { applyCtxAction, barItems, deleteSelection, plusAction, plusLabel, plusVisible } from '../editor/actions';
|
|
3
|
+
import { applyCtxAction, barItems, deleteSelection, iconAlone, plusAction, plusLabel, plusVisible } from '../editor/actions';
|
|
4
4
|
import { selInfo, selectionHint } from '../editor/selection';
|
|
5
5
|
import { $ } from './dom';
|
|
6
6
|
import { icon } from './icons';
|
|
@@ -16,10 +16,11 @@ export function updateCtx(): void {
|
|
|
16
16
|
if (it === 'sep') { const d = document.createElement('div'); d.className = 'sep'; box.appendChild(d); continue; }
|
|
17
17
|
if ('lbl' in it) { const d = document.createElement('div'); d.className = 'lbl'; d.textContent = it.lbl; box.appendChild(d); continue; }
|
|
18
18
|
const b = document.createElement('button');
|
|
19
|
-
b.className = 'cb' + (it.on ? ' on' : '') + (it.locked ? ' locked' : '');
|
|
19
|
+
b.className = 'cb' + (it.on ? ' on' : '') + (it.locked ? ' locked' : '') + (iconAlone(it) ? ' lead' : '');
|
|
20
|
+
// (the bar's lead — Lock, or Open — and Lock wherever it is: its icon alone, its name its label for a screen reader)
|
|
20
21
|
b.innerHTML = (it.ic === 'color'
|
|
21
22
|
? `<svg width="24" height="24" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="8" fill="${markupTarget().color}" stroke="#fff" stroke-width="2"/></svg>`
|
|
22
|
-
: icon(it.ic)) + '<span>' + it.label + '</span>';
|
|
23
|
+
: icon(it.ic)) + (iconAlone(it) ? '' : '<span>' + it.label + '</span>');
|
|
23
24
|
b.setAttribute('aria-label', it.label);
|
|
24
25
|
if (it.on != null) b.setAttribute('aria-pressed', String(!!it.on));
|
|
25
26
|
if (it.locked != null) b.setAttribute('aria-pressed', String(it.locked));
|
package/src/ui/drawer.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { addPage, deletePage, deletePending, duplicatePage, focusSpace, renamePage, renameProject, renameSpace, spaceSchedule, switchPage as switchPageIn } from '../editor/project';
|
|
2
2
|
import { allPages, page, store } from '../model/store';
|
|
3
|
+
import { noScaleYet } from '../editor/scaleGate';
|
|
3
4
|
import { drawThumb } from '../render/draw';
|
|
4
5
|
import { fmtArea, fmtLen } from '../units';
|
|
5
6
|
import { $, esc } from './dom';
|
|
@@ -57,11 +58,16 @@ export function renderDrawer(): void {
|
|
|
57
58
|
} else {
|
|
58
59
|
const sched = spaceSchedule(), total = sched.total;
|
|
59
60
|
if (!sched.rows.length) { body.innerHTML = '<p class="empty">No spaces on this page yet. Close a loop of walls or lines, or pick the Space tool and tap inside a closed shape.</p>'; return; }
|
|
61
|
+
// (a sketch's sizes are placeholders until its first length: no areas until then)
|
|
62
|
+
const sketchOnly = noScaleYet();
|
|
60
63
|
let h = '';
|
|
61
64
|
for (const r of sched.rows) {
|
|
62
|
-
|
|
65
|
+
const nums = sketchOnly ? '' : `<div class="nums"><span><b>${fmtArea(r.area)}</b> floor</span><span><b>${fmtLen(r.per)}</b> perimeter</span></div>`;
|
|
66
|
+
h += `<div class="roomrow"><input class="field" data-room="${r.id}" value="${esc(r.name)}" aria-label="Space name">${nums}</div>`;
|
|
63
67
|
}
|
|
64
|
-
h +=
|
|
68
|
+
h += sketchOnly
|
|
69
|
+
? '<p class="empty">Areas show once the drawing has a length. Select a line and tap + to enter one.</p>'
|
|
70
|
+
: `<div class="row"><div><b>Total</b><small>Measured to inside wall faces</small></div><b>${fmtArea(total)}</b></div>`;
|
|
65
71
|
body.innerHTML = h;
|
|
66
72
|
body.querySelectorAll<HTMLInputElement>('[data-room]').forEach(i => {
|
|
67
73
|
i.onchange = () => renameSpace(i.dataset.room!, i.value);
|
package/src/view3d/build.ts
CHANGED
|
@@ -2,12 +2,12 @@ import { openingHeights, toU, wallHeight } from '../model/elevation';
|
|
|
2
2
|
import { faceKeyParts } from '../model/faces';
|
|
3
3
|
import { faceFrame, UPPER_Z } from '../model/facesolve';
|
|
4
4
|
import { hyp, normAng } from '../model/geom';
|
|
5
|
-
import { edgeS, openingSpan } from '../model/openings';
|
|
5
|
+
import { edgeS, hingeEdge, openingSpan } from '../model/openings';
|
|
6
6
|
import { curveFrame, wallThick } from '../model/planGeom';
|
|
7
7
|
import { roomGeom } from '../model/rooms';
|
|
8
8
|
import { settings } from '../model/settings';
|
|
9
9
|
import { roomValid, withLineIndex } from '../model/sketch';
|
|
10
|
-
import type { Opening, Sketch, XY } from '../model/types';
|
|
10
|
+
import type { Opening, OpeningEdge, Sketch, XY } from '../model/types';
|
|
11
11
|
import { wallPieces } from '../model/wallshape';
|
|
12
12
|
import { boxLines, cellLines, mergeIv, raiseIn, type Cell3 } from './lines';
|
|
13
13
|
import { PartBuilder, type Mat3, type Part3, type V3 } from './mesh';
|
|
@@ -59,7 +59,7 @@ export function inFront(cut: Cut3, m: XY, n: XY): boolean {
|
|
|
59
59
|
|
|
60
60
|
interface Wall3 { id: string; A: XY; B: XY; u: XY; L: number; t: number; h: number; full: number }
|
|
61
61
|
|
|
62
|
-
interface Opening3 { id: string; type: Opening['type']; s0: number; s1: number; sill: number; head: number; flip: boolean }
|
|
62
|
+
interface Opening3 { id: string; type: Opening['type']; s0: number; s1: number; sill: number; head: number; flip: boolean; hinge: OpeningEdge }
|
|
63
63
|
|
|
64
64
|
/** The plan's straight walls, each at its height (cut down when it's in front of a cutaway). */
|
|
65
65
|
function straightWalls(sk: Sketch, cut: Cut3 | null, lowered: string[]): Map<string, Wall3> {
|
|
@@ -150,7 +150,7 @@ function openingsIn(sk: Sketch, w: Wall3, mine: [string, Opening][]): Opening3[]
|
|
|
150
150
|
const s0 = Math.max(0, Math.min(a, b)), s1 = Math.min(w.L, Math.max(a, b));
|
|
151
151
|
if (s1 - s0 < 0.01) continue;
|
|
152
152
|
const { sill, head } = openingHeights(o);
|
|
153
|
-
out.push({ id, type: o.type, s0, s1, sill: Math.min(sill, w.h), head: Math.min(head, w.h), flip: !!o.flip });
|
|
153
|
+
out.push({ id, type: o.type, s0, s1, sill: Math.min(sill, w.h), head: Math.min(head, w.h), flip: !!o.flip, hinge: hingeEdge(o) });
|
|
154
154
|
}
|
|
155
155
|
return out.sort((p, q) => p.s0 - q.s0);
|
|
156
156
|
}
|
|
@@ -243,8 +243,8 @@ const FRAME = 0.75, CASING = 2.5, CASING_OUT = 0.625, LEAF = 1.375, OPEN = (30 *
|
|
|
243
243
|
|
|
244
244
|
/**
|
|
245
245
|
* A door, so it reads as one: the floor run on through the doorway, a frame lining its sides and head, casings round
|
|
246
|
-
* it on both faces (as high as the wall goes), and its leaf —
|
|
247
|
-
* plan draws it, and open a little. Its outlines go on `lines`.
|
|
246
|
+
* it on both faces (as high as the wall goes), and its leaf — hung on its hinge's jamb (Opening.hinge) on the face it
|
|
247
|
+
* swings to, as the plan draws it, and open a little. Its outlines go on `lines`.
|
|
248
248
|
*/
|
|
249
249
|
function doorPart(w: Wall3, o: Opening3, lines: number[]): PartBuilder {
|
|
250
250
|
const b = new PartBuilder('opening', o.id), half = w.t / 2, head = Math.min(o.head, w.h), nx = -w.u.y, ny = w.u.x;
|
|
@@ -267,12 +267,13 @@ function doorPart(w: Wall3, o: Opening3, lines: number[]): PartBuilder {
|
|
|
267
267
|
box(rect(o.s1, o.s1 + CASING, k0, k1), 0, top, 'frame');
|
|
268
268
|
box(rect(o.s0, o.s1, k0, k1), head, top, 'frame');
|
|
269
269
|
}
|
|
270
|
-
const f = o.flip ? -1 : 1, W = o.s1 - o.s0 - 2 * FRAME, H = head - FRAME - 0.5;
|
|
270
|
+
const f = o.flip ? -1 : 1, m = o.hinge === 's1' ? -1 : 1, W = o.s1 - o.s0 - 2 * FRAME, H = head - FRAME - 0.5;
|
|
271
271
|
if (W > 2 && H > 2) {
|
|
272
|
-
// (turned about its hinge, at the frame on the face it swings to, toward that face
|
|
273
|
-
|
|
272
|
+
// (turned about its hinge, at the frame on the face it swings to, toward that face; it runs from its hinge's jamb
|
|
273
|
+
// to the other: along the wall, or back along it, `m`)
|
|
274
|
+
const hs = m > 0 ? o.s0 + FRAME : o.s1 - FRAME, hk = f * half, c = Math.cos(m * f * OPEN), sn = Math.sin(m * f * OPEN);
|
|
274
275
|
const R = (s: number, k: number): XY => P(hs + (s - hs) * c - (k - hk) * sn, hk + (s - hs) * sn + (k - hk) * c);
|
|
275
|
-
box([R(hs, hk), R(hs + W, hk), R(hs + W, hk - f * LEAF), R(hs, hk - f * LEAF)], 0.5, 0.5 + H, 'door');
|
|
276
|
+
box([R(hs, hk), R(hs + m * W, hk), R(hs + m * W, hk - f * LEAF), R(hs, hk - f * LEAF)], 0.5, 0.5 + H, 'door');
|
|
276
277
|
}
|
|
277
278
|
return b;
|
|
278
279
|
}
|