brep-io-kernel 1.0.0 → 1.0.11
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 +16 -3
- package/package.json +5 -1
- package/dist-kernel/help/CONTRIBUTING.html +0 -248
- package/dist-kernel/help/LICENSE.html +0 -248
- package/dist-kernel/help/MODELING.png +0 -0
- package/dist-kernel/help/PMI.png +0 -0
- package/dist-kernel/help/SKETCH.png +0 -0
- package/dist-kernel/help/assembly-constraints__Coincident_Constraint_dialog.png +0 -0
- package/dist-kernel/help/assembly-constraints___Angle_Constraint_dialog.png +0 -0
- package/dist-kernel/help/assembly-constraints___Distance_Constraint_dialog.png +0 -0
- package/dist-kernel/help/assembly-constraints___Fixed_Constraint_dialog.png +0 -0
- package/dist-kernel/help/assembly-constraints___Parallel_Constraint_dialog.png +0 -0
- package/dist-kernel/help/assembly-constraints___Touch_Align_Constraint_dialog.png +0 -0
- package/dist-kernel/help/assembly-constraints__angle-constraint.html +0 -248
- package/dist-kernel/help/assembly-constraints__coincident-constraint.html +0 -248
- package/dist-kernel/help/assembly-constraints__distance-constraint.html +0 -248
- package/dist-kernel/help/assembly-constraints__fixed-constraint.html +0 -248
- package/dist-kernel/help/assembly-constraints__parallel-constraint.html +0 -248
- package/dist-kernel/help/assembly-constraints__solver.html +0 -248
- package/dist-kernel/help/assembly-constraints__touch-align-constraint.html +0 -248
- package/dist-kernel/help/brep-api.html +0 -263
- package/dist-kernel/help/brep-kernel.html +0 -258
- package/dist-kernel/help/brep-model.html +0 -248
- package/dist-kernel/help/cylindrical-face-radius-embedding.html +0 -290
- package/dist-kernel/help/dialog-screenshots.html +0 -248
- package/dist-kernel/help/extruded-sketch-radius-embedding.html +0 -336
- package/dist-kernel/help/features__Assembly_Component_dialog.png +0 -0
- package/dist-kernel/help/features__Boolean_dialog.png +0 -0
- package/dist-kernel/help/features__Chamfer_dialog.png +0 -0
- package/dist-kernel/help/features__Datium_dialog.png +0 -0
- package/dist-kernel/help/features__Extrude_dialog.png +0 -0
- package/dist-kernel/help/features__Fillet_dialog.png +0 -0
- package/dist-kernel/help/features__Helix_dialog.png +0 -0
- package/dist-kernel/help/features__Hole_dialog.png +0 -0
- package/dist-kernel/help/features__Image_Heightmap_Solid_dialog.png +0 -0
- package/dist-kernel/help/features__Image_to_Face_dialog.png +0 -0
- package/dist-kernel/help/features__Import_3D_Model_dialog.png +0 -0
- package/dist-kernel/help/features__Loft_dialog.png +0 -0
- package/dist-kernel/help/features__Mirror_dialog.png +0 -0
- package/dist-kernel/help/features__Offset_Shell_dialog.png +0 -0
- package/dist-kernel/help/features__Overlap_Cleanup_dialog.png +0 -0
- package/dist-kernel/help/features__Pattern_Linear_dialog.png +0 -0
- package/dist-kernel/help/features__Pattern_Radial_dialog.png +0 -0
- package/dist-kernel/help/features__Pattern_dialog.png +0 -0
- package/dist-kernel/help/features__Plane_dialog.png +0 -0
- package/dist-kernel/help/features__Primitive_Cone_dialog.png +0 -0
- package/dist-kernel/help/features__Primitive_Cube_dialog.png +0 -0
- package/dist-kernel/help/features__Primitive_Cylinder_dialog.png +0 -0
- package/dist-kernel/help/features__Primitive_Pyramid_dialog.png +0 -0
- package/dist-kernel/help/features__Primitive_Sphere_dialog.png +0 -0
- package/dist-kernel/help/features__Primitive_Torus_dialog.png +0 -0
- package/dist-kernel/help/features__Remesh_dialog.png +0 -0
- package/dist-kernel/help/features__Revolve_dialog.png +0 -0
- package/dist-kernel/help/features__Sheet_Metal_Contour_Flange_dialog.png +0 -0
- package/dist-kernel/help/features__Sheet_Metal_Cutout_dialog.png +0 -0
- package/dist-kernel/help/features__Sheet_Metal_Flange_dialog.png +0 -0
- package/dist-kernel/help/features__Sheet_Metal_Tab_dialog.png +0 -0
- package/dist-kernel/help/features__Sketch_dialog.png +0 -0
- package/dist-kernel/help/features__Spline_dialog.png +0 -0
- package/dist-kernel/help/features__Sweep_dialog.png +0 -0
- package/dist-kernel/help/features__Transform_dialog.png +0 -0
- package/dist-kernel/help/features__Tube_dialog.png +0 -0
- package/dist-kernel/help/features__assembly-component.html +0 -248
- package/dist-kernel/help/features__boolean.html +0 -248
- package/dist-kernel/help/features__chamfer.html +0 -248
- package/dist-kernel/help/features__datium.html +0 -248
- package/dist-kernel/help/features__datum.html +0 -248
- package/dist-kernel/help/features__extrude.html +0 -248
- package/dist-kernel/help/features__fillet.html +0 -248
- package/dist-kernel/help/features__helix.html +0 -248
- package/dist-kernel/help/features__hole.html +0 -248
- package/dist-kernel/help/features__image-heightmap-solid.html +0 -248
- package/dist-kernel/help/features__image-to-face-2D_dialog.png +0 -0
- package/dist-kernel/help/features__image-to-face-3D_dialog.png +0 -0
- package/dist-kernel/help/features__image-to-face.html +0 -248
- package/dist-kernel/help/features__import-3d-model.html +0 -248
- package/dist-kernel/help/features__index.html +0 -248
- package/dist-kernel/help/features__loft.html +0 -248
- package/dist-kernel/help/features__mirror.html +0 -248
- package/dist-kernel/help/features__offset-shell.html +0 -248
- package/dist-kernel/help/features__pattern-linear.html +0 -248
- package/dist-kernel/help/features__pattern-radial.html +0 -248
- package/dist-kernel/help/features__pattern.html +0 -248
- package/dist-kernel/help/features__plane.html +0 -248
- package/dist-kernel/help/features__primitive-cone.html +0 -248
- package/dist-kernel/help/features__primitive-cube.html +0 -248
- package/dist-kernel/help/features__primitive-cylinder.html +0 -248
- package/dist-kernel/help/features__primitive-pyramid.html +0 -248
- package/dist-kernel/help/features__primitive-sphere.html +0 -248
- package/dist-kernel/help/features__primitive-torus.html +0 -248
- package/dist-kernel/help/features__remesh.html +0 -248
- package/dist-kernel/help/features__revolve.html +0 -248
- package/dist-kernel/help/features__sheet-metal-contour-flange.html +0 -248
- package/dist-kernel/help/features__sheet-metal-flange.html +0 -248
- package/dist-kernel/help/features__sheet-metal-tab.html +0 -248
- package/dist-kernel/help/features__sketch.html +0 -248
- package/dist-kernel/help/features__spline.html +0 -248
- package/dist-kernel/help/features__sweep.html +0 -248
- package/dist-kernel/help/features__transform.html +0 -248
- package/dist-kernel/help/features__tube.html +0 -248
- package/dist-kernel/help/file-formats.html +0 -248
- package/dist-kernel/help/getting-started.html +0 -248
- package/dist-kernel/help/highlights.html +0 -248
- package/dist-kernel/help/history-systems.html +0 -248
- package/dist-kernel/help/how-it-works.html +0 -248
- package/dist-kernel/help/index.html +0 -862
- package/dist-kernel/help/input-params-schema.html +0 -363
- package/dist-kernel/help/inspector-improvements.html +0 -248
- package/dist-kernel/help/inspector.html +0 -248
- package/dist-kernel/help/modes__modeling.html +0 -248
- package/dist-kernel/help/modes__pmi.html +0 -248
- package/dist-kernel/help/modes__sketch.html +0 -248
- package/dist-kernel/help/plugins.html +0 -248
- package/dist-kernel/help/pmi-annotations__Angle_Dimension_dialog.png +0 -0
- package/dist-kernel/help/pmi-annotations__Explode_Body_dialog.png +0 -0
- package/dist-kernel/help/pmi-annotations__Hole_Callout_dialog.png +0 -0
- package/dist-kernel/help/pmi-annotations__Leader_dialog.png +0 -0
- package/dist-kernel/help/pmi-annotations__Linear_Dimension_dialog.png +0 -0
- package/dist-kernel/help/pmi-annotations__Note_dialog.png +0 -0
- package/dist-kernel/help/pmi-annotations__Radial_Dimension_dialog.png +0 -0
- package/dist-kernel/help/pmi-annotations__angle-dimension.html +0 -248
- package/dist-kernel/help/pmi-annotations__explode-body.html +0 -248
- package/dist-kernel/help/pmi-annotations__hole-callout.html +0 -248
- package/dist-kernel/help/pmi-annotations__index.html +0 -248
- package/dist-kernel/help/pmi-annotations__leader.html +0 -248
- package/dist-kernel/help/pmi-annotations__linear-dimension.html +0 -248
- package/dist-kernel/help/pmi-annotations__note.html +0 -248
- package/dist-kernel/help/pmi-annotations__radial-dimension.html +0 -248
- package/dist-kernel/help/search-index.json +0 -464
- package/dist-kernel/help/simplified-radial-dimensions.html +0 -298
- package/dist-kernel/help/solid-methods.html +0 -359
- package/dist-kernel/help/table-of-contents.html +0 -330
- package/dist-kernel/help/ui-overview.html +0 -248
- package/dist-kernel/help/whats-new.html +0 -248
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
6
|
-
<title>Distance Constraint - BREP</title>
|
|
7
|
-
<style>
|
|
8
|
-
:root{
|
|
9
|
-
--bg:#0b0f14; --panel:#0f141b; --text:#d7dde6; --muted:#9aa7b2;
|
|
10
|
-
--border:#1b2430; --accent:#5cc8ff; --chip:#121823;
|
|
11
|
-
}
|
|
12
|
-
*{box-sizing:border-box}
|
|
13
|
-
html,body{margin:0;background:var(--bg);color:var(--text);font:14px/1.5 ui-monospace,SFMono-Regular,Menlo,Consolas,'Liberation Mono','Courier New',monospace}
|
|
14
|
-
main{max-width:1100px;margin:0 auto;padding:28px}
|
|
15
|
-
h1{margin:0 0 18px;font-size:22px;color:var(--accent);font-weight:700}
|
|
16
|
-
.summary{color:var(--muted);margin-bottom:22px}
|
|
17
|
-
.card{background:var(--panel);border:1px solid var(--border);border-radius:14px;padding:18px;margin:0 0 18px}
|
|
18
|
-
.readme{padding:0}
|
|
19
|
-
.readme .header{padding:16px 18px;border-bottom:1px solid var(--border)}
|
|
20
|
-
.readme .content{padding:18px}
|
|
21
|
-
.doc-card{padding:18px}
|
|
22
|
-
.doc-nav{margin:0 0 18px;display:flex;gap:12px;flex-wrap:wrap;align-items:center;color:var(--muted)}
|
|
23
|
-
.doc-nav-links{display:flex;gap:12px;flex-wrap:wrap;align-items:center}
|
|
24
|
-
.doc-nav a{color:var(--accent);font-weight:600}
|
|
25
|
-
.doc-list{list-style:none;margin:18px 0;padding:0}
|
|
26
|
-
.doc-list li{margin:6px 0}
|
|
27
|
-
.doc-list a{color:var(--accent)}
|
|
28
|
-
.nav-search{margin-left:auto;position:relative;display:flex;align-items:center;gap:8px;flex-wrap:wrap;justify-content:flex-end;min-width:240px;max-width:520px}
|
|
29
|
-
.search-title{font-weight:700;font-size:14px;margin-bottom:4px}
|
|
30
|
-
.search-hint{color:var(--muted);font-size:12px;margin:0}
|
|
31
|
-
.search-input-row{display:flex;gap:8px;align-items:center;width:100%}
|
|
32
|
-
.search-input{flex:1;border:1px solid var(--border);border-radius:10px;background:var(--chip);color:var(--text);padding:10px 12px}
|
|
33
|
-
.search-input:focus{outline:1px solid var(--accent);box-shadow:0 0 0 3px rgba(92,200,255,0.15)}
|
|
34
|
-
.search-clear{border:1px solid var(--border);background:var(--panel);color:var(--muted);border-radius:10px;padding:9px 12px;cursor:pointer;font-weight:600}
|
|
35
|
-
.search-clear:hover{border-color:var(--accent);color:var(--accent)}
|
|
36
|
-
.search-status{color:var(--muted);font-size:12px;margin-top:4px;width:100%;text-align:right}
|
|
37
|
-
.search-results{position:absolute;top:100%;right:0;margin-top:10px;border:1px solid var(--border);border-radius:12px;background:var(--panel);max-height:360px;overflow:auto;min-width:320px;max-width:520px;box-shadow:0 12px 30px rgba(0,0,0,0.35);z-index:25}
|
|
38
|
-
.search-results ul{list-style:none;margin:0;padding:0}
|
|
39
|
-
.search-item{padding:10px 12px;border-bottom:1px solid var(--border)}
|
|
40
|
-
.search-item:last-child{border-bottom:none}
|
|
41
|
-
.search-item a{display:block;color:var(--accent);font-weight:600}
|
|
42
|
-
.search-snippet{color:var(--muted);font-size:13px;margin-top:4px}
|
|
43
|
-
.search-results mark{background:rgba(92,200,255,0.2);color:var(--text);border-radius:4px;padding:0 2px}
|
|
44
|
-
.prose h1{font-size:24px;margin:0 0 12px}
|
|
45
|
-
.prose h2{font-size:18px;margin:18px 0 8px}
|
|
46
|
-
.prose h3{font-size:16px;margin:14px 0 6px}
|
|
47
|
-
.prose p{margin:0 0 10px}
|
|
48
|
-
.prose ul,.prose ol{margin:0 0 10px 18px}
|
|
49
|
-
.prose li{margin:4px 0}
|
|
50
|
-
.prose code{background:#0d1520;border:1px solid var(--border);padding:1px 5px;border-radius:6px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,'Liberation Mono','Courier New',monospace;font-size:12px}
|
|
51
|
-
.prose pre{background:#0d1520;border:1px solid var(--border);padding:12px;border-radius:12px;overflow:auto}
|
|
52
|
-
.prose a{color:var(--accent)}
|
|
53
|
-
.prose img{max-width:100%;height:auto;}
|
|
54
|
-
.prose table{border-collapse:collapse;width:100%;margin:16px 0;background:var(--panel);border:1px solid var(--border);border-radius:8px;overflow:hidden}
|
|
55
|
-
.prose th,.prose td{padding:8px 12px;text-align:left;border-bottom:1px solid var(--border)}
|
|
56
|
-
.prose th{background:var(--chip);color:var(--text);font-weight:600;font-size:13px}
|
|
57
|
-
.prose tr:last-child td{border-bottom:none}
|
|
58
|
-
.prose tbody tr:hover{background:rgba(92,200,255,0.05)}
|
|
59
|
-
.license{
|
|
60
|
-
background:var(--panel);border:1px solid var(--border);border-radius:14px;
|
|
61
|
-
padding:16px 16px 8px;margin:0 0 18px;
|
|
62
|
-
}
|
|
63
|
-
.license > h2{margin:0 0 10px;font-size:16px;font-weight:700}
|
|
64
|
-
.pkg{
|
|
65
|
-
border-top:1px solid var(--border);padding:10px 0;
|
|
66
|
-
display:grid;grid-template-columns:1fr auto;gap:10px;align-items:center;
|
|
67
|
-
}
|
|
68
|
-
.pkg:first-of-type{border-top:none}
|
|
69
|
-
.pkg .meta{display:flex;gap:10px;flex-wrap:wrap}
|
|
70
|
-
.pkg .name{font-weight:600}
|
|
71
|
-
.pkg .desc{color:var(--muted);margin-top:2px}
|
|
72
|
-
a{color:var(--accent);text-decoration:none} a:hover{text-decoration:underline}
|
|
73
|
-
.chip{background:var(--chip);border:1px solid var(--border);border-radius:999px;padding:2px 8px;font-size:12px;color:var(--muted)}
|
|
74
|
-
.footer{margin-top:26px;color:var(--muted);font-size:12px}
|
|
75
|
-
</style>
|
|
76
|
-
</head>
|
|
77
|
-
<body>
|
|
78
|
-
<main>
|
|
79
|
-
<nav class="doc-nav">
|
|
80
|
-
<div class="doc-nav-links">
|
|
81
|
-
<a href="index.html">Help Home</a><span>·</span><a href="/help/table-of-contents.html">Table of Contents</a><span>·</span><a href="https://github.com/mmiscool/BREP" target="_blank" rel="noopener noreferrer">GitHub</a>
|
|
82
|
-
</div>
|
|
83
|
-
<div class="nav-search">
|
|
84
|
-
<div class="search-input-row">
|
|
85
|
-
<input type="search" id="doc-search" class="search-input" placeholder="Search docs... Type at least 2 characters to search" autocomplete="off" spellcheck="false" />
|
|
86
|
-
<button type="button" class="search-clear" id="doc-search-clear">Clear</button>
|
|
87
|
-
</div>
|
|
88
|
-
<div class="search-status" id="doc-search-status"></div>
|
|
89
|
-
<div class="search-results" id="doc-search-results" hidden>
|
|
90
|
-
<ul id="doc-search-list"></ul>
|
|
91
|
-
</div>
|
|
92
|
-
</div>
|
|
93
|
-
</nav>
|
|
94
|
-
<section class="card doc-card">
|
|
95
|
-
|
|
96
|
-
<div class="prose">
|
|
97
|
-
<h1>Distance Constraint</h1><p>Status: Implemented</p><p><img src="assembly-constraints___Distance_Constraint_dialog.png" alt="Distance Constraint dialog" width="280" loading="lazy" /></p><p>Distance constraints maintain a target separation between two references. They support point/point, point/edge, point/face, edge/edge, and face/face combinations and will translate components until the measured offset matches the requested distance.</p><h2>Inputs</h2><ul><li><code>id</code> – unique label used across the solver UI and logs.</li><li><code>elements</code> – two references (faces, edges, or vertices). Component references are treated as points at their representative location.</li><li><code>distance</code> – desired separation in scene units. Negative input is clamped to zero.</li><li><code>opposeNormals</code> – for face-to-face alignment, flips the normal on the second selection before the parallel alignment stage.</li></ul><h2>Behaviour</h2><ul><li>If both selections are faces, the solver first delegates to <code>solveParallelAlignment()</code> to make the faces parallel. It pauses the distance stage while orientation adjustments are pending.</li><li>Measures the current separation with specialised routines for different selection pairings (for example perpendicular distance for edges or signed offset along the face normal).</li><li>Records the absolute error in <code>persistentData.error</code> and honours the solver tolerance to decide when the constraint is satisfied.</li><li>When movement is required, splits the correction between components unless one of them reports <code>isComponentFixed(component) === true</code>.</li><li>Applies all translations through <code>context.applyTranslation()</code> and keeps a log of moves in <code>lastAppliedMoves</code> (useful for UI tooltips or debugging).</li><li>In debug mode the constraint emits temporary arrow helpers that show the resolved surface normals being used during the orientation stage.</li></ul><h2>Usage Tips</h2><ul><li>Combine Distance with Parallel when you need to hold two planar faces at a controlled offset.</li><li>Toggle <code>opposeNormals</code> if the alignment stage happens with both faces pointing the same way when you expected an opposed configuration.</li><li>Large jumps are easier to converge when you reduce the solver <code>translationGain</code>; increase it back toward 1.0 for the final tightening iterations.</li></ul>
|
|
98
|
-
</div>
|
|
99
|
-
</section>
|
|
100
|
-
</main>
|
|
101
|
-
<script>
|
|
102
|
-
(() => {
|
|
103
|
-
const searchInput = document.getElementById("doc-search");
|
|
104
|
-
const resultsBox = document.getElementById("doc-search-results");
|
|
105
|
-
const resultsList = document.getElementById("doc-search-list");
|
|
106
|
-
const statusEl = document.getElementById("doc-search-status");
|
|
107
|
-
const clearBtn = document.getElementById("doc-search-clear");
|
|
108
|
-
if (!searchInput || !resultsBox || !resultsList || !statusEl || !clearBtn) return;
|
|
109
|
-
const indexUrl = "./search-index.json";
|
|
110
|
-
let indexPromise = null;
|
|
111
|
-
let cachedEntries = null;
|
|
112
|
-
|
|
113
|
-
const escapeHtml = (text = "") =>
|
|
114
|
-
text
|
|
115
|
-
.replace(/&/g, "&")
|
|
116
|
-
.replace(/</g, "<")
|
|
117
|
-
.replace(/>/g, ">");
|
|
118
|
-
|
|
119
|
-
const escapeRegExp = (text = "") => text.replace(/[.*+?^{}()|[]\$]/g, "\$&");
|
|
120
|
-
|
|
121
|
-
const highlight = (text, terms) => {
|
|
122
|
-
if (!terms.length) return escapeHtml(text);
|
|
123
|
-
const pattern = terms.map(escapeRegExp).join("|");
|
|
124
|
-
const regex = new RegExp(`(${pattern})`, "gi");
|
|
125
|
-
return escapeHtml(text).replace(regex, "<mark>$1</mark>");
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
const buildSnippet = (entry, terms) => {
|
|
129
|
-
const source = entry.content || entry.summary || "";
|
|
130
|
-
if (!source) return { plain: "", highlighted: "" };
|
|
131
|
-
let hit = source.length;
|
|
132
|
-
for (const term of terms) {
|
|
133
|
-
const idx = entry.contentLower.indexOf(term);
|
|
134
|
-
if (idx !== -1 && idx < hit) hit = idx;
|
|
135
|
-
}
|
|
136
|
-
if (!Number.isFinite(hit) || hit === source.length) hit = 0;
|
|
137
|
-
const start = Math.max(0, hit - 60);
|
|
138
|
-
const end = Math.min(source.length, hit + 140);
|
|
139
|
-
const snippet = source.slice(start, end);
|
|
140
|
-
return { plain: snippet, highlighted: highlight(snippet, terms) };
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const updateStatus = (message) => {
|
|
144
|
-
statusEl.textContent = message;
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
const loadIndex = async () => {
|
|
148
|
-
if (cachedEntries) return cachedEntries;
|
|
149
|
-
if (!indexPromise) {
|
|
150
|
-
indexPromise = fetch(indexUrl, { cache: "no-store" })
|
|
151
|
-
.then((res) => {
|
|
152
|
-
if (!res.ok) throw new Error("Failed to load search index");
|
|
153
|
-
return res.json();
|
|
154
|
-
})
|
|
155
|
-
.then((json) => {
|
|
156
|
-
cachedEntries = Array.isArray(json)
|
|
157
|
-
? json.map((entry) => ({
|
|
158
|
-
...entry,
|
|
159
|
-
contentLower: (entry.content || "").toLowerCase(),
|
|
160
|
-
titleLower: (entry.title || "").toLowerCase(),
|
|
161
|
-
}))
|
|
162
|
-
: [];
|
|
163
|
-
return cachedEntries;
|
|
164
|
-
})
|
|
165
|
-
.catch((err) => {
|
|
166
|
-
console.error("Search index load failed", err);
|
|
167
|
-
cachedEntries = [];
|
|
168
|
-
throw err;
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
return indexPromise;
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
const renderResults = (entries, terms) => {
|
|
175
|
-
if (!terms.length) {
|
|
176
|
-
resultsBox.hidden = true;
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
if (!entries.length) {
|
|
180
|
-
resultsList.innerHTML = '<li class="search-item"><div class="search-snippet">No matches found.</div></li>';
|
|
181
|
-
resultsBox.hidden = false;
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
const rendered = entries
|
|
185
|
-
.slice(0, 30)
|
|
186
|
-
.map((entry) => {
|
|
187
|
-
const snippet = buildSnippet(entry, terms);
|
|
188
|
-
const title = highlight(entry.title || entry.href, terms);
|
|
189
|
-
const hasFragment = snippet.plain && snippet.plain.trim().length > 0;
|
|
190
|
-
const fragment = hasFragment ? `#:~:text=${encodeURIComponent(snippet.plain.trim())}` : "";
|
|
191
|
-
const hrefWithFragment = hasFragment
|
|
192
|
-
? (entry.href.includes("#") ? `${entry.href}&:~:text=${encodeURIComponent(snippet.plain.trim())}` : `${entry.href}${fragment}`)
|
|
193
|
-
: entry.href;
|
|
194
|
-
const snippetHtml = snippet.highlighted ? `<div class="search-snippet">${snippet.highlighted}</div>` : "";
|
|
195
|
-
return `<li class="search-item"><a href="${hrefWithFragment}">${title}</a>${snippetHtml}</li>`;
|
|
196
|
-
})
|
|
197
|
-
.join("");
|
|
198
|
-
resultsList.innerHTML = rendered;
|
|
199
|
-
resultsBox.hidden = false;
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
const performSearch = async () => {
|
|
203
|
-
const rawQuery = searchInput.value.trim();
|
|
204
|
-
if (rawQuery.length < 2) {
|
|
205
|
-
updateStatus("Type at least 2 characters to search.");
|
|
206
|
-
resultsBox.hidden = true;
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
const terms = rawQuery.toLowerCase().split(/\s+/).filter(Boolean);
|
|
210
|
-
updateStatus("Searching...");
|
|
211
|
-
try {
|
|
212
|
-
const entries = await loadIndex();
|
|
213
|
-
const matches = entries.filter((entry) =>
|
|
214
|
-
terms.every((term) => entry.contentLower.includes(term) || entry.titleLower.includes(term))
|
|
215
|
-
);
|
|
216
|
-
renderResults(matches, terms);
|
|
217
|
-
updateStatus(matches.length ? `${matches.length} result${matches.length === 1 ? "" : "s"} for "${rawQuery}"` : "No matches found.");
|
|
218
|
-
} catch (err) {
|
|
219
|
-
updateStatus("Search unavailable (could not load index).");
|
|
220
|
-
}
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
searchInput.addEventListener("input", performSearch);
|
|
224
|
-
|
|
225
|
-
searchInput.addEventListener("focus", () => {
|
|
226
|
-
if (!cachedEntries) {
|
|
227
|
-
loadIndex().catch(() => updateStatus("Search unavailable (could not load index)."));
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
clearBtn.addEventListener("click", () => {
|
|
232
|
-
searchInput.value = "";
|
|
233
|
-
resultsBox.hidden = true;
|
|
234
|
-
updateStatus("Type at least 2 characters to search.");
|
|
235
|
-
searchInput.focus();
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
document.addEventListener("keydown", (evt) => {
|
|
239
|
-
if (evt.key === "Escape" && document.activeElement === searchInput) {
|
|
240
|
-
searchInput.value = "";
|
|
241
|
-
resultsBox.hidden = true;
|
|
242
|
-
updateStatus("Type at least 2 characters to search.");
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
})();
|
|
246
|
-
</script>
|
|
247
|
-
</body>
|
|
248
|
-
</html>
|
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
6
|
-
<title>Fixed Constraint - BREP</title>
|
|
7
|
-
<style>
|
|
8
|
-
:root{
|
|
9
|
-
--bg:#0b0f14; --panel:#0f141b; --text:#d7dde6; --muted:#9aa7b2;
|
|
10
|
-
--border:#1b2430; --accent:#5cc8ff; --chip:#121823;
|
|
11
|
-
}
|
|
12
|
-
*{box-sizing:border-box}
|
|
13
|
-
html,body{margin:0;background:var(--bg);color:var(--text);font:14px/1.5 ui-monospace,SFMono-Regular,Menlo,Consolas,'Liberation Mono','Courier New',monospace}
|
|
14
|
-
main{max-width:1100px;margin:0 auto;padding:28px}
|
|
15
|
-
h1{margin:0 0 18px;font-size:22px;color:var(--accent);font-weight:700}
|
|
16
|
-
.summary{color:var(--muted);margin-bottom:22px}
|
|
17
|
-
.card{background:var(--panel);border:1px solid var(--border);border-radius:14px;padding:18px;margin:0 0 18px}
|
|
18
|
-
.readme{padding:0}
|
|
19
|
-
.readme .header{padding:16px 18px;border-bottom:1px solid var(--border)}
|
|
20
|
-
.readme .content{padding:18px}
|
|
21
|
-
.doc-card{padding:18px}
|
|
22
|
-
.doc-nav{margin:0 0 18px;display:flex;gap:12px;flex-wrap:wrap;align-items:center;color:var(--muted)}
|
|
23
|
-
.doc-nav-links{display:flex;gap:12px;flex-wrap:wrap;align-items:center}
|
|
24
|
-
.doc-nav a{color:var(--accent);font-weight:600}
|
|
25
|
-
.doc-list{list-style:none;margin:18px 0;padding:0}
|
|
26
|
-
.doc-list li{margin:6px 0}
|
|
27
|
-
.doc-list a{color:var(--accent)}
|
|
28
|
-
.nav-search{margin-left:auto;position:relative;display:flex;align-items:center;gap:8px;flex-wrap:wrap;justify-content:flex-end;min-width:240px;max-width:520px}
|
|
29
|
-
.search-title{font-weight:700;font-size:14px;margin-bottom:4px}
|
|
30
|
-
.search-hint{color:var(--muted);font-size:12px;margin:0}
|
|
31
|
-
.search-input-row{display:flex;gap:8px;align-items:center;width:100%}
|
|
32
|
-
.search-input{flex:1;border:1px solid var(--border);border-radius:10px;background:var(--chip);color:var(--text);padding:10px 12px}
|
|
33
|
-
.search-input:focus{outline:1px solid var(--accent);box-shadow:0 0 0 3px rgba(92,200,255,0.15)}
|
|
34
|
-
.search-clear{border:1px solid var(--border);background:var(--panel);color:var(--muted);border-radius:10px;padding:9px 12px;cursor:pointer;font-weight:600}
|
|
35
|
-
.search-clear:hover{border-color:var(--accent);color:var(--accent)}
|
|
36
|
-
.search-status{color:var(--muted);font-size:12px;margin-top:4px;width:100%;text-align:right}
|
|
37
|
-
.search-results{position:absolute;top:100%;right:0;margin-top:10px;border:1px solid var(--border);border-radius:12px;background:var(--panel);max-height:360px;overflow:auto;min-width:320px;max-width:520px;box-shadow:0 12px 30px rgba(0,0,0,0.35);z-index:25}
|
|
38
|
-
.search-results ul{list-style:none;margin:0;padding:0}
|
|
39
|
-
.search-item{padding:10px 12px;border-bottom:1px solid var(--border)}
|
|
40
|
-
.search-item:last-child{border-bottom:none}
|
|
41
|
-
.search-item a{display:block;color:var(--accent);font-weight:600}
|
|
42
|
-
.search-snippet{color:var(--muted);font-size:13px;margin-top:4px}
|
|
43
|
-
.search-results mark{background:rgba(92,200,255,0.2);color:var(--text);border-radius:4px;padding:0 2px}
|
|
44
|
-
.prose h1{font-size:24px;margin:0 0 12px}
|
|
45
|
-
.prose h2{font-size:18px;margin:18px 0 8px}
|
|
46
|
-
.prose h3{font-size:16px;margin:14px 0 6px}
|
|
47
|
-
.prose p{margin:0 0 10px}
|
|
48
|
-
.prose ul,.prose ol{margin:0 0 10px 18px}
|
|
49
|
-
.prose li{margin:4px 0}
|
|
50
|
-
.prose code{background:#0d1520;border:1px solid var(--border);padding:1px 5px;border-radius:6px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,'Liberation Mono','Courier New',monospace;font-size:12px}
|
|
51
|
-
.prose pre{background:#0d1520;border:1px solid var(--border);padding:12px;border-radius:12px;overflow:auto}
|
|
52
|
-
.prose a{color:var(--accent)}
|
|
53
|
-
.prose img{max-width:100%;height:auto;}
|
|
54
|
-
.prose table{border-collapse:collapse;width:100%;margin:16px 0;background:var(--panel);border:1px solid var(--border);border-radius:8px;overflow:hidden}
|
|
55
|
-
.prose th,.prose td{padding:8px 12px;text-align:left;border-bottom:1px solid var(--border)}
|
|
56
|
-
.prose th{background:var(--chip);color:var(--text);font-weight:600;font-size:13px}
|
|
57
|
-
.prose tr:last-child td{border-bottom:none}
|
|
58
|
-
.prose tbody tr:hover{background:rgba(92,200,255,0.05)}
|
|
59
|
-
.license{
|
|
60
|
-
background:var(--panel);border:1px solid var(--border);border-radius:14px;
|
|
61
|
-
padding:16px 16px 8px;margin:0 0 18px;
|
|
62
|
-
}
|
|
63
|
-
.license > h2{margin:0 0 10px;font-size:16px;font-weight:700}
|
|
64
|
-
.pkg{
|
|
65
|
-
border-top:1px solid var(--border);padding:10px 0;
|
|
66
|
-
display:grid;grid-template-columns:1fr auto;gap:10px;align-items:center;
|
|
67
|
-
}
|
|
68
|
-
.pkg:first-of-type{border-top:none}
|
|
69
|
-
.pkg .meta{display:flex;gap:10px;flex-wrap:wrap}
|
|
70
|
-
.pkg .name{font-weight:600}
|
|
71
|
-
.pkg .desc{color:var(--muted);margin-top:2px}
|
|
72
|
-
a{color:var(--accent);text-decoration:none} a:hover{text-decoration:underline}
|
|
73
|
-
.chip{background:var(--chip);border:1px solid var(--border);border-radius:999px;padding:2px 8px;font-size:12px;color:var(--muted)}
|
|
74
|
-
.footer{margin-top:26px;color:var(--muted);font-size:12px}
|
|
75
|
-
</style>
|
|
76
|
-
</head>
|
|
77
|
-
<body>
|
|
78
|
-
<main>
|
|
79
|
-
<nav class="doc-nav">
|
|
80
|
-
<div class="doc-nav-links">
|
|
81
|
-
<a href="index.html">Help Home</a><span>·</span><a href="/help/table-of-contents.html">Table of Contents</a><span>·</span><a href="https://github.com/mmiscool/BREP" target="_blank" rel="noopener noreferrer">GitHub</a>
|
|
82
|
-
</div>
|
|
83
|
-
<div class="nav-search">
|
|
84
|
-
<div class="search-input-row">
|
|
85
|
-
<input type="search" id="doc-search" class="search-input" placeholder="Search docs... Type at least 2 characters to search" autocomplete="off" spellcheck="false" />
|
|
86
|
-
<button type="button" class="search-clear" id="doc-search-clear">Clear</button>
|
|
87
|
-
</div>
|
|
88
|
-
<div class="search-status" id="doc-search-status"></div>
|
|
89
|
-
<div class="search-results" id="doc-search-results" hidden>
|
|
90
|
-
<ul id="doc-search-list"></ul>
|
|
91
|
-
</div>
|
|
92
|
-
</div>
|
|
93
|
-
</nav>
|
|
94
|
-
<section class="card doc-card">
|
|
95
|
-
|
|
96
|
-
<div class="prose">
|
|
97
|
-
<h1>Fixed Constraint</h1><p>Status: Implemented</p><p><img src="assembly-constraints___Fixed_Constraint_dialog.png" alt="Fixed Constraint dialog" width="280" loading="lazy" /></p><p>Fixed constraints lock an assembly component in place so other constraints treat it as immovable. They usually provide the reference frame that every other constraint solves against.</p><h2>Inputs</h2><ul><li><code>id</code> – solver-assigned identifier that appears in the constraint list.</li><li><code>component</code> – single component selection. No other entity types are accepted.</li></ul><h2>Behaviour</h2><ul><li>Resolves the selection to an assembly component and verifies that it exposes the <code>isAssemblyComponent</code> marker used throughout the solver.</li><li>Marks the component and its owning feature as fixed (<code>component.fixed = true</code> and <code>feature.inputParams.isFixed = true</code>) so downstream calls to <code>isComponentFixed()</code> short-circuit.</li><li>Stores a friendly component name in <code>persistentData.componentName</code> for UI presentation.</li><li>Reports whether the component was already fixed so the UI can skip redundant status updates.</li></ul><h2>Usage Tips</h2><ul><li>Always fix at least one component in an assembly; otherwise every constraint will read as <code>blocked</code> because both sides appear movable.</li><li>Use Fixed constraints for temporary jig components: remove the constraint after the rest of the assembly is solved to free the part again.</li><li>When importing legacy data, run the solver once—Fixed constraints will refresh feature metadata that older saves might not have populated.</li></ul>
|
|
98
|
-
</div>
|
|
99
|
-
</section>
|
|
100
|
-
</main>
|
|
101
|
-
<script>
|
|
102
|
-
(() => {
|
|
103
|
-
const searchInput = document.getElementById("doc-search");
|
|
104
|
-
const resultsBox = document.getElementById("doc-search-results");
|
|
105
|
-
const resultsList = document.getElementById("doc-search-list");
|
|
106
|
-
const statusEl = document.getElementById("doc-search-status");
|
|
107
|
-
const clearBtn = document.getElementById("doc-search-clear");
|
|
108
|
-
if (!searchInput || !resultsBox || !resultsList || !statusEl || !clearBtn) return;
|
|
109
|
-
const indexUrl = "./search-index.json";
|
|
110
|
-
let indexPromise = null;
|
|
111
|
-
let cachedEntries = null;
|
|
112
|
-
|
|
113
|
-
const escapeHtml = (text = "") =>
|
|
114
|
-
text
|
|
115
|
-
.replace(/&/g, "&")
|
|
116
|
-
.replace(/</g, "<")
|
|
117
|
-
.replace(/>/g, ">");
|
|
118
|
-
|
|
119
|
-
const escapeRegExp = (text = "") => text.replace(/[.*+?^{}()|[]\$]/g, "\$&");
|
|
120
|
-
|
|
121
|
-
const highlight = (text, terms) => {
|
|
122
|
-
if (!terms.length) return escapeHtml(text);
|
|
123
|
-
const pattern = terms.map(escapeRegExp).join("|");
|
|
124
|
-
const regex = new RegExp(`(${pattern})`, "gi");
|
|
125
|
-
return escapeHtml(text).replace(regex, "<mark>$1</mark>");
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
const buildSnippet = (entry, terms) => {
|
|
129
|
-
const source = entry.content || entry.summary || "";
|
|
130
|
-
if (!source) return { plain: "", highlighted: "" };
|
|
131
|
-
let hit = source.length;
|
|
132
|
-
for (const term of terms) {
|
|
133
|
-
const idx = entry.contentLower.indexOf(term);
|
|
134
|
-
if (idx !== -1 && idx < hit) hit = idx;
|
|
135
|
-
}
|
|
136
|
-
if (!Number.isFinite(hit) || hit === source.length) hit = 0;
|
|
137
|
-
const start = Math.max(0, hit - 60);
|
|
138
|
-
const end = Math.min(source.length, hit + 140);
|
|
139
|
-
const snippet = source.slice(start, end);
|
|
140
|
-
return { plain: snippet, highlighted: highlight(snippet, terms) };
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const updateStatus = (message) => {
|
|
144
|
-
statusEl.textContent = message;
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
const loadIndex = async () => {
|
|
148
|
-
if (cachedEntries) return cachedEntries;
|
|
149
|
-
if (!indexPromise) {
|
|
150
|
-
indexPromise = fetch(indexUrl, { cache: "no-store" })
|
|
151
|
-
.then((res) => {
|
|
152
|
-
if (!res.ok) throw new Error("Failed to load search index");
|
|
153
|
-
return res.json();
|
|
154
|
-
})
|
|
155
|
-
.then((json) => {
|
|
156
|
-
cachedEntries = Array.isArray(json)
|
|
157
|
-
? json.map((entry) => ({
|
|
158
|
-
...entry,
|
|
159
|
-
contentLower: (entry.content || "").toLowerCase(),
|
|
160
|
-
titleLower: (entry.title || "").toLowerCase(),
|
|
161
|
-
}))
|
|
162
|
-
: [];
|
|
163
|
-
return cachedEntries;
|
|
164
|
-
})
|
|
165
|
-
.catch((err) => {
|
|
166
|
-
console.error("Search index load failed", err);
|
|
167
|
-
cachedEntries = [];
|
|
168
|
-
throw err;
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
return indexPromise;
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
const renderResults = (entries, terms) => {
|
|
175
|
-
if (!terms.length) {
|
|
176
|
-
resultsBox.hidden = true;
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
if (!entries.length) {
|
|
180
|
-
resultsList.innerHTML = '<li class="search-item"><div class="search-snippet">No matches found.</div></li>';
|
|
181
|
-
resultsBox.hidden = false;
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
const rendered = entries
|
|
185
|
-
.slice(0, 30)
|
|
186
|
-
.map((entry) => {
|
|
187
|
-
const snippet = buildSnippet(entry, terms);
|
|
188
|
-
const title = highlight(entry.title || entry.href, terms);
|
|
189
|
-
const hasFragment = snippet.plain && snippet.plain.trim().length > 0;
|
|
190
|
-
const fragment = hasFragment ? `#:~:text=${encodeURIComponent(snippet.plain.trim())}` : "";
|
|
191
|
-
const hrefWithFragment = hasFragment
|
|
192
|
-
? (entry.href.includes("#") ? `${entry.href}&:~:text=${encodeURIComponent(snippet.plain.trim())}` : `${entry.href}${fragment}`)
|
|
193
|
-
: entry.href;
|
|
194
|
-
const snippetHtml = snippet.highlighted ? `<div class="search-snippet">${snippet.highlighted}</div>` : "";
|
|
195
|
-
return `<li class="search-item"><a href="${hrefWithFragment}">${title}</a>${snippetHtml}</li>`;
|
|
196
|
-
})
|
|
197
|
-
.join("");
|
|
198
|
-
resultsList.innerHTML = rendered;
|
|
199
|
-
resultsBox.hidden = false;
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
const performSearch = async () => {
|
|
203
|
-
const rawQuery = searchInput.value.trim();
|
|
204
|
-
if (rawQuery.length < 2) {
|
|
205
|
-
updateStatus("Type at least 2 characters to search.");
|
|
206
|
-
resultsBox.hidden = true;
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
const terms = rawQuery.toLowerCase().split(/\s+/).filter(Boolean);
|
|
210
|
-
updateStatus("Searching...");
|
|
211
|
-
try {
|
|
212
|
-
const entries = await loadIndex();
|
|
213
|
-
const matches = entries.filter((entry) =>
|
|
214
|
-
terms.every((term) => entry.contentLower.includes(term) || entry.titleLower.includes(term))
|
|
215
|
-
);
|
|
216
|
-
renderResults(matches, terms);
|
|
217
|
-
updateStatus(matches.length ? `${matches.length} result${matches.length === 1 ? "" : "s"} for "${rawQuery}"` : "No matches found.");
|
|
218
|
-
} catch (err) {
|
|
219
|
-
updateStatus("Search unavailable (could not load index).");
|
|
220
|
-
}
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
searchInput.addEventListener("input", performSearch);
|
|
224
|
-
|
|
225
|
-
searchInput.addEventListener("focus", () => {
|
|
226
|
-
if (!cachedEntries) {
|
|
227
|
-
loadIndex().catch(() => updateStatus("Search unavailable (could not load index)."));
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
clearBtn.addEventListener("click", () => {
|
|
232
|
-
searchInput.value = "";
|
|
233
|
-
resultsBox.hidden = true;
|
|
234
|
-
updateStatus("Type at least 2 characters to search.");
|
|
235
|
-
searchInput.focus();
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
document.addEventListener("keydown", (evt) => {
|
|
239
|
-
if (evt.key === "Escape" && document.activeElement === searchInput) {
|
|
240
|
-
searchInput.value = "";
|
|
241
|
-
resultsBox.hidden = true;
|
|
242
|
-
updateStatus("Type at least 2 characters to search.");
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
})();
|
|
246
|
-
</script>
|
|
247
|
-
</body>
|
|
248
|
-
</html>
|