Merge branch 'rel-4.2' into dev-4.2-refactor-module
This commit is contained in:
commit
ed722edff7
12 changed files with 256 additions and 130 deletions
23
.github/workflows/prettier-check.yml
vendored
Normal file
23
.github/workflows/prettier-check.yml
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
name: 'Docs Fomatting Check'
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- './docs/**'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
format-check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Setup bun
|
||||||
|
uses: oven-sh/setup-bun@v2
|
||||||
|
with:
|
||||||
|
bun-version: latest
|
||||||
|
- name: Install dependencies
|
||||||
|
run: bun install
|
||||||
|
- name: check format for docs
|
||||||
|
run:
|
||||||
|
bun docs-check
|
||||||
|
|
@ -16,13 +16,13 @@ you can view some example of how to use the engine [here](https://grid.space/kir
|
||||||
## Methods
|
## Methods
|
||||||
|
|
||||||
| Method | Description | Returns |
|
| Method | Description | Returns |
|
||||||
| --------------------------- | -------------------------------------------------------------------------------------------------------------- | ----------------- | ------- | ------- | -------- |
|
| --------------------------- | -------------------------------------------------------------------------------------------------------------- | ----------------- |
|
||||||
| `load(url)` | Loads an [STL](<https://en.wikipedia.org/wiki/STL_(file_format)>) file from a given URL and centers the model. | `Promise<Engine>` |
|
| `load(url)` | Loads an [STL](<https://en.wikipedia.org/wiki/STL_(file_format)>) file from a given URL and centers the model. | `Promise<Engine>` |
|
||||||
| `clear()` | Clears all objects from workspace. | `void` |
|
| `clear()` | Clears all objects from workspace. | `void` |
|
||||||
| `parse(data)` | Parses raw [STL](<https://en.wikipedia.org/wiki/STL_(file_format)>) data and loads it as a centered widget. | `Promise<Engine>` |
|
| `parse(data)` | Parses raw [STL](<https://en.wikipedia.org/wiki/STL_(file_format)>) data and loads it as a centered widget. | `Promise<Engine>` |
|
||||||
| `setListener(listener)` | Sets an event listener function to receive engine progress updates | `Engine` |
|
| `setListener(listener)` | Sets an event listener function to receive engine progress updates | `Engine` |
|
||||||
| `setRender(bool)` | Enables or disables rendering. | `Engine` |
|
| `setRender(bool)` | Enables or disables rendering. | `Engine` |
|
||||||
| `setMode(mode)` | Sets the slicing mode where mode is "CAM" | "FDM" | "LASER" | "SLA" . | `Engine` |
|
| `setMode(mode)` | Sets the slicing mode where mode is "CAM" \| "FDM" \| "LASER" \| "SLA" . | `Engine` |
|
||||||
| `setDevice(device)` | Merges a custom device profile into the current settings. | `Engine` |
|
| `setDevice(device)` | Merges a custom device profile into the current settings. | `Engine` |
|
||||||
| `setProcess(process)` | Merges custom slicing process parameters. | `Engine` |
|
| `setProcess(process)` | Merges custom slicing process parameters. | `Engine` |
|
||||||
| `setController(controller)` | Sets the slicing controller settings and starts/stops the worker pool accordingly. | `Engine` |
|
| `setController(controller)` | Sets the slicing controller settings and starts/stops the worker pool accordingly. | `Engine` |
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ id: mesh-tool
|
||||||
|
|
||||||
[Mesh:Tool](https://grid.space/mesh) is an intentionally simplified tool designed to address the most common cases of mesh editing and repair. It is under active development with new features daily or weekly. This means the docs can get out of date quickly.
|
[Mesh:Tool](https://grid.space/mesh) is an intentionally simplified tool designed to address the most common cases of mesh editing and repair. It is under active development with new features daily or weekly. This means the docs can get out of date quickly.
|
||||||
|
|
||||||
Mesh:Tool is part of the [Grid.Apps GitHub Repo](https://github.com/GridSpace/grid-apps) and is Open Source under the [MIT License](https://en.wikipedia.org/wiki/MIT\_License). It runs entirely in browser, is not connected to the cloud, and does not collect or share any model data. [Desktop builds](https://github.com/GridSpace/grid-apps/releases) are available whiel start in the Slicer view. Use the central drop-menu to switch to Mesh:Tool.
|
Mesh:Tool is part of the [Grid.Apps GitHub Repo](https://github.com/GridSpace/grid-apps) and is Open Source under the [MIT License](https://en.wikipedia.org/wiki/MIT_License). It runs entirely in browser, is not connected to the cloud, and does not collect or share any model data. [Desktop builds](https://github.com/GridSpace/grid-apps/releases) are available whiel start in the Slicer view. Use the central drop-menu to switch to Mesh:Tool.
|
||||||
|
|
||||||
Browse the Mesh:Tool [video playlist](https://www.youtube.com/playlist?list=PLRoVgyRoWZps84Scj5wQ2LYK-wMu-7r0L) on YouTube
|
Browse the Mesh:Tool [video playlist](https://www.youtube.com/playlist?list=PLRoVgyRoWZps84Scj5wQ2LYK-wMu-7r0L) on YouTube
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ const sidebars = {
|
||||||
{
|
{
|
||||||
type: "doc",
|
type: "doc",
|
||||||
id: "index",
|
id: "index",
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "category",
|
type: "category",
|
||||||
|
|
@ -14,8 +13,8 @@ const sidebars = {
|
||||||
type: "category",
|
type: "category",
|
||||||
label: "Kiri Moto",
|
label: "Kiri Moto",
|
||||||
link: {
|
link: {
|
||||||
type: 'doc',
|
type: "doc",
|
||||||
id: 'kiri-moto/index',
|
id: "kiri-moto/index",
|
||||||
},
|
},
|
||||||
items: [
|
items: [
|
||||||
"kiri-moto/interface",
|
"kiri-moto/interface",
|
||||||
|
|
@ -31,17 +30,17 @@ const sidebars = {
|
||||||
{
|
{
|
||||||
type: "category",
|
type: "category",
|
||||||
label: "CAM Mode",
|
label: "CAM Mode",
|
||||||
link:{
|
link: {
|
||||||
type: 'doc',
|
type: "doc",
|
||||||
id: 'kiri-moto/CAM/index',
|
id: "kiri-moto/CAM/index",
|
||||||
},
|
},
|
||||||
items:[
|
items: [
|
||||||
"kiri-moto/CAM/processOpts",
|
"kiri-moto/CAM/processOpts",
|
||||||
"kiri-moto/CAM/machines",
|
"kiri-moto/CAM/machines",
|
||||||
"kiri-moto/CAM/tools",
|
"kiri-moto/CAM/tools",
|
||||||
"kiri-moto/CAM/ops",
|
"kiri-moto/CAM/ops",
|
||||||
],
|
],
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -52,39 +51,37 @@ const sidebars = {
|
||||||
type: "category",
|
type: "category",
|
||||||
label: "GridBot",
|
label: "GridBot",
|
||||||
link: {
|
link: {
|
||||||
type: 'doc',
|
type: "doc",
|
||||||
id: 'gridbot/index',
|
id: "gridbot/index",
|
||||||
|
},
|
||||||
|
items: ["gridbot/bom"],
|
||||||
},
|
},
|
||||||
items: [
|
|
||||||
"gridbot/bom",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'category',
|
type: "category",
|
||||||
label: 'Community',
|
label: "Community",
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
type: 'link',
|
type: "link",
|
||||||
label: 'Forums',
|
label: "Forums",
|
||||||
href: 'https://forum.grid.space',
|
href: "https://forum.grid.space",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'link',
|
type: "link",
|
||||||
label: 'Discord',
|
label: "Discord",
|
||||||
href: 'https://discord.gg/suyCCgr',
|
href: "https://discord.gg/suyCCgr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'link',
|
type: "link",
|
||||||
label: 'GitHub',
|
label: "GitHub",
|
||||||
href: 'https://github.com/GridSpace/grid-apps',
|
href: "https://github.com/GridSpace/grid-apps",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'link',
|
type: "link",
|
||||||
label: 'BlueSky',
|
label: "BlueSky",
|
||||||
href: 'https://bsky.app/profile/grid.space',
|
href: "https://bsky.app/profile/grid.space",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
@ -104,4 +101,3 @@ const sidebars = {
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = sidebars;
|
module.exports = sidebars;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "grid-apps",
|
"name": "grid-apps",
|
||||||
"version": "4.1.9",
|
"version": "4.2.0",
|
||||||
"description": "grid.space 3d slicing & modeling tools",
|
"description": "grid.space 3d slicing & modeling tools",
|
||||||
"author": "Stewart Allen <sa@grid.space>",
|
"author": "Stewart Allen <sa@grid.space>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|
@ -50,6 +50,7 @@
|
||||||
"jszip": "^3.7.1",
|
"jszip": "^3.7.1",
|
||||||
"manifold-3d": "^3.1.1",
|
"manifold-3d": "^3.1.1",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
"prettier": "^3.5.3",
|
||||||
"react-responsive-carousel": "^3.2.23",
|
"react-responsive-carousel": "^3.2.23",
|
||||||
"serve-static": "^1.14.1",
|
"serve-static": "^1.14.1",
|
||||||
"three": "^0.174.0",
|
"three": "^0.174.0",
|
||||||
|
|
@ -99,7 +100,8 @@
|
||||||
"preinstall2": "npx webpack --config src2/webpack/webpack-three-esm.js",
|
"preinstall2": "npx webpack --config src2/webpack/webpack-three-esm.js",
|
||||||
"docs-dev": "docusaurus start",
|
"docs-dev": "docusaurus start",
|
||||||
"docs-build": "docusaurus build",
|
"docs-build": "docusaurus build",
|
||||||
"docs-serve": "docusaurus serve"
|
"docs-serve": "docusaurus serve",
|
||||||
|
"docs-check": "prettier ./docs --check"
|
||||||
},
|
},
|
||||||
"main": "app-el.js",
|
"main": "app-el.js",
|
||||||
"build": {
|
"build": {
|
||||||
|
|
|
||||||
|
|
@ -1002,6 +1002,23 @@ class Polygon {
|
||||||
return this.z !== undefined ? this.z : this.points[i || 0]?.z || 0;
|
return this.z !== undefined ? this.z : this.points[i || 0]?.z || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
minZ() {
|
||||||
|
return Math.min(...this.points.map(p => p.z));
|
||||||
|
}
|
||||||
|
|
||||||
|
avgZ() {
|
||||||
|
return [...this.points.map(p => p.z)].reduce((a,v) => a+v) / this.points.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {*} z value target
|
||||||
|
* @param {*} epsilon allowed Z variance
|
||||||
|
* @returns {boolean} true if all points Z within epsilon of value
|
||||||
|
*/
|
||||||
|
onZ(z, epsilon = 10e-4) {
|
||||||
|
return Math.max(...this.points.map(p => Math.abs(z - p.z))) < epsilon;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
render(layer, color, recursive, open) {
|
render(layer, color, recursive, open) {
|
||||||
|
|
|
||||||
|
|
@ -1008,7 +1008,6 @@ CAM.init = function(kiri, api) {
|
||||||
CAM.traces((ids) => {
|
CAM.traces((ids) => {
|
||||||
api.hide.alert(alert);
|
api.hide.alert(alert);
|
||||||
alert = api.show.alert("[esc] cancels trace editing");
|
alert = api.show.alert("[esc] cancels trace editing");
|
||||||
kiri.api.widgets.opacity(0.8);
|
|
||||||
kiri.api.widgets.for(widget => {
|
kiri.api.widgets.for(widget => {
|
||||||
if (ids.indexOf(widget.id) >= 0) {
|
if (ids.indexOf(widget.id) >= 0) {
|
||||||
unselectTraces(widget, true);
|
unselectTraces(widget, true);
|
||||||
|
|
@ -1042,6 +1041,7 @@ CAM.init = function(kiri, api) {
|
||||||
});
|
});
|
||||||
// ensure appropriate traces are toggled matching current record
|
// ensure appropriate traces are toggled matching current record
|
||||||
kiri.api.widgets.for(widget => {
|
kiri.api.widgets.for(widget => {
|
||||||
|
widget.setVisualState({ opacity: 0.25 });
|
||||||
let areas = (poppedRec.areas[widget.id] || []);
|
let areas = (poppedRec.areas[widget.id] || []);
|
||||||
let stack = widget.trace_stack;
|
let stack = widget.trace_stack;
|
||||||
stack.meshes.forEach(mesh => {
|
stack.meshes.forEach(mesh => {
|
||||||
|
|
@ -1070,6 +1070,7 @@ CAM.init = function(kiri, api) {
|
||||||
api.feature.hover = false;
|
api.feature.hover = false;
|
||||||
api.feature.hoverAdds = false;
|
api.feature.hoverAdds = false;
|
||||||
kiri.api.widgets.for(widget => {
|
kiri.api.widgets.for(widget => {
|
||||||
|
widget.restoreVisualState();
|
||||||
if (widget.trace_stack) {
|
if (widget.trace_stack) {
|
||||||
widget.trace_stack.hide();
|
widget.trace_stack.hide();
|
||||||
widget.adds.removeAll(widget.trace_stack.meshes);
|
widget.adds.removeAll(widget.trace_stack.meshes);
|
||||||
|
|
@ -1096,17 +1097,15 @@ CAM.init = function(kiri, api) {
|
||||||
color.r = colorSave.r;
|
color.r = colorSave.r;
|
||||||
color.g = colorSave.g;
|
color.g = colorSave.g;
|
||||||
color.b = colorSave.b;
|
color.b = colorSave.b;
|
||||||
lastTrace.position.z -= 0.01;
|
|
||||||
}
|
}
|
||||||
if (data.type === 'platform') {
|
|
||||||
lastTrace = null;
|
lastTrace = null;
|
||||||
|
if (data.type === 'platform') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!data.int.object.trace) {
|
if (!data.int.object.trace) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
lastTrace = data.int.object;
|
lastTrace = data.int.object;
|
||||||
lastTrace.position.z += 0.01;
|
|
||||||
if (lastTrace.selected) {
|
if (lastTrace.selected) {
|
||||||
let event = data.event;
|
let event = data.event;
|
||||||
let target = event.target;
|
let target = event.target;
|
||||||
|
|
@ -1115,11 +1114,8 @@ CAM.init = function(kiri, api) {
|
||||||
}
|
}
|
||||||
let material = lastTrace.material[0] || lastTrace.material;
|
let material = lastTrace.material[0] || lastTrace.material;
|
||||||
let color = material.color;
|
let color = material.color;
|
||||||
let {r, g, b} = color;
|
material.colorSave = color.clone();
|
||||||
material.colorSave = {r, g, b};
|
color.setHex(isDark() ? 0x0066ff : 0x0000ff);
|
||||||
color.r = 0;
|
|
||||||
color.g = 0;
|
|
||||||
color.b = 1;
|
|
||||||
};
|
};
|
||||||
func.traceHoverUp = function(int, ev) {
|
func.traceHoverUp = function(int, ev) {
|
||||||
if (!int) return;
|
if (!int) return;
|
||||||
|
|
@ -1128,8 +1124,9 @@ CAM.init = function(kiri, api) {
|
||||||
if (ev.metaKey || ev.ctrlKey) {
|
if (ev.metaKey || ev.ctrlKey) {
|
||||||
let { selected } = object;
|
let { selected } = object;
|
||||||
let { widget, poly } = object.trace;
|
let { widget, poly } = object.trace;
|
||||||
|
let avgZ = poly.avgZ();
|
||||||
for (let add of widget.adds) {
|
for (let add of widget.adds) {
|
||||||
if (add.trace && add.selected !== selected && add.trace.poly.getZ() === poly.getZ()) {
|
if (add.trace && add.selected !== selected && add.trace.poly.onZ(avgZ)) {
|
||||||
func.traceToggle(add);
|
func.traceToggle(add);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1147,23 +1144,17 @@ CAM.init = function(kiri, api) {
|
||||||
let wlist = areas[widget.id] = areas[widget.id] || [];
|
let wlist = areas[widget.id] = areas[widget.id] || [];
|
||||||
obj.selected = !obj.selected;
|
obj.selected = !obj.selected;
|
||||||
if (!colorSave) {
|
if (!colorSave) {
|
||||||
colorSave = material.colorSave = {
|
colorSave = material.colorSave = color.clone();
|
||||||
r: color.r,
|
|
||||||
g: color.g,
|
|
||||||
b: color.b
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
if (obj.selected) {
|
if (obj.selected) {
|
||||||
obj.position.z += 0.01;
|
color.setHex(isDark() ? 0xdd0011 : 0xff0033);
|
||||||
color.r = colorSave.r = 0.9;
|
colorSave.r = color.r;
|
||||||
color.g = colorSave.g = 0;
|
colorSave.g = color.g;
|
||||||
color.b = colorSave.b = 0.1;
|
colorSave.b = color.b;
|
||||||
if (!skip) wlist.push(poly._trace);
|
if (!skip) wlist.push(poly._trace);
|
||||||
} else {
|
} else {
|
||||||
obj.position.z -= 0.01;
|
color.setHex(0xaaaa55);
|
||||||
color.r = colorSave.r = 0xaa/255;
|
colorSave.setHex(0xaaaa55);
|
||||||
color.g = colorSave.g = 0xaa/255;
|
|
||||||
color.b = colorSave.b = 0x55/255;
|
|
||||||
if (!skip) wlist.remove(poly._trace);
|
if (!skip) wlist.remove(poly._trace);
|
||||||
}
|
}
|
||||||
API.conf.save();
|
API.conf.save();
|
||||||
|
|
@ -1192,9 +1183,6 @@ CAM.init = function(kiri, api) {
|
||||||
func.hover = func.selectHolesHover;
|
func.hover = func.selectHolesHover;
|
||||||
func.hoverUp = func.selectHolesHoverUp;
|
func.hoverUp = func.selectHolesHoverUp;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const widgets = kiri.api.widgets.all()
|
const widgets = kiri.api.widgets.all()
|
||||||
/**
|
/**
|
||||||
* creates a mesh for a hole and adds it to a widget
|
* creates a mesh for a hole and adds it to a widget
|
||||||
|
|
@ -1712,13 +1700,11 @@ CAM.init = function(kiri, api) {
|
||||||
dogbone: 'camTraceDogbone',
|
dogbone: 'camTraceDogbone',
|
||||||
revbone: 'camTraceDogbone',
|
revbone: 'camTraceDogbone',
|
||||||
merge: 'camTraceMerge',
|
merge: 'camTraceMerge',
|
||||||
select: 'camTraceMode',
|
|
||||||
ov_topz: 0,
|
ov_topz: 0,
|
||||||
ov_botz: 0,
|
ov_botz: 0,
|
||||||
ov_conv: '~camConventional',
|
ov_conv: '~camConventional',
|
||||||
}).inputs = {
|
}).inputs = {
|
||||||
tool: UC.newSelect(LANG.cc_tool, {}, "tools"),
|
tool: UC.newSelect(LANG.cc_tool, {}, "tools"),
|
||||||
select: UC.newSelect(LANG.cc_sele_s, {title:LANG.cc_sele_l}, "select"),
|
|
||||||
mode: UC.newSelect(LANG.cu_type_s, {title:LANG.cu_type_l}, "trace"),
|
mode: UC.newSelect(LANG.cu_type_s, {title:LANG.cu_type_l}, "trace"),
|
||||||
offset: UC.newSelect(LANG.cc_offs_s, {title: LANG.cc_offs_l, show:() => (poppedRec.mode === 'follow')}, "traceoff"),
|
offset: UC.newSelect(LANG.cc_offs_s, {title: LANG.cc_offs_l, show:() => (poppedRec.mode === 'follow')}, "traceoff"),
|
||||||
sep: UC.newBlank({class:"pop-sep"}),
|
sep: UC.newBlank({class:"pop-sep"}),
|
||||||
|
|
@ -1969,10 +1955,12 @@ function createPopOp(type, map) {
|
||||||
const settings = conf.get();
|
const settings = conf.get();
|
||||||
const {tool} = new CAM.Tool(settings,op.rec.tool); //get tool by id
|
const {tool} = new CAM.Tool(settings,op.rec.tool); //get tool by id
|
||||||
const opType = op.rec.type
|
const opType = op.rec.type
|
||||||
if( opType != "drill" && tool.type == "drill"){
|
const drillOrRegister = opType == "drill" || opType == "register"
|
||||||
|
|
||||||
|
if ( !drillOrRegister && tool.type == "drill"){
|
||||||
alerts.show(`Warning: Drills should not be used for ${opType} operations.`)
|
alerts.show(`Warning: Drills should not be used for ${opType} operations.`)
|
||||||
}
|
}
|
||||||
else if( opType == "drill" && tool.type != "drill"){
|
else if ( drillOrRegister && tool.type != "drill"){
|
||||||
alerts.show(`Warning: Only drills should be used for drilling operations.`)
|
alerts.show(`Warning: Only drills should be used for drilling operations.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2160,16 +2148,18 @@ function validateTools(tools) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isDark() { return API.space.is_dark() };
|
||||||
|
|
||||||
function addbox() { return FDM.addbox(...arguments)};
|
function addbox() { return FDM.addbox(...arguments)};
|
||||||
|
|
||||||
function delbox() { return FDM.delbox(...arguments)};
|
function delbox() { return FDM.delbox(...arguments)};
|
||||||
|
|
||||||
function boxColor() {
|
function boxColor() {
|
||||||
return API.space.is_dark() ? 0x00ddff : 0x0000dd;
|
return isDark() ? 0x00ddff : 0x0000dd;
|
||||||
}
|
}
|
||||||
|
|
||||||
function boxOpacity() {
|
function boxOpacity() {
|
||||||
return API.space.is_dark() ? 0.75 : 0.6;
|
return isDark() ? 0.75 : 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
function animate() {
|
function animate() {
|
||||||
|
|
@ -2252,7 +2242,7 @@ function updateStock() {
|
||||||
camStock.position.z = center.z;
|
camStock.position.z = center.z;
|
||||||
camStock.rotation.x = currentIndex || 0;
|
camStock.rotation.x = currentIndex || 0;
|
||||||
camStock.lines.material.color =
|
camStock.lines.material.color =
|
||||||
new THREE.Color(API.space.is_dark() ? 0x555555 : 0xaaaaaa);
|
new THREE.Color(isDark() ? 0x555555 : 0xaaaaaa);
|
||||||
} else if (camStock) {
|
} else if (camStock) {
|
||||||
SPACE.world.remove(camStock);
|
SPACE.world.remove(camStock);
|
||||||
camStock = null;
|
camStock = null;
|
||||||
|
|
|
||||||
|
|
@ -976,11 +976,12 @@ class OpTrace extends CamOp {
|
||||||
}
|
}
|
||||||
|
|
||||||
async slice(progress) {
|
async slice(progress) {
|
||||||
|
const debug = false;
|
||||||
let { op, state } = this;
|
let { op, state } = this;
|
||||||
let { tool, rate, down, plunge, offset, offover, thru } = op;
|
let { tool, rate, down, plunge, offset, offover, thru } = op;
|
||||||
let { ov_conv } = op;
|
let { ov_conv } = op;
|
||||||
let { settings, widget, addSlices, zThru, tabs, workarea } = state;
|
let { settings, widget, addSlices, zThru, tabs, workarea } = state;
|
||||||
let { updateToolDiams, cutTabs, cutPolys, healPolys, color } = state;
|
let { updateToolDiams, cutTabs, cutPolys, healPolys, color, shadowAt } = state;
|
||||||
let { process, stock } = settings;
|
let { process, stock } = settings;
|
||||||
let { camStockClipTo } = process;
|
let { camStockClipTo } = process;
|
||||||
if (state.isIndexed) {
|
if (state.isIndexed) {
|
||||||
|
|
@ -1042,17 +1043,39 @@ class OpTrace extends CamOp {
|
||||||
.setLayer("trace follow", {line: color}, false)
|
.setLayer("trace follow", {line: color}, false)
|
||||||
.addPolys(slice.camLines)
|
.addPolys(slice.camLines)
|
||||||
}
|
}
|
||||||
function clearZ(polys, z, down) {
|
function clearZnew(polys, z, down) {
|
||||||
|
if (down) {
|
||||||
|
// adjust step down to a value <= down that
|
||||||
|
// ends on the lowest z specified
|
||||||
|
let diff = zTop - z;
|
||||||
|
down = diff / Math.ceil(diff / down);
|
||||||
|
}
|
||||||
let zs = down ? base.util.lerp(zTop, z, down) : [ z ];
|
let zs = down ? base.util.lerp(zTop, z, down) : [ z ];
|
||||||
let nested = POLY.nest(polys);
|
let zpro = 0, zinc = 1 / (polys.length * zs.length);
|
||||||
for (let poly of nested) {
|
for (let poly of polys) {
|
||||||
|
// newPocket();
|
||||||
for (let z of zs) {
|
for (let z of zs) {
|
||||||
|
let clip = [], shadow;
|
||||||
|
shadow = shadowAt(z);
|
||||||
|
POLY.subtract([ poly ], shadow, clip, undefined, undefined, 0);
|
||||||
|
if (op.outline) {
|
||||||
|
POLY.clearInner(clip);
|
||||||
|
}
|
||||||
|
if (clip.length === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let slice = newSliceOut(z);
|
let slice = newSliceOut(z);
|
||||||
|
let count = 999;
|
||||||
slice.camTrace = { tool, rate, plunge };
|
slice.camTrace = { tool, rate, plunge };
|
||||||
POLY.offset([ poly ], [ -toolDiam/2, -toolOver ], {
|
if (toolDiam) {
|
||||||
count:999, outs: slice.camLines = [], flat:true, z,
|
const offs = [ -toolDiam / 2, -toolOver ];
|
||||||
minArea: 0
|
POLY.offset(clip, offs, {
|
||||||
|
count, outs: slice.camLines = [], flat:true, z, minArea: 0
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// when engraving with a 0 width tip
|
||||||
|
slice.camLines = clip;
|
||||||
|
}
|
||||||
if (tabs) {
|
if (tabs) {
|
||||||
slice.camLines = cutTabs(tabs, POLY.flatten(slice.camLines, null, true), z);
|
slice.camLines = cutTabs(tabs, POLY.flatten(slice.camLines, null, true), z);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1060,8 +1083,14 @@ class OpTrace extends CamOp {
|
||||||
}
|
}
|
||||||
POLY.setWinding(slice.camLines, cutdir, false);
|
POLY.setWinding(slice.camLines, cutdir, false);
|
||||||
slice.output()
|
slice.output()
|
||||||
.setLayer("trace clear", {line: color}, false)
|
.setLayer("trace", {line: color}, false)
|
||||||
.addPolys(slice.camLines)
|
.addPolys(slice.camLines)
|
||||||
|
if (debug && shadow) slice.output()
|
||||||
|
.setLayer("trace shadow", {line: 0xff8811}, false)
|
||||||
|
.addPolys(shadow)
|
||||||
|
progress(zpro, "trace");
|
||||||
|
zpro += zinc;
|
||||||
|
addSlices(slice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1176,7 +1205,7 @@ class OpTrace extends CamOp {
|
||||||
const zbo = widget.track.top - widget.track.box.d;
|
const zbo = widget.track.top - widget.track.box.d;
|
||||||
let zmap = {};
|
let zmap = {};
|
||||||
for (let poly of polys) {
|
for (let poly of polys) {
|
||||||
let z = minZ(poly.getZ());
|
let z = minZ(poly.minZ());
|
||||||
if (offover) {
|
if (offover) {
|
||||||
let pnew = POLY.offset([poly], -offover, { minArea: 0, open: true });
|
let pnew = POLY.offset([poly], -offover, { minArea: 0, open: true });
|
||||||
if (pnew) {
|
if (pnew) {
|
||||||
|
|
@ -1190,7 +1219,7 @@ class OpTrace extends CamOp {
|
||||||
(zmap[z] = zmap[z] || []).appendAll(poly);
|
(zmap[z] = zmap[z] || []).appendAll(poly);
|
||||||
}
|
}
|
||||||
for (let [zv, polys] of Object.entries(zmap)) {
|
for (let [zv, polys] of Object.entries(zmap)) {
|
||||||
clearZ(polys, parseFloat(zv), down);
|
clearZnew(polys, parseFloat(zv), down);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
// dep: geo.line
|
||||||
// dep: geo.point
|
// dep: geo.point
|
||||||
// dep: geo.polygons
|
// dep: geo.polygons
|
||||||
// dep: kiri.slice
|
// dep: kiri.slice
|
||||||
|
|
@ -14,7 +15,7 @@ gapp.register("kiri-mode.cam.slice", (root, exports) => {
|
||||||
const { base, kiri } = root;
|
const { base, kiri } = root;
|
||||||
const { util } = base;
|
const { util } = base;
|
||||||
const { driver, newSlice, setSliceTracker } = kiri;
|
const { driver, newSlice, setSliceTracker } = kiri;
|
||||||
const { polygons, newPoint, newPolygon } = base;
|
const { polygons, newLine, newPoint, newPolygon } = base;
|
||||||
const { CAM } = driver;
|
const { CAM } = driver;
|
||||||
const { OPS } = CAM;
|
const { OPS } = CAM;
|
||||||
|
|
||||||
|
|
@ -379,60 +380,107 @@ CAM.addDogbones = function(poly, dist, reverse) {
|
||||||
};
|
};
|
||||||
|
|
||||||
CAM.traces = async function(settings, widget, single) {
|
CAM.traces = async function(settings, widget, single) {
|
||||||
if (widget.traces && widget.trace_single === single) {
|
if (widget.traces) {
|
||||||
// do no work if cached
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let slicer = new kiri.cam_slicer(widget);
|
|
||||||
let zees = Object.assign({}, slicer.zFlat, slicer.zLine);
|
|
||||||
let indices = [...new Set(Object.keys(zees)
|
|
||||||
.map(kv => parseFloat(kv).round(5))
|
|
||||||
.appendAll(Object.entries(single ? slicer.zLine : {}).map(ze => {
|
|
||||||
let [ zk, zv ] = ze;
|
|
||||||
return zv > 1 ? parseFloat(zk).round(5) : null;
|
|
||||||
})
|
|
||||||
.filter(v => v !== null))
|
|
||||||
)]
|
|
||||||
.sort((a,b) => b - a);
|
|
||||||
let traces = [];
|
|
||||||
// find and trim polys (including open) to shadow
|
|
||||||
let oneach = data => {
|
|
||||||
if (single) {
|
|
||||||
for (let line of data.lines) {
|
|
||||||
if (line.p1.distTo2D(line.p2) > 1) {
|
|
||||||
traces.push(newPolygon().append(line.p1).append(line.p2).setOpen());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
base.polygons.flatten(data.tops).forEach(poly => {
|
|
||||||
poly.inner = null;
|
|
||||||
poly.parent = null;
|
|
||||||
let z = poly.getZ();
|
|
||||||
for (let i=0, il=traces.length; i<il; i++) {
|
|
||||||
let trace = traces[i];
|
|
||||||
let dz = Math.abs(z - trace.getZ());
|
|
||||||
// only compare polys farther apart in Z
|
|
||||||
if (dz > 0.01) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// do not add duplicates
|
|
||||||
if (traces[i].isEquivalent(poly) && dz < 0.1) {
|
|
||||||
// console.log({ dup: poly });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
traces.push(poly);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// --- points → line segments ---
|
||||||
|
let edges = new THREE.EdgesGeometry(widget.mesh.geometry, 20);
|
||||||
|
let array = edges.attributes.position.array;
|
||||||
|
let pcache = {};
|
||||||
|
let points = new Array(2);
|
||||||
|
let lines = new Array(points.length / 2);
|
||||||
|
for (let i=0, j=0, k=0, l=array.length; i<l; ) {
|
||||||
|
let ps = [ array[i++], array[i++], array[i++] ];
|
||||||
|
let key = ps.map(v => (v * 10000) | 0).join(',');
|
||||||
|
let point = pcache[key];
|
||||||
|
if (!point) {
|
||||||
|
point = newPoint(ps[0], ps[1], ps[2], key);
|
||||||
|
point.lines = [];
|
||||||
|
pcache[key] = point;
|
||||||
|
}
|
||||||
|
points[j++ % 2] = point;
|
||||||
|
if (j % 2 === 0) {
|
||||||
|
let [ p0, p1 ] = points;
|
||||||
|
let line = lines[k++] = newLine(p0, p1);
|
||||||
|
p0.lines.push(line);
|
||||||
|
p1.lines.push(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- segments → chains (bidirectional walk) ---
|
||||||
|
let chains = [];
|
||||||
|
{
|
||||||
|
const segs = lines.slice(); // shallow copy
|
||||||
|
segs.forEach(s => (s.visited = false));
|
||||||
|
|
||||||
|
function step(dirPoint, prevSeg, pushFront, chain) {
|
||||||
|
let curr = dirPoint;
|
||||||
|
let prev = prevSeg;
|
||||||
|
|
||||||
|
while (curr.lines.length === 2) { // stay inside the arc
|
||||||
|
const next = curr.lines.find(l => !l.visited && l !== prev);
|
||||||
|
if (!next) break; // should not happen
|
||||||
|
|
||||||
|
next.visited = true;
|
||||||
|
if (pushFront) chain.unshift(next); // prepend or append?
|
||||||
|
else chain.push(next);
|
||||||
|
|
||||||
|
curr = next.p1 === curr ? next.p2 : next.p1;
|
||||||
|
prev = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const seed of segs) {
|
||||||
|
if (seed.visited) continue;
|
||||||
|
|
||||||
|
seed.visited = true; // always include the seed
|
||||||
|
const chain = [seed];
|
||||||
|
|
||||||
|
// grow backwards from p1 (prepend)
|
||||||
|
step(seed.p1, seed, true, chain);
|
||||||
|
|
||||||
|
// grow forwards from p2 (append)
|
||||||
|
step(seed.p2, seed, false, chain);
|
||||||
|
|
||||||
|
chains.push(chain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- chains → polylines ---
|
||||||
|
const polylines = [];
|
||||||
|
|
||||||
|
for (const chain of chains) {
|
||||||
|
const poly = newPolygon().setOpen();
|
||||||
|
|
||||||
|
// choose any node whose degree !== 2; if none, it’s a closed loop
|
||||||
|
let start = null;
|
||||||
|
for (const s of chain) {
|
||||||
|
if (s.p1.lines.length !== 2) { start = s.p1; break; }
|
||||||
|
if (s.p2.lines.length !== 2) { start = s.p2; break; }
|
||||||
|
}
|
||||||
|
if (!start) start = chain[0].p1; // closed loop
|
||||||
|
|
||||||
|
let curr = start, prevSeg = null;
|
||||||
|
poly.push(curr); // first point
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const nextSeg = curr.lines.find(
|
||||||
|
l => l !== prevSeg && chain.includes(l)
|
||||||
|
);
|
||||||
|
if (!nextSeg) break; // open end reached
|
||||||
|
|
||||||
|
curr = nextSeg.p1 === curr ? nextSeg.p2 : nextSeg.p1;
|
||||||
|
poly.push(curr); // << push *before* test
|
||||||
|
if (curr === start) break; // loop closed
|
||||||
|
|
||||||
|
prevSeg = nextSeg;
|
||||||
|
}
|
||||||
|
polylines.push(poly);
|
||||||
|
}
|
||||||
|
|
||||||
|
widget.traces = polylines;
|
||||||
|
|
||||||
let opts = { each: oneach, over: false, flatoff: 0, edges: true, openok: true, lines: true };
|
|
||||||
await slicer.slice(indices, opts);
|
|
||||||
// pick up bottom features
|
|
||||||
opts.over = true;
|
|
||||||
await slicer.slice(indices, opts);
|
|
||||||
widget.traces = traces;
|
|
||||||
widget.trace_single = single;
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -355,6 +355,27 @@ class Widget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getVisualState() {
|
||||||
|
return {
|
||||||
|
edges: this.outline ? true : false,
|
||||||
|
wires: this.wire ? true : false,
|
||||||
|
opacity: this.getMaterial().opacity
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
setVisualState({ edges, wires, opacity}) {
|
||||||
|
this.cache.vizstate = this.getVisualState();
|
||||||
|
this.setEdges(edges ?? false);
|
||||||
|
this.setWireframe(wires ?? false);
|
||||||
|
this.setOpacity(opacity ?? 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreVisualState() {
|
||||||
|
if (this.cache.vizstate) {
|
||||||
|
this.setVisualState(this.cache.vizstate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} value
|
* @param {number} value
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
let terms = {
|
let terms = {
|
||||||
COPYRIGHT: "Copyright (C) Stewart Allen <sa@grid.space> - All Rights Reserved",
|
COPYRIGHT: "Copyright (C) Stewart Allen <sa@grid.space> - All Rights Reserved",
|
||||||
LICENSE: "See the license.md file included with the source distribution",
|
LICENSE: "See the license.md file included with the source distribution",
|
||||||
VERSION: (is_self ? self : this).debug_version || "4.1.9"
|
VERSION: (is_self ? self : this).debug_version || "4.2.0"
|
||||||
};
|
};
|
||||||
|
|
||||||
if (typeof(module) === 'object') {
|
if (typeof(module) === 'object') {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "Kiri:Moto 4.1.9",
|
"name": "Kiri:Moto 4.2.0",
|
||||||
"short_name": "Kiri:Moto",
|
"short_name": "Kiri:Moto",
|
||||||
"description": "Slicer for 3D printers, CNC mills, laser cutters and more",
|
"description": "Slicer for 3D printers, CNC mills, laser cutters and more",
|
||||||
"start_url": "/kiri/",
|
"start_url": "/kiri/",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue