diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 8f9d9b9e..7014846a 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -2,6 +2,8 @@ name: 'docs-publish' on: push: branches: [ master ] + paths: + - 'docs/**' workflow_dispatch: diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index e9092fd2..5ec1acde 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -3,6 +3,8 @@ name: Test Electron App Production Build on: # Runs on pushes targeting the default branch push: + paths: + - '!docs/**' # Allows you to run this workflow manually from the Actions tab workflow_dispatch: diff --git a/.github/workflows/prettier-check.yml b/.github/workflows/prettier-check.yml index 90dfe288..0625c749 100644 --- a/.github/workflows/prettier-check.yml +++ b/.github/workflows/prettier-check.yml @@ -2,7 +2,7 @@ name: 'Docs Fomatting Check' on: push: paths: - - './docs/**' + - 'docs/**' workflow_dispatch: jobs: diff --git a/.github/workflows/prod-test.yml b/.github/workflows/prod-test.yml index aba0391f..3a07ffb4 100644 --- a/.github/workflows/prod-test.yml +++ b/.github/workflows/prod-test.yml @@ -3,6 +3,8 @@ name: Test NPM Production Build on: # Runs on pushes targeting the default branch push: + paths: + - '!docs/**' # Allows you to run this workflow manually from the Actions tab workflow_dispatch: diff --git a/conf/docusaurus.config.js b/conf/docusaurus.config.js index 316a7ece..8ff533cf 100644 --- a/conf/docusaurus.config.js +++ b/conf/docusaurus.config.js @@ -7,7 +7,28 @@ module.exports = { favicon: 'img/gs-logo.png', staticDirectories: ['./docs/static/'], // ... - plugins: [require.resolve('docusaurus-lunr-search')], + plugins: [ + require.resolve('docusaurus-lunr-search'), + ['@docusaurus/plugin-client-redirects', + { + redirects: [ + // /docs/oldDoc -> /docs/newDoc + { + to: '/kiri-moto', + from: '/projects/kiri-moto', + }, + // Redirect from multiple old paths to the new path + { + to: '/gridbot', + from: '/projects/gridbot', + }, + { + to: '/mesh-tool', + from: '/projects/mesh-tool', + }, + ], + }], + ], presets: [ [ '@docusaurus/preset-classic', diff --git a/conf/prettier.config.js b/conf/prettier.config.js index 5a5bf0eb..0ec558d1 100644 --- a/conf/prettier.config.js +++ b/conf/prettier.config.js @@ -8,12 +8,12 @@ const config = { useTabs: false, bracketSpacing: true, objectWrap: "preserve", - tabWidth: 4, - semi: false, - singleQuote: true, + tabWidth: 2, + semi: true, + singleQuote: false, bracketSameLine: true, arrowParens: "always", - endOfLine: "crlf" + endOfLine: "lf" } module.exports = config \ No newline at end of file diff --git a/docs/kiri-moto/CAM/interface.mdx b/docs/kiri-moto/CAM/interface.mdx new file mode 100644 index 00000000..c83cb992 --- /dev/null +++ b/docs/kiri-moto/CAM/interface.mdx @@ -0,0 +1,29 @@ +--- +description: Overview of CAM mode +--- + +import { ImageCarousel } from "@site/docs/src/components/carousel.js"; + +# Interface + +The CAM interface is, in most respects, the same as the [general interface](/kiri-moto/interface). It is highly recommended to familiarize yourself with the general interface before using the CAM interface. + +With that in mind, here are the key differences: + +## Operation List + +The Operation List defines the order in which operations are +performed on the model. Selecting the **plus** button will open a +popup menu for choosing an operation to add. The operations are +draggable, enabling easy re-ordering. + +- An operation can be disabled by **Ctrl+clicking** it. Disabled operations will be skipped in the slice/preview/export process. +- The thin blue bar in the Operation List is also draggable. Moving the bar automatically disables all operations below it. This allows you to slice, preview, and export up to a specific point in the timeline. + + + +## Origin Selection + +Origin Selection (introduced in v4.3) enables automatically setting an origin to points of interest on the model, including corners and arc centers. + +![](/img/CAM/interface/originSelect.png) diff --git a/docs/kiri-moto/CAM/machines.md b/docs/kiri-moto/CAM/machines.md index 3b49ea05..21676e2d 100644 --- a/docs/kiri-moto/CAM/machines.md +++ b/docs/kiri-moto/CAM/machines.md @@ -39,7 +39,6 @@ Customize how your G-code is formatted for your controller or post-processor: - **Enable Laser** — Enables [Laser](./ops#laser-operations) operations (typically uses `M3/M5`; adjusts movement strategy) - **File Extension** — Optional override for exported file type (e.g., `.nc`, `.ngc`, `.cnc`, `.cam`, `.gcode`, `.txt`) - --- ## G-code Macros diff --git a/docs/kiri-moto/interface.md b/docs/kiri-moto/interface.md deleted file mode 100644 index f02b8de3..00000000 --- a/docs/kiri-moto/interface.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -description: A tour of Kiri:Moto's web-based interface ---- - -# Interface - -Kiri:Moto's interface is designed for maximum access to the workspace and to present only the options that are necessary and relevant to the current action. - -The left menus control workspace, work mode, and objects while. - -The right menus control the behavior and settings for slicing, path routing, and path to code exports. - -[OBJ](https://www.cs.cmu.edu/~mbz/personal/graphics/obj.html), [STL](), and [3MF](https://docs.fileformat.com/3d/3mf/) files import natively. [SVG](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics), [JPG](https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format), and [PNG](https://en.wikipedia.org/wiki/Portable_Network_Graphics) are auto-extruded into a 3D parts. - -![A minimalist approach](/img/kiri-moto.png) - -In CNC mode, there is an additional storyboard menu at the bottom where you can plan and re-order milling operations. - -![](/img/kiri-moto-cnc.png) - -The Setup menu offers access to Device selection and customization. - -![Device Dialog](/img/device-dialog.png) - -Application preferences are mode-dependent. For example, CNC preferences show animation controls. - -![CNC Mode Preferences](/img/preferences-cam.png) - -![FDM Mode Preferences](/img/preferences-fdm.png) diff --git a/docs/kiri-moto/interface.mdx b/docs/kiri-moto/interface.mdx new file mode 100644 index 00000000..f372e032 --- /dev/null +++ b/docs/kiri-moto/interface.mdx @@ -0,0 +1,54 @@ +--- +description: A tour of Kiri:Moto's web-based interface +--- + +import { ImageCarousel } from "@site/docs/src/components/carousel.js"; + +# Interface + +Kiri:Moto's interface is designed for maximum access to the workspace, showing only the options necessary and relevant to the current action. + +[OBJ](https://www.cs.cmu.edu/~mbz/personal/graphics/obj.html), [STL](), and [3MF](https://docs.fileformat.com/3d/3mf/) files import natively. [SVG](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics), [JPG](https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format), and [PNG](https://en.wikipedia.org/wiki/Portable_Network_Graphics) files are auto-extruded into 3D parts. + +For the CAM interface, click [here](/kiri-moto/CAM). + +![A minimalist approach](/img/interface/main.png) + +## Top Menu + +- **Left:** Buttons to move through the `Arrange → Slice → Preview → Export` workflow. These steps are also available as [keyboard shortcuts](/kiri-moto/controls). Selecting an option here will automatically run prior steps as needed. +- **Center:** Clicking the [logo](/kiri-moto) opens a dialog with version selection, helpful links, and your current Kiri:Moto version. If you are unsure which version to use, check the [release notes](https://github.com/GridSpace/grid-apps/releases/) on GitHub. +- **Right:** Options for importing/exporting the workspace, manipulating the view, changing modes, and setting preferences. Kiri:Moto currently supports CAM, FDM, SLA, and LASER modes. Some other modes are extensions of the LASER workflow, but do not have first-class support. The [preferences menu](#preferences) will be covered later in this section. + - Changing the mode updates the left menu and enables/disables mode-specific options such as the `CAM Operation` menu and the `CAM Animate` step. + - Learn about K:M file types [here](/kiri-moto/faq#what-are-the-kmz-and-km-file-formats-short-for). + +## Left Menu + +The left menu contains options for the current mode. These vary widely: + +- CAM has ample documentation on [Process Options](/kiri-moto/CAM/processOpts). +- Other modes are less documented but many options are self-explanatory if you're familiar with the manufacturing process. +- Hovering over an option provides tooltips. When in doubt, a quick search usually explains the option in detail. + +## Right Menu + +The right menu contains controls for objects in the workspace. + +- Hovering over the arrow next to an object reveals options for deleting, renaming, disabling, or replacing it. +- Disabled objects remain visible but are ignored during slicing and exporting. + +## Machines Dialog + +![Device Dialog](/img/interface/CAMDevice.png) + +The device dialog is accessed through the settings dropdown. It allows you to configure a device or select a predefined one. + +- The dropdown only shows devices available for the current mode. +- Stock device settings are greyed out. To modify them, click `Customize`. +- Learn more about how K:M handles CAM devices [here](/kiri-moto/CAM/machines). + +## Preferences + +Application preferences are also accessed through the settings dropdown. These are mode-dependent but share many common options. For example, CNC preferences include animation controls. + + diff --git a/docs/sidebars.js b/docs/sidebars.js index 429f8036..89c74d27 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -35,6 +35,7 @@ const sidebars = { id: "kiri-moto/CAM/index", }, items: [ + "kiri-moto/CAM/interface", "kiri-moto/CAM/processOpts", "kiri-moto/CAM/machines", "kiri-moto/CAM/tools", diff --git a/docs/src/components/carousel.js b/docs/src/components/carousel.js index 49634d04..0174fc2d 100644 --- a/docs/src/components/carousel.js +++ b/docs/src/components/carousel.js @@ -4,59 +4,69 @@ import "react-responsive-carousel/lib/styles/carousel.min.css"; const imageDescripts = { outline: [ - ["/1.png", "outline of a complex part"], - ["/2.png", "outline of a non-flat part with inside holes"], - ["/3.png", "outside-only outline"], - ["/4.png", "inside-only outline"], + ["/1.png", "Outline of a complex part"], + ["/2.png", "Outline of a non-flat part with inside holes"], + ["/3.png", "Outside-only outline"], + ["/4.png", "Inside-only outline"], ], level: [ - ["/1.png", "level of a large part"], - ["/2.png", `level of a square part's stock`], - ["/3.png", `level of a part with a non-uniform top surface`], + ["/1.png", "Level of a large part"], + ["/2.png", `Level of a square part's stock`], + ["/3.png", `Level of a part with a non-uniform top surface`], ], rough: [ - ["/1.png", "rough cut of two parts"], - ["/2.png", "side-view of a rough cut"], - ["/3.png", "rough cut of a part with a complex top surface"], - ["/4.gif", "animate of complex rough cut"], + ["/1.png", "Rough cut of two parts"], + ["/2.png", "Side-view of a rough cut"], + ["/3.png", "Rough cut of a part with a complex top surface"], + ["/4.gif", "Animate of complex rough cut"], ], contour: [ - ["/1.png", "close-up of a precise contour"], - ["/2.png", "contour of multiple parts at once"], - ["/3.png", "low precision contour"], + ["/1.png", "Close-up of a precise contour"], + ["/2.png", "Contour of multiple parts at once"], + ["/3.png", "Low precision contour"], ], register: [ ["/1.png", "2-drill register"], ["/2.png", "3-drill register"], - ["/3.png", "jigsaw register"], + ["/3.png", "Jigsaw register"], ], drill: [ - ["/1.png", "drills for a robot baseplate"], - ["/2.png", "drills for a pegboard"], - ["/3.png", "selecting only matching-size holes"], + ["/1.png", "Drills for a robot baseplate"], + ["/2.png", "Drills for a pegboard"], + ["/3.png", "Selecting only matching-size holes"], ], trace: [ - ["/1.png", "toolpath tracing a design into a part"], - ["/2.png", "selecting loops to trace"], - ["/3.png", "tracing edges for deburring"], - ["/4.png", "trace to clear a pocket"], - ["/5.png", "arbitrary offset outline"], + ["/1.png", "Toolpath tracing a design into a part"], + ["/2.png", "Selecting loops to trace"], + ["/3.png", "Tracing edges for deburring"], + ["/4.png", "Trace to clear a pocket"], + ["/5.png", "Arbitrary offset outline"], ], pocket: [ - ["/1.png", "selecting areas to pocket"], - ["/2.png", "toolpath for pocket"], - ["/3.gif", "pocket animation"], - ["/4.png", "toolpath for v-bit carve"], - ["/5.png", "many-pocketed part"], + ["/1.png", "Selecting areas to pocket"], + ["/2.png", "Toolpath for pocket"], + ["/3.gif", "Pocket animation"], + ["/4.png", "Toolpath for v-bit carve"], + ["/5.png", "Many-pocketed part"], ], - gcode: [["/1.png", "the gcode editor popup"]], + gcode: [["/1.png", "The gcode editor popup"]], laserOn: [], laserOff: [], index: [], lathe: [], tabs: [ - ["/1.png", "tabs added to part"], - ["/2.png", "sliced part with tabs"], + ["/1.png", "Tabs added to part"], + ["/2.png", "Sliced part with tabs"], + ], + camInterface: [ + ["/1.png", "Empty Ops list"], + ["/2.png", "Sample Ops list"], + ["/3.png", "Ops list with history bar"], + ], + prefs: [ + ["/prefs-CAM.png", "Preferences in CAM mode"], + ["/prefs-FDM.png", "Preferences in FDM mode"], + ["/prefs-LASER.png", "Preferences in laser mode"], ], }; @@ -66,15 +76,17 @@ const imageDescripts = { * @param {keyof typeof imageDescripts} images - The key to an array of images and descriptions. * @returns {ReactElement} - A carousel of the given images. */ -export function ImageCarousel({ base, images }) { +export function ImageCarousel({ base, images, sml }) { return ( - - {imageDescripts[images].map(([image, caption], index) => ( -
- -

{caption}

-
- ))} -
+
+ + {imageDescripts[images].map(([image, caption], index) => ( +
+ +

{caption}

+
+ ))} +
+
); } diff --git a/docs/src/custom.css b/docs/src/custom.css index e51d1d87..23ac403b 100644 --- a/docs/src/custom.css +++ b/docs/src/custom.css @@ -5,3 +5,14 @@ .navbar .navbar__items { height: 4.5rem; } + +.carouselWrapper { + width: 100%; + display: flex; + justify-content: center; +} + +.carouselWrapper .carousel-root { + width: fit-content; + max-width: 20rem; +} diff --git a/docs/static/img/CAM/interface/1.png b/docs/static/img/CAM/interface/1.png new file mode 100644 index 00000000..2a75a236 Binary files /dev/null and b/docs/static/img/CAM/interface/1.png differ diff --git a/docs/static/img/CAM/interface/2.png b/docs/static/img/CAM/interface/2.png new file mode 100644 index 00000000..16b3e591 Binary files /dev/null and b/docs/static/img/CAM/interface/2.png differ diff --git a/docs/static/img/CAM/interface/3.png b/docs/static/img/CAM/interface/3.png new file mode 100644 index 00000000..4b29a334 Binary files /dev/null and b/docs/static/img/CAM/interface/3.png differ diff --git a/docs/static/img/CAM/interface/originSelect.png b/docs/static/img/CAM/interface/originSelect.png new file mode 100644 index 00000000..808e9570 Binary files /dev/null and b/docs/static/img/CAM/interface/originSelect.png differ diff --git a/docs/static/img/device-dialog.png b/docs/static/img/device-dialog.png deleted file mode 100644 index d282d2c0..00000000 Binary files a/docs/static/img/device-dialog.png and /dev/null differ diff --git a/docs/static/img/interface/CAMDevice.png b/docs/static/img/interface/CAMDevice.png new file mode 100644 index 00000000..4226882c Binary files /dev/null and b/docs/static/img/interface/CAMDevice.png differ diff --git a/docs/static/img/interface/main.png b/docs/static/img/interface/main.png new file mode 100644 index 00000000..a9bedf9d Binary files /dev/null and b/docs/static/img/interface/main.png differ diff --git a/docs/static/img/interface/prefs-CAM.png b/docs/static/img/interface/prefs-CAM.png new file mode 100644 index 00000000..d68372f5 Binary files /dev/null and b/docs/static/img/interface/prefs-CAM.png differ diff --git a/docs/static/img/interface/prefs-FDM.png b/docs/static/img/interface/prefs-FDM.png new file mode 100644 index 00000000..bb947612 Binary files /dev/null and b/docs/static/img/interface/prefs-FDM.png differ diff --git a/docs/static/img/interface/prefs-LASER.png b/docs/static/img/interface/prefs-LASER.png new file mode 100644 index 00000000..363cfbfe Binary files /dev/null and b/docs/static/img/interface/prefs-LASER.png differ diff --git a/docs/static/img/preferences-cam.png b/docs/static/img/preferences-cam.png deleted file mode 100644 index d0236bf3..00000000 Binary files a/docs/static/img/preferences-cam.png and /dev/null differ diff --git a/docs/static/img/preferences-fdm.png b/docs/static/img/preferences-fdm.png deleted file mode 100644 index fb521b82..00000000 Binary files a/docs/static/img/preferences-fdm.png and /dev/null differ diff --git a/package.json b/package.json index 1e56a302..08eba04b 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "node": ">=22.0.0" }, "dependencies": { + "@docusaurus/plugin-client-redirects": "^3.9.0", "@fortawesome/fontawesome-free": "^6.1.1", "@gridspace/app-server": "^0.0.20", "@gridspace/basic-ftp": "github:gridspace/basic-ftp#v5.0.5-gridspace", @@ -68,8 +69,8 @@ "ws": "^8.18.2" }, "devDependencies": { - "@docusaurus/core": "^3.7.0", - "@docusaurus/preset-classic": "^3.7.0", + "@docusaurus/core": "^3.9.0", + "@docusaurus/preset-classic": "^3.9.0", "@electron/notarize": "^3.0.0", "copy-webpack-plugin": "^12.0.2", "cross-env": "^10.1.0", diff --git a/src/kiri/mode/cam/cl-hole.js b/src/kiri/mode/cam/cl-hole.js index 8ebf6e56..77d703ab 100644 --- a/src/kiri/mode/cam/cl-hole.js +++ b/src/kiri/mode/cam/cl-hole.js @@ -7,6 +7,9 @@ import { lastTrace, setLastTrace } from './cl-trace.js'; export let holeSelOn = false; export let lastSelHoles; +let holeGeo = new THREE.CylinderGeometry(1,1,1,20); +let holeMaterial = new THREE.MeshPhongMaterial( ); +let alert; /** * Client side function to select holes in widgets for CAM operations. @@ -21,7 +24,7 @@ export async function selectHoles(individual) { return selectHolesDone(); } clearPops(); - let alert = api.show.alert("analyzing parts..."); + alert = api.show.alert("analyzing parts..."); holeSelOn = env.hoveredOp; holeSelOn.classList.add("editing"); api.feature.hover = true; @@ -33,48 +36,38 @@ export async function selectHoles(individual) { /** * creates a mesh for a hole and adds it to a widget * @param {Object3D} widget - widget to add the hole mesh to - * @param {Object} drill - {depth,selected} object of the hole + * @param {Object} drills - {depth,selected}[] object of the hole * @returns {Mesh} the created mesh */ - function createHoleMesh(widget, drill) { - let { depth, selected, diam } = drill - let color = selected ? 0xFF0000 : 0x39e366 - let geo = new THREE.CylinderGeometry(diam / 2, diam / 2, depth, 20); - const material = new THREE.MeshPhongMaterial({ color }); - let mesh = new THREE.Mesh(geo, material); - mesh.position.copy(drill) - mesh.position.z -= depth / 2 - mesh.rotation.x = Math.PI / 2 - drill.widgetID = widget.id - drill.meshId = mesh.id; // add pointers to both objects - mesh.hole = drill; - mesh.parent = widget.mesh; - widget.mesh.add(mesh); - widget.adds.push(mesh); // for click detection - return mesh + function createHoleMesh(widget,drills){ + if(widget.holeMesh) widget.holeMesh.dispose() + widget.holeMesh = new THREE.InstancedMesh(holeGeo, holeMaterial,drills.length); + let baseMx = new THREE.Matrix4() + + for(let [i,drill] of drills.entries()){ + let { depth, selected, diam } = drill + let color = selected ? 0xFF0000 : 0x39e366 + let mx = new THREE.Matrix4(); + mx.copy( baseMx ); + mx.multiply( new THREE.Matrix4().makeTranslation(drill.x, drill.y, drill.z-depth/2)); + mx.multiply( new THREE.Matrix4().makeRotationX(Math.PI / 2)) + mx.multiply( new THREE.Matrix4().makeScale(diam /2, depth,diam /2)); + widget.holeMesh.setMatrixAt(i, mx); + widget.holeMesh.setColorAt(i, new THREE.Color(color)); + drill.widgetID = widget.id + } + widget.mesh.add(widget.holeMesh); + widget.adds.push(widget.holeMesh); // for click detection + return widget.holeMesh } let meshesCached = widgets.every(widget => env.poppedRec.drills[widget.id] != undefined) if (individual && meshesCached) { // if any widget already has cached holes - // console.log("already has cached holes",env.poppedRec.drills) api.hide.alert(alert); api.widgets.for(widget => { if (widget.adds) { - let drills = env.poppedRec.drills[widget.id]; - let ids = drills.map(hole => hole.meshId); - if (false && widget.adds.includes(mesh)) { - // mesh in the old code was a dead artifact - // so this code branch never actually ran - widget.adds.forEach(add => { - if (ids.includes(add.id)) { - add.visible = true; - } - }) - } else { - drills.forEach(drill => { - createHoleMesh(widget, drill); - }); - } + let drills = env.poppedRec.drills[widget.id]; + createHoleMesh(widget, drills); } }) } else { @@ -86,7 +79,7 @@ export async function selectHoles(individual) { (progress, msg) => { alert2[0] = msg; api.show.progress(progress, msg); - api.alerts.update(); + // api.alerts.update(); }, async centers => { api.show.progress(0); @@ -108,17 +101,10 @@ export async function selectHoles(individual) { api.widgets.for(widget => { const { holes } = centers.find(center => center.id == widget.id); // console.log(holes) - if (!holes.length) unselectHoles(holes); - holes.forEach(hole => { - createHoleMesh(widget, hole); - }); + createHoleMesh(widget, holes); //add hole data to record env.poppedRec.drills = env.poppedRec.drills ?? {}; - env.poppedRec.drills[widget.id] = holes; - //give widget access to an array of drill records that refrence it - //so that it can be cleared when widget is rotated or mirrored etc. - if (!widget.drills) { widget.drills = [] }; - widget.drills.push(holes); + env.poppedRec.drills[widget.id] = holes; }) } ); @@ -140,24 +126,27 @@ export async function selectHoles(individual) { export function selectHolesHover(data) { //not used right now. may be useful in the future - } export function selectHolesHoverUp(int, ev) { if (!int) return; //if not a hole mesh return - let { object } = int; - selectHoleToggle(object); + let { instanceId, object } = int; + selectHoleToggle(instanceId, object); } /** * Toggle the selection of a hole mesh and update its color * @param {Object3D} mesh - the hole mesh to toggle */ -export function selectHoleToggle(mesh) { - let { hole } = mesh - if (!hole) return - hole.selected = !hole.selected; - mesh.material.color.setHex(hole.selected ? 0xFF0000 : 0x39e366); +export function selectHoleToggle(id,mesh) { + + if (id === undefined) return + let widget = mesh.parent.widget; + if(env.poppedRec.drills[widget.id][id].selected !== undefined){ + env.poppedRec.drills[widget.id][id].selected = !env.poppedRec.drills[widget.id][id].selected + widget.holeMesh.setColorAt(id, new THREE.Color(env.poppedRec.drills[widget.id][id].selected ? 0xFF0000 : 0x39e366)); + widget.holeMesh.instanceColor.needsUpdate = true + } } /** @@ -166,12 +155,9 @@ export function selectHoleToggle(mesh) { * @param {Object} widget - the widget with the drills array to clear */ export function clearHolesRec(widget) { - if (widget.drills) { - widget.drills.forEach(rec => { - }) - } if (widget.adds) { widget.adds.length = 0 //clear adds array + delete env.poppedRec.drills[widget.id] } } @@ -199,10 +185,3 @@ export function selectHolesDone() { } }); } - -export function unselectHoles(widget) { - if (!widget.holes) return - widget.holes.forEach(hole => { - hole.selected = false - }) -}