fluidcad 0.0.21 → 0.0.23
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 +7 -2
- package/lib/dist/common/shape.d.ts +7 -0
- package/lib/dist/common/shape.js +7 -0
- package/lib/dist/core/copy.js +1 -1
- package/lib/dist/core/draft.d.ts +16 -0
- package/lib/dist/core/draft.js +29 -0
- package/lib/dist/core/index.d.ts +2 -3
- package/lib/dist/core/index.js +1 -1
- package/lib/dist/core/interfaces.d.ts +2 -0
- package/lib/dist/core/part.d.ts +2 -6
- package/lib/dist/core/part.js +12 -22
- package/lib/dist/core/trim.d.ts +12 -5
- package/lib/dist/core/trim.js +7 -2
- package/lib/dist/features/2d/sketch.js +23 -17
- package/lib/dist/features/copy-circular.js +1 -0
- package/lib/dist/features/copy-circular2d.js +1 -0
- package/lib/dist/features/copy-linear.js +4 -5
- package/lib/dist/features/copy-linear2d.js +4 -5
- package/lib/dist/features/draft.d.ts +15 -0
- package/lib/dist/features/draft.js +88 -0
- package/lib/dist/features/extrude-base.js +1 -0
- package/lib/dist/features/remove.d.ts +2 -0
- package/lib/dist/features/remove.js +7 -0
- package/lib/dist/features/trim2d.d.ts +16 -4
- package/lib/dist/features/trim2d.js +80 -29
- package/lib/dist/filters/edge/above-below.d.ts +20 -0
- package/lib/dist/filters/edge/above-below.js +57 -0
- package/lib/dist/filters/edge/edge-filter.d.ts +40 -6
- package/lib/dist/filters/edge/edge-filter.js +76 -8
- package/lib/dist/filters/edge/intersects-with.d.ts +18 -0
- package/lib/dist/filters/edge/intersects-with.js +38 -0
- package/lib/dist/filters/edge/on-plane.d.ts +4 -2
- package/lib/dist/filters/edge/on-plane.js +37 -12
- package/lib/dist/filters/filter-builder-base.d.ts +0 -5
- package/lib/dist/filters/filter-builder-base.js +12 -0
- package/lib/dist/helpers/clone-transform.js +3 -0
- package/lib/dist/oc/draft-ops.d.ts +5 -0
- package/lib/dist/oc/draft-ops.js +51 -0
- package/lib/dist/oc/edge-query.d.ts +5 -1
- package/lib/dist/oc/edge-query.js +40 -0
- package/lib/dist/oc/mesh.d.ts +2 -0
- package/lib/dist/oc/mesh.js +14 -6
- package/lib/dist/rendering/mesh-transform.d.ts +3 -0
- package/lib/dist/rendering/mesh-transform.js +22 -0
- package/lib/dist/rendering/render-solid.js +3 -2
- package/lib/dist/rendering/render.js +28 -6
- package/lib/dist/tests/features/chamfer.test.js +1 -1
- package/lib/dist/tests/features/draft.test.d.ts +1 -0
- package/lib/dist/tests/features/draft.test.js +147 -0
- package/lib/dist/tests/features/fillet.test.js +1 -1
- package/lib/dist/tests/features/part.test.js +69 -114
- package/lib/dist/tests/features/select.test.js +101 -3
- package/lib/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -3
- package/server/dist/fluidcad-server.d.ts +2 -0
- package/server/dist/fluidcad-server.js +10 -0
- package/server/dist/routes/actions.js +20 -0
- package/server/dist/vite-manager.js +7 -1
- package/ui/dist/assets/{index-B1LkrBga.js → index-CqP_mgZk.js} +23 -12
- package/ui/dist/assets/{index-BfcNNxXr.css → index-gPoNOiIs.css} +1 -1
- package/ui/dist/index.html +2 -2
- package/lib/dist/core/use.d.ts +0 -5
- package/lib/dist/core/use.js +0 -22
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fluidcad",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.23",
|
|
4
4
|
"description": "Parametric CAD modeling library using javascript",
|
|
5
5
|
"author": "Marwan Aouida <contact@marwan.dev>",
|
|
6
6
|
"homepage": "https://fluidcad.io",
|
|
@@ -58,8 +58,7 @@
|
|
|
58
58
|
"chokidar": "^5.0.0",
|
|
59
59
|
"color-name": "^2.1.0",
|
|
60
60
|
"express": "^5.2.1",
|
|
61
|
-
"occjs-
|
|
62
|
-
"occjs-wrapper": "npm:occjs-fluidcad@3.0.0",
|
|
61
|
+
"occjs-wrapper": "../occjs-fluidcad",
|
|
63
62
|
"stacktrace-parser": "^0.1.11",
|
|
64
63
|
"tree-sitter-wasms": "^0.1.13",
|
|
65
64
|
"tsx": "^4.21.0",
|
|
@@ -10,10 +10,12 @@ export declare class FluidCadServer {
|
|
|
10
10
|
private previousScenes;
|
|
11
11
|
private renderingCache;
|
|
12
12
|
private currentFileName;
|
|
13
|
+
private currentFilePath;
|
|
13
14
|
init(workspacePath: string): Promise<void>;
|
|
14
15
|
processFile(filePath: string, ignoreCache?: boolean): Promise<SceneRenderedData | null>;
|
|
15
16
|
updateLiveCode(fileName: string, code: string): Promise<SceneRenderedData | null>;
|
|
16
17
|
rollbackFromUI(index: number): Promise<SceneRenderedData | null>;
|
|
18
|
+
recomputeCurrentFile(): Promise<SceneRenderedData | null>;
|
|
17
19
|
rollback(fileName: string, index: number): Promise<SceneRenderedData | null>;
|
|
18
20
|
importFile(workspacePath: string, fileName: string, data: string): Promise<void>;
|
|
19
21
|
getShapeProperties(shapeId: string): any;
|
|
@@ -9,6 +9,7 @@ export class FluidCadServer {
|
|
|
9
9
|
previousScenes = new Map();
|
|
10
10
|
renderingCache = new Map();
|
|
11
11
|
currentFileName = '';
|
|
12
|
+
currentFilePath = '';
|
|
12
13
|
async init(workspacePath) {
|
|
13
14
|
await this.viteManager.init(workspacePath);
|
|
14
15
|
const initFilePath = normalizePath(join(workspacePath, 'init.js'));
|
|
@@ -24,6 +25,7 @@ export class FluidCadServer {
|
|
|
24
25
|
filePath = normalizePath(filePath);
|
|
25
26
|
const normalizedFileName = filePath.replace('virtual:live-render:', '');
|
|
26
27
|
this.currentFileName = normalizedFileName;
|
|
28
|
+
this.currentFilePath = filePath;
|
|
27
29
|
if (!ignoreCache) {
|
|
28
30
|
const fromCache = this.renderingCache.get(normalizedFileName);
|
|
29
31
|
if (fromCache) {
|
|
@@ -88,6 +90,14 @@ export class FluidCadServer {
|
|
|
88
90
|
async rollbackFromUI(index) {
|
|
89
91
|
return this.rollback(this.currentFileName, index);
|
|
90
92
|
}
|
|
93
|
+
async recomputeCurrentFile() {
|
|
94
|
+
if (!this.currentFilePath) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
this.previousScenes.delete(this.currentFileName);
|
|
98
|
+
this.renderingCache.delete(this.currentFileName);
|
|
99
|
+
return this.processFile(this.currentFilePath, true);
|
|
100
|
+
}
|
|
91
101
|
async rollback(fileName, index) {
|
|
92
102
|
if (!this.sceneManager) {
|
|
93
103
|
return null;
|
|
@@ -67,6 +67,26 @@ export function createActionsRouter(fluidCadServer, sendToExtension, broadcastTo
|
|
|
67
67
|
});
|
|
68
68
|
res.json({ success: true });
|
|
69
69
|
});
|
|
70
|
+
router.post('/recompute', async (_req, res) => {
|
|
71
|
+
const data = await fluidCadServer.recomputeCurrentFile();
|
|
72
|
+
if (!data) {
|
|
73
|
+
res.status(404).json({ error: 'No active scene' });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
sendToExtension({
|
|
77
|
+
type: 'scene-rendered',
|
|
78
|
+
absPath: data.absPath,
|
|
79
|
+
result: data.result,
|
|
80
|
+
rollbackStop: data.rollbackStop,
|
|
81
|
+
});
|
|
82
|
+
broadcastToUI({
|
|
83
|
+
type: 'scene-rendered',
|
|
84
|
+
result: data.result,
|
|
85
|
+
absPath: data.absPath,
|
|
86
|
+
breakpointHit: data.breakpointHit,
|
|
87
|
+
});
|
|
88
|
+
res.json({ success: true });
|
|
89
|
+
});
|
|
70
90
|
router.post('/clear-breakpoints', (_req, res) => {
|
|
71
91
|
sendToExtension({ type: 'clear-breakpoints' });
|
|
72
92
|
res.json({ success: true });
|
|
@@ -103,7 +103,13 @@ export class ViteManager {
|
|
|
103
103
|
this.buffers.set(id, code);
|
|
104
104
|
}
|
|
105
105
|
async loadModule(filePath) {
|
|
106
|
-
|
|
106
|
+
const mod = await this.server.ssrLoadModule(filePath);
|
|
107
|
+
for (const value of Object.values(mod)) {
|
|
108
|
+
if (typeof value === 'function') {
|
|
109
|
+
await value();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return mod;
|
|
107
113
|
}
|
|
108
114
|
invalidateModule() {
|
|
109
115
|
for (const [id, mod] of this.server.moduleGraph.idToModuleMap) {
|
|
@@ -4657,10 +4657,10 @@ void main() {
|
|
|
4657
4657
|
<span class="text-sm font-medium text-base-content/70">History</span>
|
|
4658
4658
|
<span data-ref="history-total" class="text-xs text-base-content/40 tabular-nums hidden"></span>
|
|
4659
4659
|
<button data-ref="history-dots" class="ml-auto btn btn-ghost btn-square btn-xs text-base-content/40 hover:text-base-content/70 shrink-0">${bm}</button>
|
|
4660
|
-
`,this.panel.appendChild(l),this.historyTotalLabel=l.querySelector(`[data-ref="history-total"]`);let u=l.querySelector(`[data-ref="history-dots"]`);u.addEventListener(`click`,e=>{e.stopPropagation(),this.showHistoryDropdown(u)}),this.timelineBody=document.createElement(`div`),this.timelineBody.className=`py-1 overflow-y-auto min-h-0
|
|
4660
|
+
`,this.panel.appendChild(l),this.historyTotalLabel=l.querySelector(`[data-ref="history-total"]`);let u=l.querySelector(`[data-ref="history-dots"]`);u.addEventListener(`click`,e=>{e.stopPropagation(),this.showHistoryDropdown(u)}),this.timelineBody=document.createElement(`div`),this.timelineBody.className=`py-1 overflow-y-auto min-h-0`,this.panel.appendChild(this.timelineBody);let d=document.createElement(`div`);d.className=_m,d.innerHTML=`
|
|
4661
4661
|
<span class="flex items-center justify-center w-5 h-5 opacity-50 transition-transform rotate-90">${vm}</span>
|
|
4662
4662
|
<span class="text-sm font-medium text-base-content/70">Shapes</span>
|
|
4663
|
-
`,this.panel.appendChild(d),this.shapesBody=document.createElement(`div`),this.shapesBody.className=`py-1 overflow-y-auto min-h-[
|
|
4663
|
+
`,this.panel.appendChild(d),this.shapesBody=document.createElement(`div`),this.shapesBody.className=`py-1 overflow-y-auto min-h-[33vh] flex-1`,this.panel.appendChild(this.shapesBody),l.addEventListener(`click`,()=>{this.timelineExpanded=!this.timelineExpanded,this.timelineBody.classList.toggle(`hidden`,!this.timelineExpanded),l.querySelector(`[data-ref="chevron"]`).classList.toggle(`rotate-90`,this.timelineExpanded)}),d.addEventListener(`click`,()=>{this.shapesExpanded=!this.shapesExpanded,this.shapesBody.classList.toggle(`hidden`,!this.shapesExpanded),d.querySelector(`span`).classList.toggle(`rotate-90`,this.shapesExpanded)})}update(e,t,n){if(this.sceneObjects=e,this.rollbackStop=t,n){let e=n.split(`/`).pop()||n;this.fileLabel.textContent=e}this.loaded||(this.loaded=!0,this.panel.classList.remove(`hidden`)),this.renderTimeline(!0),this.renderShapes(),this.updateHistoryTotal()}updateHistoryTotal(){if(!this.showBuildTimings){this.historyTotalLabel.classList.add(`hidden`);return}let e=0,t=!1;for(let n of this.sceneObjects)n.parentId||n.fromCache||n.buildDurationMs==null||(e+=n.buildDurationMs,t=!0);if(!t){this.historyTotalLabel.classList.add(`hidden`);return}this.historyTotalLabel.textContent=`· ${Sm(e)}`,this.historyTotalLabel.classList.remove(`hidden`)}renderTimeline(e=!1){let t=this.sceneObjects,n=this.rollbackStop,r=new Set;for(let e of t)e.uniqueType!==`lazy-select`&&e.parentId&&r.add(e.parentId);let i=``;for(let e=0;e<t.length;e++){let a=t[e];if(a.parentId||a.uniqueType===`lazy-select`)continue;let o=a.id!=null&&r.has(a.id),s=a.id!=null&&this.collapsedIds.has(a.id);if(i+=this.renderTimelineItem(a,e,n,!1,o,s),o&&!s)for(let e=0;e<t.length;e++)t[e].uniqueType!==`lazy-select`&&t[e].parentId===a.id&&(i+=this.renderTimelineItem(t[e],e,n,!0,!1,!1))}if(this.timelineBody.innerHTML=i,this.timelineBody.querySelectorAll(`[data-index]`).forEach(e=>{e.addEventListener(`click`,t=>{if(t.target.closest(`[data-toggle]`))return;let n=parseInt(e.dataset.index,10);this.rollbackTo(n),this.gotoSource(this.sceneObjects[n])}),e.addEventListener(`dblclick`,t=>{if(t.target.closest(`[data-toggle]`))return;let n=parseInt(e.dataset.index,10);this.addBreakpointAfter(n),this.gotoSource(this.sceneObjects[n])})}),this.timelineBody.querySelectorAll(`[data-toggle]`).forEach(e=>{e.addEventListener(`click`,t=>{t.stopPropagation();let n=e.dataset.toggle;this.collapsedIds.has(n)?this.collapsedIds.delete(n):this.collapsedIds.add(n),this.renderTimeline()})}),e){let e=this.timelineBody.querySelector(`[data-current="true"]`);e&&e.scrollIntoView({block:`nearest`})}}renderTimelineItem(e,t,n,r,i,a){let o=t===n,s=t>n,c=e.visible===!1,l=e.hasError===!0,u=e.name?e.name.charAt(0).toUpperCase()+e.name.slice(1):e.type||`Unknown`,d=e.type===`part`?`/icons/box.png`:`/icons/${gm(e.uniqueType,e.type)}.png`,f=`flex items-center gap-1 px-3 py-1.5 cursor-pointer hover:bg-base-content/[0.06] text-sm`;r&&(f+=` pl-7`),o?f+=` border-l-2 border-primary bg-primary/10 text-primary`:l?f+=` text-error`:s||c?f+=` text-base-content/60`:f+=` text-base-content/80`;let p=c?`w-4 h-4 object-contain grayscale opacity-60`:`w-4 h-4 object-contain`,m=``;if(i){let t=a?``:`rotate-90`;m=`<span data-toggle="${e.id}" class="flex items-center justify-center w-5 h-5 opacity-50 hover:opacity-100 transition-transform ${t}">
|
|
4664
4664
|
${vm}
|
|
4665
4665
|
</span>`}else m=`<span class="w-4"></span>`;let h=this.showBuildTimings&&!e.fromCache&&e.buildDurationMs!=null,g=h?`<span class="ml-auto shrink-0 text-xs text-base-content/40 tabular-nums">${Sm(e.buildDurationMs)}</span>`:``,_=h?`shrink-0 text-base-content/40 [&>svg]:w-4 [&>svg]:h-4`:`ml-auto shrink-0 text-base-content/40 [&>svg]:w-4 [&>svg]:h-4`,v=e.fromCache?`<span class="${_}">${Lp}</span>`:`<span class="${_}">${Rp}</span>`;return`
|
|
4666
4666
|
<div class="${f}" data-index="${t}" data-container="${e.isContainer??!1}" data-current="${o}">
|
|
@@ -4670,7 +4670,7 @@ void main() {
|
|
|
4670
4670
|
${g}
|
|
4671
4671
|
${v}
|
|
4672
4672
|
</div>
|
|
4673
|
-
`}async rollbackTo(e){try{await fetch(`/api/rollback`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({index:e})})}catch(e){console.error(`Rollback failed:`,e)}}async addBreakpointAfter(e){let t=this.sceneObjects[e];if(!(!t||!t.sourceLocation))try{await fetch(`/api/add-breakpoint`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({sourceLocation:t.sourceLocation})})}catch(e){console.error(`Add breakpoint failed:`,e)}}async gotoSource(e){if(!(!e||!e.sourceLocation))try{await fetch(`/api/code/goto-source`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e.sourceLocation)})}catch(e){console.error(`Goto source failed:`,e)}}renderShapes(){let e=new Map;for(let t of this.sceneObjects)for(let n of t.sceneShapes){if(n.isMetaShape)continue;let t=n.shapeType||`unknown`;e.has(t)||e.set(t,[]),e.get(t).push({shapeId:n.shapeId||``,shapeType:t})}let t=``;for(let[n,r]of e){let e=n.charAt(0).toUpperCase()+n.slice(1),i=this.collapsedShapeGroups.has(n);if(t+=`
|
|
4673
|
+
`}async recomputeScene(){try{await fetch(`/api/recompute`,{method:`POST`,headers:{"Content-Type":`application/json`}})}catch(e){console.error(`Recompute failed:`,e)}}async rollbackTo(e){try{await fetch(`/api/rollback`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({index:e})})}catch(e){console.error(`Rollback failed:`,e)}}async addBreakpointAfter(e){let t=this.sceneObjects[e];if(!(!t||!t.sourceLocation))try{await fetch(`/api/add-breakpoint`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({sourceLocation:t.sourceLocation})})}catch(e){console.error(`Add breakpoint failed:`,e)}}async gotoSource(e){if(!(!e||!e.sourceLocation))try{await fetch(`/api/code/goto-source`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e.sourceLocation)})}catch(e){console.error(`Goto source failed:`,e)}}renderShapes(){let e=new Map;for(let t of this.sceneObjects)for(let n of t.sceneShapes){if(n.isMetaShape)continue;let t=n.shapeType||`unknown`;e.has(t)||e.set(t,[]),e.get(t).push({shapeId:n.shapeId||``,shapeType:t})}let t=``;for(let[n,r]of e){let e=n.charAt(0).toUpperCase()+n.slice(1),i=this.collapsedShapeGroups.has(n);if(t+=`
|
|
4674
4674
|
<div class="flex items-center gap-1 px-3 py-1.5 cursor-pointer hover:bg-base-content/[0.06] text-sm text-base-content/70 font-medium" data-shape-group="${n}">
|
|
4675
4675
|
<span class="flex items-center justify-center w-5 h-5 opacity-50 hover:opacity-100 transition-transform ${i?``:`rotate-90`}">
|
|
4676
4676
|
${vm}
|
|
@@ -4687,12 +4687,16 @@ void main() {
|
|
|
4687
4687
|
</div>
|
|
4688
4688
|
`}}this.shapesBody.innerHTML=t,this.shapesBody.querySelectorAll(`[data-shape-group]`).forEach(e=>{e.addEventListener(`click`,()=>{let t=e.dataset.shapeGroup;this.collapsedShapeGroups.has(t)?this.collapsedShapeGroups.delete(t):this.collapsedShapeGroups.add(t),this.renderShapes()})}),this.shapesBody.querySelectorAll(`[data-shape-id]`).forEach(e=>{e.addEventListener(`click`,t=>{if(t.target.closest(`[data-dots]`)||t.target.closest(`[data-eye]`))return;let n=e.dataset.shapeId;t.ctrlKey||t.metaKey?this.selectedShapeIds.has(n)?this.selectedShapeIds.delete(n):this.selectedShapeIds.add(n):(this.selectedShapeIds.clear(),this.selectedShapeIds.add(n)),this.renderShapes(),n&&this.onHighlightShape(n)})}),this.shapesBody.querySelectorAll(`[data-dots]`).forEach(e=>{e.addEventListener(`click`,t=>{t.stopPropagation();let n=e.dataset.dots;this.showShapeDropdown(e,n)})}),this.shapesBody.querySelectorAll(`[data-eye]`).forEach(e=>{e.addEventListener(`click`,t=>{t.stopPropagation();let n=e.dataset.eye,r=this.isShapeHidden(n);this.onToggleShapeVisibility(n,r),this.renderShapes()})})}setShowBuildTimings(e){this.showBuildTimings!==e&&(this.showBuildTimings=e,this.applyPanelWidth(),this.updateHistoryTotal(),this.loaded&&this.renderTimeline())}applyPanelWidth(){this.panel.classList.toggle(`w-[220px]`,!this.showBuildTimings),this.panel.classList.toggle(`w-[270px]`,this.showBuildTimings)}showHistoryDropdown(e){this.closeDropdown();let t=document.createElement(`div`);t.className=`absolute z-[200] panel-bg border border-base-content/10 rounded-md shadow-[0_4px_12px_rgba(0,0,0,0.4)]`;let n=e.getBoundingClientRect(),r=this.panel.getBoundingClientRect();t.style.top=`${n.bottom-r.top+2}px`,t.style.right=`${r.right-n.right}px`,t.innerHTML=`
|
|
4689
4689
|
<ul class="menu menu-xs p-1 min-w-[180px]">
|
|
4690
|
+
<li><button data-action="recompute" class="flex items-center gap-2">
|
|
4691
|
+
<span class="flex items-center justify-center w-4 h-4 shrink-0 [&>svg]:size-3.5">${Rp}</span>
|
|
4692
|
+
<span>Recompute scene</span>
|
|
4693
|
+
</button></li>
|
|
4690
4694
|
<li><button data-action="toggle-timings" class="flex items-center gap-2">
|
|
4691
|
-
${this.showBuildTimings?`<span class="text-primary
|
|
4695
|
+
${this.showBuildTimings?`<span class="flex items-center justify-center w-4 h-4 shrink-0 text-primary [&>svg]:size-3">${xm}</span>`:`<span class="w-4 h-4 shrink-0"></span>`}
|
|
4692
4696
|
<span>Show execution time</span>
|
|
4693
4697
|
</button></li>
|
|
4694
4698
|
</ul>
|
|
4695
|
-
`,this.panel.appendChild(t),this.activeDropdown=t,t.querySelector(`[data-action="toggle-timings"]`).addEventListener(`click`,()=>{let e=!this.showBuildTimings;this.showBuildTimings=e,this.applyPanelWidth(),this.updateHistoryTotal(),dp(`showBuildTimings`,e),this.closeDropdown(),this.renderTimeline()});let i=n=>{!t.contains(n.target)&&!e.contains(n.target)&&this.closeDropdown()};setTimeout(()=>document.addEventListener(`click`,i),0),this.dropdownCleanup=()=>document.removeEventListener(`click`,i)}showShapeDropdown(e,t){this.closeDropdown();let n=document.createElement(`div`);n.className=`absolute z-[200] panel-bg border border-base-content/10 rounded-md shadow-[0_4px_12px_rgba(0,0,0,0.4)]`;let r=e.getBoundingClientRect(),i=this.panel.getBoundingClientRect();n.style.top=`${r.bottom-i.top+2}px`,n.style.left=`${r.left-i.left}px`,n.innerHTML=`
|
|
4699
|
+
`,this.panel.appendChild(t),this.activeDropdown=t,t.querySelector(`[data-action="toggle-timings"]`).addEventListener(`click`,()=>{let e=!this.showBuildTimings;this.showBuildTimings=e,this.applyPanelWidth(),this.updateHistoryTotal(),dp(`showBuildTimings`,e),this.closeDropdown(),this.renderTimeline()}),t.querySelector(`[data-action="recompute"]`).addEventListener(`click`,()=>{this.closeDropdown(),this.recomputeScene()});let i=n=>{!t.contains(n.target)&&!e.contains(n.target)&&this.closeDropdown()};setTimeout(()=>document.addEventListener(`click`,i),0),this.dropdownCleanup=()=>document.removeEventListener(`click`,i)}showShapeDropdown(e,t){this.closeDropdown();let n=document.createElement(`div`);n.className=`absolute z-[200] panel-bg border border-base-content/10 rounded-md shadow-[0_4px_12px_rgba(0,0,0,0.4)]`;let r=e.getBoundingClientRect(),i=this.panel.getBoundingClientRect();n.style.top=`${r.bottom-i.top+2}px`,n.style.left=`${r.left-i.left}px`,n.innerHTML=`
|
|
4696
4700
|
<ul class="menu menu-xs p-1 min-w-[140px]">
|
|
4697
4701
|
<li><button data-action="export">Export</button></li>
|
|
4698
4702
|
<li><button data-action="set-transparency">Set Transparency</button></li>
|
|
@@ -4838,23 +4842,30 @@ void main() {
|
|
|
4838
4842
|
<span class="loading loading-spinner loading-sm"></span>
|
|
4839
4843
|
<span class="loading-text">Loading FluidCAD...</span>
|
|
4840
4844
|
</div>
|
|
4841
|
-
`,yh.appendChild(xh);var Sh=xh.querySelector(`.loading-text`);function Ch(e){Sh.textContent=e,xh.classList.remove(`hidden`)}function wh(){xh.classList.add(`hidden`)}var $=new im(`fluidcad-viewer`);Pd(()=>$.rebuildSceneMesh());var Th=new dm(yh),Eh=new mm(yh),Dh=new km(yh,$.sceneContext),Oh=new Am(yh,()=>{
|
|
4845
|
+
`,yh.appendChild(xh);var Sh=xh.querySelector(`.loading-text`);function Ch(e){Sh.textContent=e,xh.classList.remove(`hidden`)}function wh(){xh.classList.add(`hidden`)}var $=new im(`fluidcad-viewer`);Pd(()=>$.rebuildSceneMesh());var Th=new dm(yh),Eh=new mm(yh),Dh=new km(yh,$.sceneContext),Oh=new Am(yh,()=>{Zh===`picking-active`&&ag(),Mh===`picking-active`&&Bh()}),kh=new Cm(yh,e=>$.highlightShape(e),e=>Dh.show(e),(e,t)=>$.setShapeVisibility(e,t),e=>$.isShapeHidden(e),(e,t)=>$.setShapeTransparency(e,t),e=>$.getShapeTransparency(e));Th.setOpenHandler(()=>{$.clearHighlight(),Eh.hide()}),Th.setCentroidHandler(e=>{e?$.showCentroid(e):$.clearCentroid()}),$.setSelectionHandler((e,t)=>{e?Th.isOpen?$.highlightShape(e):t?.type===`face`?$.highlightFace(e,t.index):t?.type===`edge`?$.highlightEdge(e,t.index):$.clearHighlight():$.clearHighlight(),Th.setSelectedShape(e),e!==null&&t!==null?t.type===`face`?Eh.showForFace(e,t.index):Eh.showForEdge(e,t.index):Eh.hide()});var Ah=document.createElement(`div`);Ah.id=`fluidcad-trim-pick-trigger`,Ah.className=`absolute top-4 left-1/2 -translate-x-1/2 z-[999] pointer-events-auto hidden`,Ah.innerHTML=`
|
|
4846
|
+
<button class="flex items-center gap-3 panel-bg border border-base-content/10 rounded-lg px-6 py-3 text-base-content/70 text-sm leading-none select-none cursor-pointer hover:border-base-content/20 transition-colors">
|
|
4847
|
+
<span class="[&>svg]:size-5">${Pp}</span>
|
|
4848
|
+
<span>Interactive Trimming</span>
|
|
4849
|
+
</button>
|
|
4850
|
+
`,yh.appendChild(Ah);var jh=document.createElement(`div`);jh.id=`fluidcad-trim-pick-active`,jh.className=`absolute top-4 left-1/2 -translate-x-1/2 z-[999] pointer-events-auto hidden`,jh.innerHTML=`
|
|
4842
4851
|
<div class="flex items-center gap-3 panel-bg border border-base-content/10 rounded-lg px-6 py-3 text-base-content/70 text-sm leading-none select-none">
|
|
4843
4852
|
<span class="[&>svg]:size-5">${Pp}</span>
|
|
4844
4853
|
<span>Trimming Mode</span>
|
|
4854
|
+
<div class="h-4 w-px bg-base-content/10"></div>
|
|
4855
|
+
<button class="text-base-content/60 hover:text-base-content transition-colors cursor-pointer" id="exit-trim-pick">Exit</button>
|
|
4845
4856
|
</div>
|
|
4846
|
-
`,yh.appendChild(
|
|
4857
|
+
`,yh.appendChild(jh);var Mh=`idle`,Nh=null,Ph=null,Fh=null,Ih=null;function Lh(e){let t=null;for(let n=e.length-1;n>=0;n--)if(bh(e[n],e)){t=e[n];break}if(!t||t.type!==`sketch`||!t.id||!t.object?.plane)return{hasTrigger:!1};let n=null;for(let r=e.length-1;r>=0;r--)if(e[r].parentId===t.id){n=e[r];break}let r=n;return!r||r.type!==`trim2d`||r.object?.trigger!==`trim-picking`||!r.sourceLocation?{hasTrigger:!1}:{hasTrigger:!0,trimObj:n,sketchObj:t}}function Rh(e,t){Vh();let n=e.sketchObj.object.plane,r=e.trimObj.sourceLocation,i=e.sketchObj.id,a=_h.fromSceneObjects(t,i,n);Fh=new Mm($.sceneContext,n,a,t,i,e=>{fetch(`/api/insert-point`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({point:e,sourceLocation:r})})},e=>{$.clearHighlight(),Jh(),e&&($.highlightShape(e.shapeId),qh(e.endpoints))}),Ih=r.line,Fh.activate()}function zh(){if(Nh){if(!Nh.trimObj.object?.picking){fetch(`/api/add-pick`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({sourceLocation:Nh.trimObj.sourceLocation})}),Mh=`picking-active`,Ah.classList.add(`hidden`),jh.classList.remove(`hidden`),$.isTrimming=!0;return}Ph&&Rh(Nh,Ph),Mh=`picking-active`,Ah.classList.add(`hidden`),jh.classList.remove(`hidden`),$.isTrimming=!0}}function Bh(){Vh(),$.isTrimming=!1;let e=Nh?.trimObj,t=e?.object?.picking,n=e?.object?.pickPoints;t&&(!n||n.length===0)&&e?.sourceLocation&&fetch(`/api/remove-pick`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({sourceLocation:e.sourceLocation})}),Nh?(Mh=`icon-visible`,jh.classList.add(`hidden`),Ah.classList.remove(`hidden`)):(Mh=`idle`,jh.classList.add(`hidden`),Ah.classList.add(`hidden`))}function Vh(){Fh&&(Fh.deactivate(),Fh=null,Ih=null),Jh()}function Hh(){Vh(),Mh=`idle`,Ah.classList.add(`hidden`),jh.classList.add(`hidden`),Nh=null,Ph=null,$.isTrimming=!1}function Uh(e){let t=Lh(e);if(!t.hasTrigger){Hh();return}Nh={trimObj:t.trimObj,sketchObj:t.sketchObj},Ph=e;let n=t.trimObj.object?.picking;if(Mh===`picking-active`){if(n){let n=Nh.trimObj.sourceLocation.line;if(Fh&&Ih===n){Fh.updateEdges(e,t.sketchObj.id);return}Rh(Nh,e)}return}Mh=`icon-visible`,Ah.classList.remove(`hidden`),jh.classList.add(`hidden`)}Ah.querySelector(`button`).addEventListener(`click`,()=>{zh()}),jh.querySelector(`#exit-trim-pick`).addEventListener(`click`,()=>{Bh()});var Wh=16762232,Gh=1e-4,Kh=[];function qh(e){Jh(),e.length!==0&&($.sceneContext.scene.traverse(t=>{if(!t.userData.isVertexDot)return;let n=t.children[0];if(!n||!n.isMesh)return;let r=t.position;for(let t of e){let e=r.x-t[0],i=r.y-t[1],a=r.z-t[2];if(e*e+i*i+a*a<Gh){let e=n.material,t=e.clone();t.color.setHex(Wh),n.material=t,Kh.push({mesh:n,originalMaterial:e});break}}}),$.sceneContext.requestRender())}function Jh(){for(let{mesh:e,originalMaterial:t}of Kh)e.material.dispose(),e.material=t;Kh.length>0&&(Kh.length=0,$.sceneContext.requestRender())}var Yh=document.createElement(`div`);Yh.id=`fluidcad-region-pick-trigger`,Yh.className=`absolute top-4 left-1/2 -translate-x-1/2 z-[999] pointer-events-auto hidden`,Yh.innerHTML=`
|
|
4847
4858
|
<button class="flex items-center gap-3 panel-bg border border-base-content/10 rounded-lg px-6 py-3 text-base-content/70 text-sm leading-none select-none cursor-pointer hover:border-base-content/20 transition-colors">
|
|
4848
4859
|
<span class="[&>svg]:size-5">${zp}</span>
|
|
4849
4860
|
<span>Pick Regions</span>
|
|
4850
4861
|
</button>
|
|
4851
|
-
`,yh.appendChild(
|
|
4862
|
+
`,yh.appendChild(Yh);var Xh=document.createElement(`div`);Xh.id=`fluidcad-region-pick-active`,Xh.className=`absolute top-4 left-1/2 -translate-x-1/2 z-[999] pointer-events-auto hidden`,Xh.innerHTML=`
|
|
4852
4863
|
<div class="flex items-center gap-3 panel-bg border border-base-content/10 rounded-lg px-6 py-3 text-base-content/70 text-sm leading-none select-none">
|
|
4853
4864
|
<span>Region Picking Mode</span>
|
|
4854
4865
|
<div class="h-4 w-px bg-base-content/10"></div>
|
|
4855
4866
|
<button class="text-base-content/60 hover:text-base-content transition-colors cursor-pointer" id="exit-region-pick">Exit</button>
|
|
4856
4867
|
</div>
|
|
4857
|
-
`,yh.appendChild(
|
|
4868
|
+
`,yh.appendChild(Xh);var Zh=`idle`,Qh=null,$h=null,eg=null,tg=[`extrude`,`cut`,`cut-symmetric`,`revolve`,`sweep`];function ng(e){let t=[`plane`,`axis`],n;for(let r=e.length-1;r>=0;r--){let i=e[r];if(!i.parentId&&!t.includes(i.type)){n=i;break}}if(!n)return{hasTrigger:!1};let r=n;if(!tg.includes(r.type)||r.object?.trigger!==`region-picking`||r.object?.thin)return{hasTrigger:!1};let i=e.indexOf(n),a;for(let t=i-1;t>=0;t--)if(e[t].type===`sketch`&&e[t].parentId===r.parentId){a=e[t];break}return{hasTrigger:!0,extrudeObj:n,sketchObj:a}}function rg(e){og();let t=e.extrudeObj.object?.pickPlane??e.sketchObj.object.plane,n=e.extrudeObj.sourceLocation;$h=new Rm($.sceneContext,t,e=>{fetch(`/api/insert-point`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({point:e,sourceLocation:n})})},e=>{fetch(`/api/set-pick-points`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({points:e,sourceLocation:n})})},e=>{}),eg=n.line,$h.activate()}function ig(){if(Qh){if(!Qh.extrudeObj.object?.picking){fetch(`/api/add-pick`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({sourceLocation:Qh.extrudeObj.sourceLocation})}),Zh=`picking-active`,Yh.classList.add(`hidden`),Xh.classList.remove(`hidden`),$.isRegionPicking=!0,$.toggleSketchMode(!1);return}rg(Qh),Zh=`picking-active`,Yh.classList.add(`hidden`),Xh.classList.remove(`hidden`),$.isRegionPicking=!0,$.toggleSketchMode(!1),$.rebuildSceneMesh()}}function ag(){og(),$.isRegionPicking=!1,$.toggleSketchMode(!0),$.rebuildSceneMesh();let e=Qh?.extrudeObj,t=e?.object?.picking,n=e?.object?.pickPoints;t&&(!n||n.length===0)&&e?.sourceLocation&&fetch(`/api/remove-pick`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({sourceLocation:e.sourceLocation})}),Qh?(Zh=`icon-visible`,Xh.classList.add(`hidden`),Yh.classList.remove(`hidden`)):(Zh=`idle`,Xh.classList.add(`hidden`),Yh.classList.add(`hidden`))}function og(){$h&&($h.deactivate(),$h=null,eg=null)}function sg(){og(),Zh=`idle`,Yh.classList.add(`hidden`),Xh.classList.add(`hidden`),Qh=null,$.isRegionPicking=!1,$.toggleSketchMode(!0)}function cg(e){let t=ng(e),n=t.extrudeObj?.object?.pickPlane||t.sketchObj?.object?.plane;if(!t.hasTrigger||!t.extrudeObj?.sourceLocation||!n){sg();return}Qh={extrudeObj:t.extrudeObj,sketchObj:t.sketchObj};let r=t.extrudeObj.object?.picking;if(Zh===`picking-active`){if(r){let e=Qh.extrudeObj.sourceLocation.line;if($h&&eg===e)return;rg(Qh)}return}Zh=`icon-visible`,Yh.classList.remove(`hidden`),Xh.classList.add(`hidden`)}Yh.querySelector(`button`).addEventListener(`click`,()=>{ig()}),Xh.querySelector(`#exit-region-pick`).addEventListener(`click`,()=>{ag()});var lg=document.createElement(`div`);lg.id=`fluidcad-bezier-indicator`,lg.className=`absolute top-4 left-1/2 -translate-x-1/2 z-[999] pointer-events-auto hidden`,lg.innerHTML=`
|
|
4858
4869
|
<div class="flex items-center gap-3 panel-bg border border-base-content/10 rounded-lg px-6 py-3 text-base-content/70 text-sm leading-none select-none">
|
|
4859
4870
|
<span>Bezier Drawing Mode</span>
|
|
4860
4871
|
<div class="h-4 w-px bg-base-content/10"></div>
|
|
@@ -4867,15 +4878,15 @@ void main() {
|
|
|
4867
4878
|
<span class="text-xs">Grid</span>
|
|
4868
4879
|
</label>
|
|
4869
4880
|
</div>
|
|
4870
|
-
`,yh.appendChild(
|
|
4881
|
+
`,yh.appendChild(lg),lg.querySelector(`[data-snap="vertex"]`).addEventListener(`change`,e=>{ug&&(ug.snapController.snapToVertices=e.target.checked)}),lg.querySelector(`[data-snap="grid"]`).addEventListener(`change`,e=>{ug&&(ug.snapController.snapToGrid=e.target.checked)});var ug=null,dg=null;function fg(e){let t=null;for(let n=e.length-1;n>=0;n--)if(bh(e[n],e)){t=e[n];break}if(!t||t.type!==`sketch`||!t.id)return!1;for(let n=e.length-1;n>=0;n--)if(e[n].parentId===t.id)return e[n].type===`bezier`;return!1}function pg(e,t){for(let n=e.length-1;n>=0;n--){let r=e[n];if(r.parentId===t&&r.type===`bezier`){let e=r.object?.startPoint,t=r.object?.resolvedPoints;return e?[e,...t||[]]:[]}}return[]}function mg(e){let t=null;for(let n=e.length-1;n>=0;n--)if(bh(e[n],e)){t=e[n];break}let n=t?.type===`sketch`?t:null;if(!n||!n.id||!n.object?.plane){hg();return}let r=null;for(let t=e.length-1;t>=0;t--)if(e[t].parentId===n.id){r=e[t];break}if(!r||r.type!==`bezier`||!r.sourceLocation){hg();return}let i=r.sourceLocation.line,a=n.object.plane,o=pg(e,n.id),s=_h.fromSceneObjects(e,n.id,a);if(ug&&dg===i){ug.updateExistingPoles(o),ug.snapController.updateSnapManager(s);return}hg();let c=r.sourceLocation,l=new vh(s,a),u=lg.querySelector(`[data-snap="vertex"]`),d=lg.querySelector(`[data-snap="grid"]`);u&&(l.snapToVertices=u.checked),d&&(l.snapToGrid=d.checked),ug=new dh($.sceneContext,a,l,o,e=>{fetch(`/api/insert-point`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({point:e,sourceLocation:c})})},e=>{fetch(`/api/set-pick-points`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({points:e,sourceLocation:c})})}),dg=i,ug.activate(),lg.classList.remove(`hidden`)}function hg(){ug&&(ug.deactivate(),ug=null,dg=null),lg.classList.add(`hidden`)}var gg=document.createElement(`div`);gg.className=`absolute bottom-6 left-6 z-[100]`,gg.innerHTML=`
|
|
4871
4882
|
<button class="btn btn-ghost btn-square btn-sm text-base-content/60" title="Import File">
|
|
4872
4883
|
<span class="[&>svg]:size-5">${Fp}</span>
|
|
4873
4884
|
</button>
|
|
4874
|
-
`,yh.appendChild(
|
|
4885
|
+
`,yh.appendChild(gg);var _g=document.createElement(`div`);_g.className=`absolute bottom-16 left-6 z-[100] panel-bg border border-base-content/10 rounded-lg px-4 py-3 text-sm text-base-content/80 hidden`,yh.appendChild(_g);var vg=null;function yg(e,t){t?(_g.innerHTML=`
|
|
4875
4886
|
<div class="flex items-center gap-2">
|
|
4876
4887
|
<span>${e} <code class="bg-base-content/10 px-1.5 py-0.5 rounded text-base-content/90">${t}</code></span>
|
|
4877
4888
|
<button class="btn btn-ghost btn-square btn-xs text-base-content/60 import-toast-copy" title="Copy">
|
|
4878
4889
|
<span class="[&>svg]:size-3.5">${Ip}</span>
|
|
4879
4890
|
</button>
|
|
4880
4891
|
</div>
|
|
4881
|
-
`,
|
|
4892
|
+
`,_g.querySelector(`.import-toast-copy`).addEventListener(`click`,()=>{navigator.clipboard.writeText(t);let e=_g.querySelector(`.import-toast-copy`);e.setAttribute(`title`,`Copied!`),setTimeout(()=>e.setAttribute(`title`,`Copy`),1500)})):_g.textContent=e,_g.classList.remove(`hidden`),vg&&clearTimeout(vg),vg=setTimeout(()=>{_g.classList.add(`hidden`),vg=null},6e3)}var bg=document.createElement(`input`);bg.type=`file`,bg.accept=`.step,.stp`,bg.style.display=`none`,yh.appendChild(bg),gg.querySelector(`button`).addEventListener(`click`,()=>{bg.click()}),bg.addEventListener(`change`,async()=>{let e=bg.files?.[0];if(e){bg.value=``,Ch(`Importing file...`);try{let t=await e.arrayBuffer(),n=new Uint8Array(t),r=``;for(let e=0;e<n.length;e++)r+=String.fromCharCode(n[e]);let i=btoa(r),a=await fetch(`/api/import-file`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({fileName:e.name,data:i})}),o=await a.json();!a.ok||!o.success?yg(`Import failed: ${o.error||`Unknown error`}`):yg(`Imported! Use:`,`load('${o.fileName}')`)}catch{yg(`Import failed: network error`)}finally{wh()}}});async function xg(e,t,n){try{let r=await(await Tm($.sceneContext,n)).arrayBuffer(),i=new Uint8Array(r),a=``;for(let e=0;e<i.length;e++)a+=String.fromCharCode(i[e]);e.send(JSON.stringify({type:`screenshot-result`,requestId:t,success:!0,data:btoa(a)}))}catch(n){e.send(JSON.stringify({type:`screenshot-result`,requestId:t,success:!1,error:n.message||String(n)}))}}function Sg(){let e=`ws://${window.location.host}`,t=new WebSocket(e);t.addEventListener(`message`,e=>{let n=JSON.parse(e.data);switch(n.type){case`init-complete`:Ch(`Loading model...`);break;case`processing-file`:Ch(`Loading model...`);break;case`scene-rendered`:{wh();let e=n.rollbackStop!=null&&n.rollbackStop<n.result.length-1;$.isTrimming=!e&&Mh===`picking-active`,$.isBezierDrawing=!e&&fg(n.result),$.updateView(n.result,e),n.absPath&&$.setFileName(n.absPath),e?(Hh(),sg(),hg()):(Uh(n.result),cg(n.result),mg(n.result)),kh.update(n.result,n.rollbackStop??n.result.length-1,n.absPath),n.breakpointHit!==void 0&&Oh.setActive(n.breakpointHit);break}case`highlight-shape`:$.highlightShape(n.shapeId),Th.setSelectedShape(n.shapeId);break;case`clear-highlight`:$.clearHighlight(),Th.setSelectedShape(null),Eh.hide();break;case`show-shape-properties`:$.clearHighlight(),Eh.hide(),Th.show(n.shapeId);break;case`take-screenshot`:xg(t,n.requestId,n.options);break}}),t.addEventListener(`close`,()=>{setTimeout(Sg,1e3)})}Sg();
|