diff --git a/src/kiri/app/conf/defaults.js b/src/kiri/app/conf/defaults.js index a829966e..6ce92a9e 100644 --- a/src/kiri/app/conf/defaults.js +++ b/src/kiri/app/conf/defaults.js @@ -518,6 +518,7 @@ export const conf = { camLatheAngle: 1, camLatheLinear: true, camLatheOver: 0.1, + camLatheDown: 0, camLatheSpeed: 500, camLatheSpindle: 1000, camLatheTool: 1000, diff --git a/src/kiri/app/conf/manager.js b/src/kiri/app/conf/manager.js index a5cbe5cc..81fb7339 100644 --- a/src/kiri/app/conf/manager.js +++ b/src/kiri/app/conf/manager.js @@ -7,6 +7,7 @@ import { codec } from '../../core/codec.js'; import { consts } from '../../core/consts.js'; import { space } from '../../../moto/space.js'; import { local } from '../../../data/local.js'; +import { newWidget } from '../widget.js'; import { utils } from '../../core/utils.js'; import { version } from '../../../moto/license.js'; import { JSZip } from '../../../ext/jszip-esm.js'; @@ -608,6 +609,7 @@ function settingsImport(data, ask) { work.type = 100; } } + codec.setFactory(codec.TYPE.WIDGET, newWidget); codec.decode(data.work).forEach(widget => { api.platform.add(widget, 0, true, true); }); diff --git a/src/kiri/core/codec.js b/src/kiri/core/codec.js index 7f6de9ea..ee102444 100644 --- a/src/kiri/core/codec.js +++ b/src/kiri/core/codec.js @@ -7,6 +7,7 @@ import { Slice, newSlice, Top, newTop } from './slice.js'; import { Widget, newWidget } from './widget.js'; const decoders = {}; +const factories = {}; const freeMem = true; const TYPE = { @@ -213,6 +214,11 @@ function decodePointArray2D(array, z, fn) { return points; } +function setFactory(type, factory) { + factories[type] = factory; + return codec; +} + // ----- Widget Codec ----- Widget.prototype.encode = function(state) { @@ -236,7 +242,8 @@ registerDecoder(TYPE.WIDGET, function(v, state) { const id = v.id, group = v.group || id, track = v.track || undefined, - widget = newWidget(id, Widget.Groups.forid(group)), + factory = factories[TYPE.WIDGET] ?? newWidget, + widget = factory(id, Widget.Groups.forid(group)), meta= v.meta || widget.meta, anno = v.anno || widget.anno; @@ -480,6 +487,7 @@ registerDecoder(TYPE.LAYERS, function(v, state) { }); export const codec = { + TYPE, encode, decode, registerDecoder, @@ -488,10 +496,12 @@ export const codec = { decodePointArray, encodePointArray2D, decodePointArray2D, + setFactory, toCodable }; export { + TYPE, encode, decode, registerDecoder, @@ -500,6 +510,7 @@ export { decodePointArray, encodePointArray2D, decodePointArray2D, + setFactory, toCodable }; diff --git a/src/kiri/core/widget.js b/src/kiri/core/widget.js index 20874102..78591324 100644 --- a/src/kiri/core/widget.js +++ b/src/kiri/core/widget.js @@ -181,9 +181,13 @@ class Widget { return this.loadVertices(...arguments); } - setModified() { + setModified(reason) { this.modified = true; this.boundingBoxNeedsUpdate = true; + this.clearShadows(); + if (reason !== 'axis') { + this.cache.geo = undefined; + } if (this.mesh && this.mesh.geometry) { // this fixes ray intersections after the mesh is modified this.mesh.geometry.boundingSphere = null; @@ -355,7 +359,7 @@ class Widget { // invalidate cached points if (x || y || z) { this.points = null; - this.setModified(); + this.setModified('moveMesh'); } } @@ -372,11 +376,11 @@ class Widget { } setAxisIndex(deg) { + // console.trace(deg); let rad = deg * (Math.PI / 180); if (rad !== this.track.indexRad) { this.track.indexRad = rad; - this.clearShadows(); - this.setModified(); + this.setModified('axis'); this._updateMeshPosition(); } } @@ -429,7 +433,7 @@ class Widget { pos.z += (z || 0); } if (x || y || z) { - this.setModified(); + this.setModified('_move'); this._updateMeshPosition(); } } @@ -475,7 +479,7 @@ class Widget { scale.x *= (x || 1.0); scale.y *= (y || 1.0); scale.z *= (z || 1.0); - this.setModified(); + this.setModified('_scale'); } rotate(x, y, z, temp, center = true) { @@ -514,7 +518,7 @@ class Widget { rot.y += (y || 0); rot.z += (z || 0); } - this.setModified(); + this.setModified('_rotate'); } // undo all accumulated rotations @@ -524,7 +528,7 @@ class Widget { }); this.roto = []; this.center(); - this.setModified(); + this.setModified('unrotate'); if (this.refreshVisualState) this.refreshVisualState(); } @@ -563,7 +567,7 @@ class Widget { } pos.needsUpdate = true; ot.mirror = !ot.mirror; - this.setModified(); + this.setModified('_mirror'); this.points = null; } @@ -586,6 +590,17 @@ class Widget { getGeoVertices(opt = {}) { const { unroll, translate } = opt; + let cacheKey = [ + unroll ? 1 : 0, + translate ? 1 : 0, + ((this.track.indexRad ?? 0) * 100000) | 0 + ].join(','); + let marked = Date.now(); + let geoCache = this.cache.geo = (this.cache.geo || {}); + let cached = geoCache[cacheKey]; + if (cached) { + return cached.pos; + } let geo = this.mesh.geometry; let pos = geo.getAttribute('position'); // if indexed, return points rotated about X and then offset @@ -595,6 +610,7 @@ class Widget { .applyMatrix4( new THREE.Matrix4().makeRotationX(-track.indexRad) ); } pos = pos.array; + // unroll indexed geometry if (geo.index && unroll !== false) { let idx = geo.index.array; let len = idx.length; @@ -608,12 +624,8 @@ class Widget { pp2[inc++] = pos[ip]; } pos = pp2; - } else if (translate) { - pos = pos.slice(); } - // used by CAM.shadowAt() - this.cache.geo = pos; - delete this.cache.shadow; + geoCache[cacheKey] = { pos, marked }; return pos; } @@ -631,7 +643,7 @@ class Widget { if (!this.bounds || refresh || this.boundingBoxNeedsUpdate) { this.bounds = new THREE.Box3().setFromArray(this.getGeoVertices({ translate: true, - unroll: false + unroll: true })); this.boundingBoxNeedsUpdate = false; } @@ -701,6 +713,7 @@ class Widget { // find closest shadow above and use to speed up delta shadow gen let zover = Object.keys(shadows).map(v => parseFloat(v)).filter(v => v > z); let minZabove = Math.min(Infinity, ...zover); + // shift shadow probeline down a fraction to capture z flats with FP noise let shadow = this.#computeShadowAt(z - 0.005, minZabove); if (minZabove < Infinity) { shadow = POLY.union([...shadow, ...shadows[minZabove]], 0, true, { wasm: false }); @@ -725,7 +738,7 @@ class Widget { if (this.cache.shadow) { return; } - const geo = this.cache.geo; + const geo = this.getGeoVertices({ unroll: true, translate: true }); const length = geo.length; const bounds = this.getBoundingBox(); const stack = {}; diff --git a/src/kiri/mode/cam/cl-ops.js b/src/kiri/mode/cam/cl-ops.js index fc03e33f..fb1ae0a6 100644 --- a/src/kiri/mode/cam/cl-ops.js +++ b/src/kiri/mode/cam/cl-ops.js @@ -350,8 +350,9 @@ export function createPopOps() { createPopOp('lathe', { tool: 'camLatheTool', spindle: 'camLatheSpindle', - step: 'camLatheOver', angle: 'camLatheAngle', + step: 'camLatheOver', + down: 'camLatheDown', rate: 'camLatheSpeed', tolerance: 'camTolerance', filter: 'camContourFilter', @@ -365,8 +366,9 @@ export function createPopOps() { spindle: UC.newInput(LANG.cc_spnd_s, { title: LANG.cc_spnd_l, convert: toInt, show: hasSpindle }), rate: UC.newInput(LANG.cc_feed_s, { title: LANG.cc_feed_l, convert: toInt, units }), sep: UC.newBlank({ class: "pop-sep" }), - step: UC.newInput(LANG.cc_sovr_s, { title: LANG.cc_sovr_l, convert: toFloat, bound: UC.bound(0.01, 100.0) }), angle: UC.newInput(LANG.cc_sang_s, { title: LANG.cc_sang_l, convert: toFloat, bound: UC.bound(0.01, 180.0) }), + step: UC.newInput(LANG.cc_sovr_s, { title: LANG.cc_sovr_l, convert: toFloat, bound: UC.bound(0.01, 100.0) }), + // down: UC.newInput(LANG.cc_sdwn_s, { title: LANG.cc_sdwn_l, convert: toFloat, units }), sep: UC.newBlank({ class: "pop-sep" }), offStart: UC.newInput(LANG.ci_laso_s, { title: LANG.ci_laso_l, convert: toFloat}), offEnd: UC.newInput(LANG.ci_laeo_s, { title: LANG.ci_laeo_l, convert: toFloat}), diff --git a/src/kiri/mode/cam/driver-be.js b/src/kiri/mode/cam/driver-be.js index d4d6b39c..2212908e 100644 --- a/src/kiri/mode/cam/driver-be.js +++ b/src/kiri/mode/cam/driver-be.js @@ -18,9 +18,9 @@ function surface_prep(widget, index) { } }; -function surface_find(widget, faces, radians) { +function surface_find(widget, faces, radians, filterZ) { surface_prep(widget); - return widget.tool.findConnectedSurface(faces, radians || 0, 0.0); + return widget.tool.findConnectedSurface(faces, radians, filterZ); }; function init(worker) { diff --git a/src/kiri/mode/cam/init-menu.js b/src/kiri/mode/cam/init-menu.js index 5b660b2e..d061b1c0 100644 --- a/src/kiri/mode/cam/init-menu.js +++ b/src/kiri/mode/cam/init-menu.js @@ -114,6 +114,10 @@ export function menu() { camStockOffset: newBoolean(LANG.cs_offs_s, onBooleanClick, {title:LANG.cs_offs_l}), camStockIndexed: newBoolean(LANG.cs_indx_s, onBooleanClick, {title:LANG.cs_indx_l}), camStockIndexGrid: newBoolean(LANG.cs_ishg_s, onBooleanClick, {title:LANG.cs_ishg_l, show:() => ui.camStockIndexed.checked}), + // separator: newBlank({ class:"set-sep", driven }), + // camStockManual: newRow([ + // (ui.stockPlace = newButton('position', onButtonClick, { })), + // ], {class:"ext-buttons f-row"}), _____: newGroup(LANG.cc_menu, $('cam-limits'), { modes:CAM, driven, separator }), camZAnchor: newSelect(LANG.ou_zanc_s, {title: LANG.ou_zanc_l, action:zAnchorSave, show:() => !ui.camStockIndexed.checked}, "zanchor"), camZOffset: newInput(LANG.ou_ztof_s, {title:LANG.ou_ztof_l, convert:toFloat, units}), diff --git a/src/kiri/mode/cam/init-ui.js b/src/kiri/mode/cam/init-ui.js index 706cb456..81a2ed94 100644 --- a/src/kiri/mode/cam/init-ui.js +++ b/src/kiri/mode/cam/init-ui.js @@ -792,8 +792,24 @@ export function init() { // TAB/TRACE BUTTON HANDLERS api.event.on("button.click", target => { - let process = api.conf.get().process; + let { process, stock } = api.conf.get(); switch (target) { + case api.ui.stockPlace: + api.uc.prompt("stock x,y,z position", "").then(csv => { + if (csv) { + let [x,y,z] = csv.split(',').map(v => parseInt(v) || 0); + let center = { + x: x ?? 0, + y: y ?? 0, + z: z ?? 0 + } + // move widgets based on postion + // more complex than just moving stock + // because stock is based on contents, not the other way + console.log({ x,y,z }); + } + }); + return; case api.ui.tabAdd: return tabAdd(); case api.ui.tabDun: diff --git a/src/kiri/mode/cam/slice.js b/src/kiri/mode/cam/slice.js index 70597045..4c260399 100644 --- a/src/kiri/mode/cam/slice.js +++ b/src/kiri/mode/cam/slice.js @@ -241,7 +241,6 @@ export async function cam_slice(settings, widget, onupdate, ondone) { } async function computeShadows() { - // console.log('(re)compute shadows'); await new OPS.shadow(state, { type: "shadow", silent: true }).slice(progress => { // console.log('reshadowing', progress.round(3)); });