2025-07-04 22:18:48 -04:00
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
import { $ , $c } from '../moto/webui.js' ;
import { api } from '../kiri/api.js' ;
import { base } from '../geo/base.js' ;
import { local as sdb } from '../data/local.js' ;
import { space } from '../moto/space.js' ;
import { LOCAL , SETUP } from './main.js' ;
import { VIEWS , MODES , SEED } from '../kiri/consts.js' ;
import { beta , version } from '../moto/license.js' ;
import { settings as set _ctrl } from './settings.js' ;
2025-07-05 17:54:01 -04:00
import { init as initCAM } from '../kiri-mode/cam/client.js' ;
import { init as initFDM } from '../kiri-mode/fdm/client.js' ;
import { init as initLaser } from '../kiri-mode/laser/client.js' ;
2025-07-05 18:52:53 -04:00
import { init as initSLA } from '../kiri-mode/sla/client.js' ;
2025-07-04 22:18:48 -04:00
let { CAM , SLA , FDM , LASER , DRAG , WJET , WEDM } = MODES ,
2025-07-05 12:41:00 -04:00
{ consts , client , catalog , platform , selection } = api ,
2025-07-04 22:18:48 -04:00
LANG = api . language . current ,
WIN = self . window ,
DOC = self . document ,
STARTMODE = SETUP . sm && SETUP . sm . length === 1 ? SETUP . sm [ 0 ] : null ,
TWOD = [ LASER , DRAG , WJET , WEDM ] ,
TWONED = [ LASER , DRAG , WJET ] ,
THREED = [ FDM , CAM , SLA ] ,
GCODE = [ FDM , CAM , ... TWOD ] ,
CAM _LZR = [ CAM , ... TWOD ] ,
FDM _LZN = [ FDM , ... TWONED ] ,
NO _WEDM = [ FDM , CAM , SLA , LASER , DRAG , WJET ] ,
FDM _CAM = [ FDM , CAM ] ,
proto = location . protocol ,
platformColor ,
separator = true ,
hideable = true ,
inline = true ,
driven = true ,
trigger = true ,
units = true ,
ui = api . ui ,
uc = api . uc ;
function settings ( ) {
return api . conf . get ( ) ;
}
function updateTool ( ev ) {
api . tool . update ( ev ) ;
}
function checkSeed ( then ) {
// skip sample object load in onshape (or any script postload)
if ( ! sdb [ SEED ] ) {
sdb [ SEED ] = new Date ( ) . getTime ( ) ;
if ( ! SETUP . s && api . feature . seed ) {
if ( SETUP . debug ) {
return then ( ) ;
}
platform . load _stl ( "/obj/cube.stl" , function ( vert ) {
catalog . putFile ( "sample cube.stl" , vert ) ;
platform . update _bounds ( ) ;
space . view . home ( ) ;
setTimeout ( ( ) => { api . space . save ( true ) } , 500 ) ;
then ( ) ;
api . help . show ( ) ;
} ) ;
return true ;
}
}
return false ;
}
function unitsSave ( ) {
api . conf . update ( { controller : true } ) ;
platform . update _size ( ) ;
}
function aniMeshSave ( ) {
api . conf . update ( { controller : true } ) ;
api . conf . save ( ) ;
}
function lineTypeSave ( ) {
const sel = ui . lineType . options [ ui . lineType . selectedIndex ] ;
if ( sel ) {
settings ( ) . controller . lineType = sel . value ;
api . conf . save ( ) ;
}
}
function thinWallSave ( ) {
let opt = ui . sliceDetectThin ;
let level = opt . options [ opt . selectedIndex ] ;
if ( level ) {
settings ( ) . process . sliceDetectThin = level . value ;
api . conf . save ( ) ;
}
}
function detailSave ( ) {
let level = ui . detail . options [ ui . detail . selectedIndex ] ;
if ( level ) {
level = level . value ;
let rez = base . config . clipperClean ;
switch ( level ) {
case '100' : rez = 50 ; break ;
case '75' : rez = base . config . clipperClean ; break ;
case '50' : rez = 500 ; break ;
case '25' : rez = 1000 ; break ;
}
client . config ( {
base : { clipperClean : rez }
} ) ;
settings ( ) . controller . detail = level ;
api . conf . save ( ) ;
}
}
function speedSave ( ) {
settings ( ) . controller . showSpeeds = ui . showSpeeds . checked ;
api . view . update _speeds ( ) ;
}
function zAnchorSave ( ) {
api . conf . update ( ) ;
api . platform . update _top _z ( ) ;
}
function setThreaded ( bool ) {
if ( bool ) {
client . pool . start ( ) ;
} else {
client . pool . stop ( ) ;
}
return bool ;
}
// upon restore, seed presets
api . event . emit ( 'preset' , api . conf . dbo ( ) ) ;
api . event . on ( "set.threaded" , bool => setThreaded ( bool ) ) ;
function booleanSave ( ) {
let control = settings ( ) . controller ;
let doAlert = ui . ortho . checked !== control . ortho ;
if ( control . assembly != ui . assembly . checked ) {
client . wasm ( ui . assembly . checked ) ;
}
if ( control . antiAlias != ui . antiAlias . checked ) {
api . show . alert ( 'Page Reload Required to Change Aliasing' ) ;
}
control . shiny = ui . shiny . checked ;
control . drawer = ui . drawer . checked ;
control . scrolls = ui . scrolls . checked ;
control . showOrigin = ui . showOrigin . checked ;
control . showRulers = ui . showRulers . checked ;
control . autoLayout = ui . autoLayout . checked ;
control . freeLayout = ui . freeLayout . checked ;
control . spaceRandoX = ui . spaceRandoX . checked ;
control . autoSave = ui . autoSave . checked ;
control . antiAlias = ui . antiAlias . checked ;
control . reverseZoom = ui . reverseZoom . checked ;
control . dark = ui . dark . checked ;
control . exportOcto = ui . exportOcto . checked ;
control . exportGhost = ui . exportGhost . checked ;
control . exportLocal = ui . exportLocal . checked ;
control . exportThumb = ui . exportThumb . checked ;
control . exportPreview = ui . exportPreview . checked ;
control . healMesh = ui . healMesh . checked ;
control . threaded = setThreaded ( ui . threaded . checked ) ;
control . assembly = ui . assembly . checked ;
control . ortho = ui . ortho . checked ;
control . devel = ui . devel . checked ;
space . view . setZoom ( control . reverseZoom , control . zoomSpeed ) ;
// platform.layout();
api . conf . save ( ) ;
api . platform . update _size ( ) ;
updateStats ( ) ;
updateDrawer ( ) ;
api . event . emit ( 'boolean.update' ) ;
if ( doAlert ) {
api . show . alert ( "change requires page refresh" ) ;
}
}
function updateDrawer ( ) {
const { drawer , scrolls } = settings ( ) . controller ;
$c ( 'app' , drawer ? 'slideshow' : '' , drawer ? '' : 'slideshow' ) ;
$c ( 'app' , scrolls ? '' : 'hide-scroll' , scrolls ? 'hide-scroll' : '' ) ;
}
function updateStats ( ) {
if ( self . debug !== true ) {
return ;
}
let { div , fps , rms , rnfo } = ui . stats ;
div . style . display = 'flex' ;
setInterval ( ( ) => {
const nrms = space . view . getRMS ( ) . toFixed ( 1 ) ;
const nfps = space . view . getFPS ( ) . toFixed ( 1 ) ;
const rend = space . renderInfo ( ) ;
const { memory , render } = rend ;
if ( nfps !== fps . innerText ) {
fps . innerText = nfps ;
}
if ( nrms !== rms . innerText ) {
rms . innerText = nrms ;
}
if ( rnfo . offsetParent !== null ) {
rnfo . innerHTML = Object . entries ( { ... memory , ... render , render _ms : nrms , frames _sec : nfps } ) . map ( row => {
return ` <div> ${ row [ 0 ] } </div><label> ${ base . util . comma ( row [ 1 ] ) } </label> `
} ) . join ( '' ) ;
}
} , 100 ) ;
}
function onBooleanClick ( el ) {
// copy some ui elements to target settings
let settings = api . conf . get ( ) ;
settings . device . bedBelt = ui . deviceBelt . checked && api . mode . is _fdm ( ) ;
settings . device . bedRound = ui . deviceRound . checked && api . mode . is _fdm ( ) ;
settings . device . originCenter = ui . deviceOrigin . checked || ui . deviceRound . checked ;
settings . device . fwRetract = ui . fwRetract . checked ;
// refresh vars and other ui elements
uc . refresh ( ) ;
if ( el === ui . camStockIndexed ) {
api . view . set _arrange ( ) ;
}
api . conf . update ( ) ;
DOC . activeElement . blur ( ) ;
api . event . emit ( "boolean.click" ) ;
api . devices . update _laser _state ( ) ;
}
function onButtonClick ( ev ) {
let target = ev . target ;
while ( target && target . tagName !== 'BUTTON' ) {
target = target . parentNode ;
}
api . event . emit ( "button.click" , target ) ;
}
function inputHasFocus ( ) {
let active = DOC . activeElement ;
return active && ( active . nodeName === "INPUT" || active . nodeName === "TEXTAREA" ) ;
}
function cca ( c ) {
return c . charCodeAt ( 0 ) ;
}
function keyUpHandler ( evt ) {
if ( api . feature . on _key ) {
if ( api . feature . on _key ( { up : evt } ) ) return ;
}
for ( let handler of api . feature . on _key2 ) {
if ( handler ( { up : evt } ) ) return ;
}
switch ( evt . keyCode ) {
// escape
case 27 :
// blur text input focus
DOC . activeElement . blur ( ) ;
// dismiss modals
api . modal . hide ( ) ;
// deselect widgets
platform . deselect ( ) ;
// hide all dialogs
api . dialog . hide ( ) ;
// cancel slicing
api . function . cancel ( ) ;
// and send an event (used by FDM client)
api . event . emit ( "key.esc" ) ;
break ;
}
return false ;
}
function keyDownHandler ( evt ) {
if ( api . modal . visible ( ) ) {
return false ;
}
if ( api . feature . on _key ) {
if ( api . feature . on _key ( { down : evt } ) ) return ;
}
for ( let handler of api . feature . on _key2 ) {
if ( handler ( { down : evt } ) ) return ;
}
let move = evt . altKey ? 5 : 0 ,
deg = move ? 0 : - Math . PI / ( evt . shiftKey ? 36 : 2 ) ;
switch ( evt . keyCode ) {
case 8 : // apple: delete/backspace
case 46 : // others: delete
if ( inputHasFocus ( ) ) return false ;
platform . delete ( selection . meshes ( ) ) ;
evt . preventDefault ( ) ;
break ;
case 37 : // left arrow
if ( inputHasFocus ( ) ) return false ;
if ( deg ) selection . rotate ( 0 , 0 , - deg ) ;
if ( move > 0 ) selection . move ( - move , 0 , 0 ) ;
evt . preventDefault ( ) ;
break ;
case 39 : // right arrow
if ( inputHasFocus ( ) ) return false ;
if ( deg ) selection . rotate ( 0 , 0 , deg ) ;
if ( move > 0 ) selection . move ( move , 0 , 0 ) ;
evt . preventDefault ( ) ;
break ;
case 38 : // up arrow
if ( inputHasFocus ( ) ) return false ;
if ( evt . metaKey ) return api . show . layer ( api . var . layer _at + 1 ) ;
if ( deg ) selection . rotate ( deg , 0 , 0 ) ;
if ( move > 0 ) selection . move ( 0 , move , 0 ) ;
evt . preventDefault ( ) ;
break ;
case 40 : // down arrow
if ( inputHasFocus ( ) ) return false ;
if ( evt . metaKey ) return api . show . layer ( api . var . layer _at - 1 ) ;
if ( deg ) selection . rotate ( - deg , 0 , 0 ) ;
if ( move > 0 ) selection . move ( 0 , - move , 0 ) ;
evt . preventDefault ( ) ;
break ;
case 65 : // 'a' for select all
if ( evt . metaKey || evt . ctrlKey ) {
if ( inputHasFocus ( ) ) return false ;
evt . preventDefault ( ) ;
platform . deselect ( ) ;
platform . select _all ( ) ;
}
break ;
case 83 : // 's' for save workspace
if ( evt . ctrlKey ) {
evt . preventDefault ( ) ;
api . conf . save ( ) ;
console . log ( "settings saved" ) ;
} else
if ( evt . metaKey ) {
evt . preventDefault ( ) ;
api . space . save ( ) ;
set _ctrl . sync . put ( ) ;
}
break ;
case 76 : // 'l' for restore workspace
if ( evt . metaKey ) {
evt . preventDefault ( ) ;
api . space . restore ( ) ;
}
break ;
}
}
function keyHandler ( evt ) {
let handled = true ;
if ( api . modal . visible ( ) || inputHasFocus ( ) ) {
return false ;
}
if ( api . feature . on _key ) {
if ( api . feature . on _key ( { key : evt } ) ) return ;
}
for ( let handler of api . feature . on _key2 ) {
if ( handler ( { key : evt } ) ) return ;
}
if ( evt . ctrlKey ) {
switch ( evt . key ) {
case 'g' : return api . group . merge ( ) ;
case 'u' : return api . group . split ( ) ;
}
}
switch ( evt . charCode ) {
case cca ( '`' ) : api . show . slices ( 0 ) ; break ;
case cca ( '0' ) : api . show . slices ( api . var . layer _max ) ; break ;
case cca ( '1' ) : api . show . slices ( api . var . layer _max / 10 ) ; break ;
case cca ( '2' ) : api . show . slices ( api . var . layer _max * 2 / 10 ) ; break ;
case cca ( '3' ) : api . show . slices ( api . var . layer _max * 3 / 10 ) ; break ;
case cca ( '4' ) : api . show . slices ( api . var . layer _max * 4 / 10 ) ; break ;
case cca ( '5' ) : api . show . slices ( api . var . layer _max * 5 / 10 ) ; break ;
case cca ( '6' ) : api . show . slices ( api . var . layer _max * 6 / 10 ) ; break ;
case cca ( '7' ) : api . show . slices ( api . var . layer _max * 7 / 10 ) ; break ;
case cca ( '8' ) : api . show . slices ( api . var . layer _max * 8 / 10 ) ; break ;
case cca ( '9' ) : api . show . slices ( api . var . layer _max * 9 / 10 ) ; break ;
case cca ( '?' ) :
api . help . show ( ) ;
break ;
case cca ( 'Z' ) : // reset stored state
uc . confirm ( 'clear all settings and preferences?' ) . then ( yes => {
if ( yes ) {
sdb . clear ( ) ;
WIN . location . reload ( ) ;
}
} ) ;
break ;
case cca ( 'C' ) : // refresh catalog
catalog . refresh ( ) ;
break ;
case cca ( 'i' ) : // file import
api . event . import ( ) ;
break ;
case cca ( 'S' ) : // slice
case cca ( 's' ) : // slice
api . function . slice ( ) ;
break ;
case cca ( 'P' ) : // prepare
case cca ( 'p' ) : // prepare
if ( api . mode . get ( ) !== 'SLA' ) {
// hidden in SLA mode
2025-07-05 18:52:53 -04:00
api . function . prepare ( ) ;
2025-07-04 22:18:48 -04:00
}
break ;
case cca ( 'X' ) : // export
case cca ( 'x' ) : // export
api . function . export ( ) ;
break ;
case cca ( 'g' ) : // CAM animate
api . function . animate ( ) ;
break ;
case cca ( 'O' ) : // manual rotation
rotateInputSelection ( ) ;
break ;
case cca ( 'r' ) : // recent files
api . modal . show ( 'files' ) ;
break ;
case cca ( 'q' ) : // preferences
api . modal . show ( 'prefs' ) ;
break ;
case cca ( 'l' ) : // device
settingsLoad ( ) ;
break ;
case cca ( 'e' ) : // device
api . show . devices ( ) ;
break ;
case cca ( 'o' ) : // tools
api . show . tools ( ) ;
break ;
case cca ( 'c' ) : // local devices
api . show . local ( ) ;
break ;
case cca ( 'v' ) : // toggle single slice view mode
if ( api . view . get ( ) === VIEWS . ARRANGE ) {
api . space . set _focus ( selection . widgets ( ) ) ;
}
if ( api . var . layer _hi == api . var . layer _lo ) {
api . var . layer _lo = 0 ;
} else {
api . var . layer _lo = api . var . layer _hi ;
}
api . show . slices ( ) ;
break ;
case cca ( 'd' ) : // duplicate object
duplicateSelection ( ) ;
break ;
case cca ( 'm' ) : // mirror object
mirrorSelection ( ) ;
break ;
case cca ( 'a' ) :
if ( api . view . get ( ) === VIEWS . ARRANGE ) {
// auto arrange items on platform
platform . layout ( ) ;
if ( ! api . conf . get ( ) . controller . spaceRandoX ) {
api . space . set _focus ( selection . widgets ( ) ) ;
}
} else {
// go to arrange view
api . view . set ( VIEWS . ARRANGE ) ;
}
break ;
default :
api . event . emit ( 'keypress' , evt ) ;
handled = false ;
break ;
}
if ( handled ) {
evt . preventDefault ( ) ;
evt . stopPropagation ( ) ;
}
return false ;
}
function duplicateSelection ( ) {
selection . duplicate ( ) ;
}
function mirrorSelection ( ) {
selection . mirror ( ) ;
}
function keys ( o ) {
let key , list = [ ] ;
for ( key in o ) { if ( o . hasOwnProperty ( key ) ) list . push ( key ) }
return list . sort ( ) ;
}
function clearSelected ( children ) {
for ( let i = 0 ; i < children . length ; i ++ ) {
children [ i ] . setAttribute ( 'class' , '' ) ;
}
}
function rotateInputSelection ( ) {
if ( selection . meshes ( ) . length === 0 ) {
api . show . alert ( "select object to rotate" ) ;
return ;
}
api . uc . prompt ( "Enter X,Y,Z degrees of rotation" , "" ) . then ( coord => {
coord = ( coord || '' ) . split ( ',' ) ;
let prod = Math . PI / 180 ,
x = parseFloat ( coord [ 0 ] || 0.0 ) * prod ,
y = parseFloat ( coord [ 1 ] || 0.0 ) * prod ,
z = parseFloat ( coord [ 2 ] || 0.0 ) * prod ;
selection . rotate ( x , y , z ) ;
} ) ;
}
function positionSelection ( ) {
if ( selection . meshes ( ) . length === 0 ) {
api . show . alert ( "select object to position" ) ;
return ;
}
let current = settings ( ) ,
{ device , process } = current ,
center = process . ctOriginCenter || process . camOriginCenter || device . bedRound || device . originCenter ,
bounds = boundsSelection ( ) ;
api . uc . prompt ( "Enter X,Y coordinates for selection" , "" ) . then ( coord => {
coord = ( coord || '' ) . split ( ',' ) ;
let x = parseFloat ( coord [ 0 ] || 0.0 ) ,
y = parseFloat ( coord [ 1 ] || 0.0 ) ,
z = parseFloat ( coord [ 2 ] || 0.0 ) ;
if ( ! center ) {
x = x - device . bedWidth / 2 + ( bounds . max . x - bounds . min . x ) / 2 ;
y = y - device . bedDepth / 2 + ( bounds . max . y - bounds . min . y ) / 2
}
selection . move ( x , y , z , true ) ;
} ) ;
}
function deviceExport ( exp , name ) {
name = ( name || "device" )
. toLowerCase ( )
. replace ( / /g , '_' )
. replace ( /\./g , '_' ) ;
uc . prompt ( "Export Device Filename" , name ) . then ( name => {
if ( name ) {
api . util . download ( exp , ` ${ name } .km ` ) ;
}
} ) ;
}
function objectsExport ( format = "stl" ) {
// return selection.export();
uc . confirm ( "Export Filename" , { ok : true , cancel : false } , ` selected. ${ format } ` ) . then ( name => {
if ( ! name ) return ;
if ( name . toLowerCase ( ) . indexOf ( ` . ${ format } ` ) < 0 ) {
name = ` ${ name } . ${ format } ` ;
}
api . util . download ( selection . export ( format ) , name ) ;
} ) ;
}
function workspaceNew ( ) {
uc . confirm ( "Clear Workspace?" , { ok : true , cancel : false } ) . then ( value => {
if ( value === true ) {
let proc = set _ctrl . proc ( ) ;
proc . ops && ( proc . ops . length = 0 ) ;
proc . op2 && ( proc . op2 . length = 0 ) ;
api . platform . clear ( ) ;
}
} ) ;
}
function profileExport ( ) {
const opt = { pre : [
"<div class='f-col a-center gap5 mlr10'>" ,
" <h3>Workspace Export</h3>" ,
" <label>This will create a backup of your</label>" ,
" <label>workspace, devices, and settings</label>" ,
" <span class='mt10'><input id='excwork' type='checkbox'> Exclude meshes</span>" ,
"</div>"
] } ;
let suggestion = "workspace" ;
let file = api . widgets . all ( ) [ 0 ] ? . meta . file || '' ;
if ( file ) {
suggestion = ` ${ suggestion } _ ${ file . split ( '.' ) [ 0 ] } ` . replaceAll ( ' ' , '_' ) ;
} ;
uc . confirm ( "Filename" , { ok : true , cancel : false } , suggestion , opt ) . then ( name => {
if ( ! name ) return ;
let work = ! $ ( 'excwork' ) . checked ;
let json = api . conf . export ( { work , clear : true } ) ;
client . zip ( [
{ name : "workspace.json" , data : JSON . stringify ( json ) }
] , progress => {
api . show . progress ( progress . percent / 100 , "compressing workspace" ) ;
} , output => {
api . show . progress ( 0 ) ;
api . util . download ( output , ` ${ name } .kmz ` ) ;
} ) ;
} ) ;
}
function settingsSave ( ev , name ) {
if ( ev ) {
ev . stopPropagation ( ) ;
ev . preventDefault ( ) ;
}
api . dialog . hide ( ) ;
let mode = api . mode . get ( ) ,
s = settings ( ) ,
def = "default" ,
cp = s . process ,
pl = s . sproc [ mode ] ,
lp = s . cproc [ mode ] ,
saveAs = ( name ) => {
if ( ! name ) {
return ;
}
let np = pl [ name ] = { } ;
cp . processName = name ;
pl [ name ] = Object . clone ( cp ) ;
for ( let k in cp ) {
if ( ! cp . hasOwnProperty ( k ) ) continue ;
np [ k ] = cp [ k ] ;
}
s . cproc [ mode ] = name ;
s . devproc [ s . device . deviceName ] = name ;
api . conf . save ( ) ;
api . conf . update ( ) ;
api . event . settings ( ) ;
sync _put ( ) ;
} ;
if ( name ) {
saveAs ( name ) ;
} else {
uc . prompt ( "Save Settings As" , cp ? lp || def : def ) . then ( saveAs ) ;
}
}
function settingsLoad ( ) {
api . conf . show ( ) ;
}
function updateDeviceSize ( ) {
api . conf . update ( ) ;
platform . update _size ( ) ;
platform . update _origin ( ) ;
}
async function sync _put ( ) {
await set _ctrl . sync . put ( ) ;
}
function dragOverHandler ( evt ) {
evt . stopPropagation ( ) ;
evt . preventDefault ( ) ;
// prevent drop actions when a dialog is open
if ( api . modal . visible ( ) ) {
return ;
}
evt . dataTransfer . dropEffect = 'copy' ;
let oldcolor = space . platform . setColor ( 0x00ff00 ) ;
if ( oldcolor !== 0x00ff00 ) platformColor = oldcolor ;
}
function dragLeave ( ) {
space . platform . setColor ( platformColor ) ;
}
function dropHandler ( evt ) {
evt . stopPropagation ( ) ;
evt . preventDefault ( ) ;
// prevent drop actions when a dialog is open
if ( api . modal . visible ( ) ) {
return ;
}
space . platform . setColor ( platformColor ) ;
let files = evt . dataTransfer . files ;
switch ( api . feature . drop _group ) {
case true :
return api . platform . load _files ( files , [ ] ) ;
case false :
return api . platform . load _files ( files , undefined ) ;
}
if ( files . length === 1 ) {
api . platform . load _files ( files ) ;
} else if ( files . length > 1 ) {
uc . confirm ( ` group ${ files . length } files? ` ) . then ( yes => {
api . platform . load _files ( files , yes ? [ ] : undefined ) ;
} ) ;
}
}
function loadCatalogFile ( e ) {
api . widgets . load ( e . target . getAttribute ( 'load' ) , function ( widget ) {
platform . add ( widget ) ;
api . dialog . hide ( ) ;
} ) ;
}
function updateCatalog ( files ) {
let table = ui . catalogList ,
list = [ ] ;
table . innerHTML = '' ;
for ( let name in files ) {
list . push ( { n : name , ln : name . toLowerCase ( ) , v : files [ name ] . vertices , t : files [ name ] . updated } ) ;
}
list . sort ( function ( a , b ) {
return a . ln < b . ln ? - 1 : 1 ;
} ) ;
for ( let i = 0 ; i < list . length ; i ++ ) {
let row = DOC . createElement ( 'div' ) ,
renm = DOC . createElement ( 'button' ) ,
load = DOC . createElement ( 'button' ) ,
size = DOC . createElement ( 'button' ) ,
del = DOC . createElement ( 'button' ) ,
file = list [ i ] ,
name = file . n ,
date = new Date ( file . t ) ,
split = name . split ( '.' ) ,
short = split [ 0 ] ,
ext = split [ 1 ] ? ` . ${ split [ 1 ] } ` : '' ;
renm . setAttribute ( 'class' , 'rename' ) ;
renm . setAttribute ( 'title' , 'rename file' ) ;
renm . innerHTML = '<i class="far fa-edit"></i>' ;
renm . onclick = ( ) => {
api . uc . prompt ( ` rename file ` , short ) . then ( newname => {
if ( newname && newname !== short ) {
catalog . rename ( name , ` ${ newname } ${ ext } ` , then => {
api . modal . show ( 'files' ) ;
} ) ;
}
} ) ;
} ;
load . setAttribute ( 'load' , name ) ;
load . setAttribute ( 'title' , ` file: ${ name } \n vertices: ${ file . v } \n date: ${ date } ` ) ;
load . onclick = loadCatalogFile ;
load . appendChild ( DOC . createTextNode ( short ) ) ;
del . setAttribute ( 'del' , name ) ;
del . setAttribute ( 'title' , "remove '" + name + "'" ) ;
del . onclick = ( ) => { catalog . deleteFile ( name ) } ;
del . innerHTML = '<i class="far fa-trash-alt"></i>' ;
size . setAttribute ( "disabled" , true ) ;
size . setAttribute ( "class" , "label" ) ;
size . appendChild ( DOC . createTextNode ( base . util . comma ( file . v ) ) ) ;
row . setAttribute ( "class" , "f-row a-center" ) ;
row . appendChild ( renm ) ;
row . appendChild ( load ) ;
row . appendChild ( size ) ;
row . appendChild ( del ) ;
table . appendChild ( row ) ;
}
}
function isMultiHead ( ) {
let dev = api . conf . get ( ) . device ;
return isNotBelt ( ) && dev . extruders && dev . extruders . length > 1 ;
}
function isBelt ( ) {
return api . device . isBelt ( ) ;
}
function isNotBelt ( ) {
return ! isBelt ( ) ;
}
// MAIN INITIALIZATION FUNCTION
function init _one ( ) {
let { event , conf , view , show } = api ,
{ bound , toInt , toFloat } = uc ,
{ newBlank , newButton , newBoolean , newGroup , newInput } = uc ,
{ newSelect , newLabel , newValue , newRow , newGCode , newDiv } = uc ;
event . emit ( 'init.one' ) ;
// ensure we have settings from last session
conf . restore ( ) ;
let container = $ ( 'container' ) ,
gcode = $ ( 'dev-gcode' ) ,
tracker = $ ( 'tracker' ) ,
controller = settings ( ) . controller ;
WIN . addEventListener ( "resize" , ( ) => {
event . emit ( 'resize' ) ;
} ) ;
event . on ( 'resize' , ( ) => {
if ( WIN . innerHeight < 800 ) {
ui . modalBox . classList . add ( 'mh85' ) ;
} else {
ui . modalBox . classList . remove ( 'mh85' ) ;
}
view . update _slider ( ) ;
} ) ;
space . sky . showGrid ( false ) ;
space . sky . setColor ( controller . dark ? 0 : 0xffffff ) ;
space . setAntiAlias ( controller . antiAlias ) ;
space . init ( container , function ( delta ) {
let vars = api . var ;
if ( vars . layer _max === 0 || ! delta ) return ;
if ( controller . reverseZoom ) delta = - delta ;
let same = vars . layer _hi === vars . layer _lo ;
let track = vars . layer _lo > 0 ;
if ( delta > 0 ) {
vars . layer _hi = Math . max ( same ? 0 : vars . layer _lo , vars . layer _hi - 1 ) ;
if ( track ) {
vars . layer _lo = Math . max ( 0 , vars . layer _lo - 1 ) ;
}
} else if ( delta < 0 ) {
vars . layer _hi = Math . min ( vars . layer _max , vars . layer _hi + 1 ) ;
if ( track ) {
vars . layer _lo = Math . min ( vars . layer _hi , vars . layer _lo + 1 ) ;
}
}
if ( same ) {
vars . layer _lo = vars . layer _hi ;
}
view . update _slider ( ) ;
show . slices ( ) ;
} , controller . ortho ) ;
space . platform . onMove ( conf . save ) ;
space . platform . setRound ( true ) ;
space . useDefaultKeys ( api . feature . on _key === undefined || api . feature . on _key _defaults ) ;
updateDrawer ( ) ;
// api augmentation with local functions
api . device . export = deviceExport ;
let anim = ui . anim = { } ;
Object . assign ( ui , {
tracker : tracker ,
container : container ,
alert : {
dialog : $ ( 'alert-area' ) ,
text : $ ( 'alert-text' )
} ,
func : {
slice : $ ( 'act-slice' ) ,
preview : $ ( 'act-preview' ) ,
animate : $ ( 'act-animate' ) ,
export : $ ( 'act-export' )
} ,
label : {
slice : $ ( 'label-slice' ) ,
preview : $ ( 'label-preview' ) ,
animate : $ ( 'label-animate' ) ,
export : $ ( 'label-export' ) ,
} ,
acct : {
help : $ ( 'app-help' ) ,
don8 : $ ( 'app-don8' ) ,
mesh : $ ( 'app-mesh' ) ,
export : $ ( 'app-export' )
} ,
dev : {
header : $ ( 'dev-header' ) ,
search : $ ( 'dev-search' ) ,
filter : $ ( 'dev-filter' )
} ,
mesh : {
name : $ ( 'mesh-name' ) ,
points : $ ( 'mesh-points' ) ,
faces : $ ( 'mesh-faces' ) ,
} ,
stats : {
fps : $ ( 'fps' ) ,
rms : $ ( 'rms' ) ,
div : $ ( 'stats' ) ,
rnfo : $ ( 'rnfo' ) ,
} ,
load : $ ( 'load-file' ) ,
speeds : $ ( 'speeds' ) ,
speedbar : $ ( 'speedbar' ) ,
context : $ ( 'context-menu' ) ,
ltsetup : $ ( 'lt-setup' ) ,
ltfile : $ ( 'lt-file' ) ,
ltview : $ ( 'lt-view' ) ,
ltact : $ ( 'lt-start' ) ,
edit : $ ( 'lt-tools' ) ,
nozzle : $ ( 'menu-nozzle' ) ,
modal : $ ( 'modal' ) ,
modalBox : $ ( 'modal-box' ) ,
modals : {
help : $ ( 'mod-help' ) ,
setup : $ ( 'mod-setup' ) ,
prefs : $ ( 'mod-prefs' ) ,
files : $ ( 'mod-files' ) ,
saves : $ ( 'mod-saves' ) ,
tools : $ ( 'mod-tools' ) ,
xany : $ ( 'mod-x-any' ) ,
xsla : $ ( 'mod-x-sla' ) ,
xlaser : $ ( 'mod-x-laser' ) ,
local : $ ( 'mod-local' ) ,
don8 : $ ( 'mod-don8' ) ,
any : $ ( 'mod-any' ) ,
} ,
catalogBody : $ ( 'catalogBody' ) ,
catalogList : $ ( 'catalogList' ) ,
devices : $ ( 'devices' ) ,
deviceAdd : $ ( 'device-add' ) ,
deviceDelete : $ ( 'device-del' ) ,
deviceRename : $ ( 'device-ren' ) ,
deviceExport : $ ( 'device-exp' ) ,
deviceSave : $ ( 'device-save' ) ,
toolsSave : $ ( 'tools-save' ) ,
toolsClose : $ ( 'tools-close' ) ,
toolsImport : $ ( 'tools-import' ) ,
toolsExport : $ ( 'tools-export' ) ,
toolSelect : $ ( 'tool-select' ) ,
toolAdd : $ ( 'tool-add' ) ,
toolCopy : $ ( 'tool-dup' ) ,
toolDelete : $ ( 'tool-del' ) ,
toolType : $ ( 'tool-type' ) ,
toolName : $ ( 'tool-name' ) ,
toolNum : $ ( 'tool-num' ) ,
toolFluteDiam : $ ( 'tool-fdiam' ) ,
toolFluteLen : $ ( 'tool-flen' ) ,
toolShaftDiam : $ ( 'tool-sdiam' ) ,
toolShaftLen : $ ( 'tool-slen' ) ,
toolTaperAngle : $ ( 'tool-tangle' ) ,
toolTaperTip : $ ( 'tool-ttip' ) ,
toolMetric : $ ( 'tool-metric' ) ,
setMenu : $ ( 'set-menu' ) ,
settings : $ ( 'settings' ) ,
settingsBody : $ ( 'settingsBody' ) ,
settingsList : $ ( 'settingsList' ) ,
slider : $ ( 'slider' ) ,
sliderMax : $ ( 'slider-max' ) ,
sliderMin : $ ( 'slider-zero' ) ,
sliderLo : $ ( 'slider-lo' ) ,
sliderMid : $ ( 'slider-mid' ) ,
sliderHi : $ ( 'slider-hi' ) ,
sliderHold : $ ( 'slider-hold' ) ,
sliderRange : $ ( 'slider-center' ) ,
loading : $ ( 'progress' ) . style ,
progress : $ ( 'progbar' ) . style ,
prostatus : $ ( 'progtxt' ) ,
selection : $ ( 'selection' ) ,
sizeX : $ ( 'size_x' ) ,
sizeY : $ ( 'size_y' ) ,
sizeZ : $ ( 'size_z' ) ,
scaleX : $ ( 'scale_x' ) ,
scaleY : $ ( 'scale_y' ) ,
scaleZ : $ ( 'scale_z' ) ,
lockX : $ ( 'lock_x' ) ,
lockY : $ ( 'lock_y' ) ,
lockZ : $ ( 'lock_z' ) ,
stock : $ ( 'stock' ) ,
stockWidth : $ ( 'stock-width' ) ,
stockDepth : $ ( 'stock-width' ) ,
stockHeight : $ ( 'stock-width' ) ,
/** CAM Animation Bar */
_ _ _ _ _ : {
_ _ _ _ _ : newDiv ( { addto : $ ( 'layer-animate' ) , set : true } ) ,
row : newRow ( [
anim . replay = newButton ( null , "anim.replay" , { icon : '<i class="fas fa-fast-backward"></i>' , title : "restart" } ) ,
anim . play = newButton ( null , "anim.play" , { icon : '<i class="fas fa-play"></i>' , title : "play" } ) ,
anim . pause = newButton ( null , "anim.pause" , { icon : '<i class="fas fa-pause"></i>' , title : "pause" } ) ,
anim . step = newButton ( null , "anim.step" , { icon : '<i class="fas fa-step-forward"></i>' , title : "single step" } ) ,
anim . speed = newButton ( null , "anim.fast" , { icon : '<i class="fas fa-forward"></i>' , title : "toggle speed" } ) ,
anim . labspd = newValue ( 3 , { class : "center padleft" } ) ,
anim . labx = newLabel ( "X" , { class : "padleft" } ) ,
anim . valx = newValue ( 7 , { class : "center" } ) ,
anim . laby = newLabel ( "Y" , { class : "padleft" } ) ,
anim . valy = newValue ( 7 , { class : "center" } ) ,
anim . labz = newLabel ( "Z" , { class : "padleft" } ) ,
anim . valz = newValue ( 6 , { class : "center" } ) ,
anim . laba = newLabel ( "A" , { class : "padleft hide" } ) ,
anim . vala = newValue ( 6 , { class : "center hide" } ) ,
anim . labpro = newLabel ( "%" , { class : "padleft" } ) ,
anim . progress = newValue ( 5 , { class : "center" } ) ,
anim . trans = newButton ( null , "anim.trans" , { icon : '<i class="fa-solid fa-border-none"></i>' , title : "transparency" , class : "padleft" } ) ,
anim . model = newButton ( null , "anim.model" , { icon : '<i class="fa-solid fa-eye"></i>' , title : "show model" } ) ,
anim . shade = newButton ( null , "anim.stock" , { icon : '<i class="fa-solid fa-cube"></i>' , title : "stock box" } ) ,
] )
} ,
/** Device Browser / Editor */
_ _ _ _ _ : newDiv ( { class : "f-col t-body t-inset" , addto : $ ( 'dev-config' ) , set : true } ) ,
device : newGroup ( LANG . dv _gr _dev , null , { group : "ddev" , inline , class : "noshow" } ) ,
_ _ _ _ _ : newGroup ( "workspace" , null , { group : "dext" , inline } ) ,
bedWidth : newInput ( 'X (width)' , { title : LANG . dv _bedw _l , convert : toFloat , size : 6 , units , round : 2 , action : updateDeviceSize } ) ,
bedDepth : newInput ( 'Y (depth)' , { title : LANG . dv _bedw _l , convert : toFloat , size : 6 , units , round : 2 , action : updateDeviceSize } ) ,
maxHeight : newInput ( 'Z (height)' , { title : LANG . dv _bedw _l , convert : toFloat , size : 6 , units , round : 2 , action : updateDeviceSize } ) ,
resolutionX : newInput ( LANG . dv _rezx _s , { title : LANG . dv _rezx _l , convert : toInt , size : 6 , modes : SLA } ) ,
resolutionY : newInput ( LANG . dv _rezy _s , { title : LANG . dv _rezy _l , convert : toInt , size : 6 , modes : SLA } ) ,
_ _ _ _ _ : newDiv ( { class : "f-col t-body t-inset" , addto : $ ( 'dev-config' ) , set : true , modes : NO _WEDM } ) ,
_ _ _ _ _ : newGroup ( "firmware" , null , { group : "dext" , inline , modes : NO _WEDM } ) ,
fwRetract : newBoolean ( LANG . dv _retr _s , onBooleanClick , { title : LANG . dv _retr _l , modes : FDM } ) ,
deviceOrigin : newBoolean ( LANG . dv _orgc _s , onBooleanClick , { title : LANG . dv _orgc _l , modes : FDM _LZN , show : ( ) => ! ui . deviceRound . checked } ) ,
deviceRound : newBoolean ( LANG . dv _bedc _s , onBooleanClick , { title : LANG . dv _bedc _l , modes : FDM , trigger , show : isNotBelt } ) ,
deviceBelt : newBoolean ( LANG . dv _belt _s , onBooleanClick , { title : LANG . dv _belt _l , modes : FDM , trigger , show : ( ) => ! ui . deviceRound . checked } ) ,
separator : newBlank ( { class : "pop-sep" , modes : FDM , driven } ) ,
spindleMax : newInput ( LANG . dv _spmx _s , { title : LANG . dv _spmx _l , convert : toInt , size : 5 , modes : CAM , trigger } ) ,
deviceZMax : newInput ( LANG . dv _zmax _s , { title : LANG . dv _zmax _l , convert : toInt , size : 5 , modes : FDM } ) ,
gcodeTime : newInput ( LANG . dv _time _s , { title : LANG . dv _time _l , convert : toFloat , size : 5 , modes : FDM } ) ,
_ _ _ _ _ : newDiv ( { class : "f-col t-body t-inset" , addto : $ ( 'dev-config' ) , set : true , modes : FDM } ) ,
extruder : newGroup ( LANG . dv _gr _ext , null , { group : "dext" , inline } ) ,
extFilament : newInput ( LANG . dv _fila _s , { title : LANG . dv _fila _l , convert : toFloat , modes : FDM } ) ,
extNozzle : newInput ( LANG . dv _nozl _s , { title : LANG . dv _nozl _l , convert : toFloat , modes : FDM } ) ,
extOffsetX : newInput ( LANG . dv _exox _s , { title : LANG . dv _exox _l , convert : toFloat , modes : FDM } ) ,
extOffsetY : newInput ( LANG . dv _exoy _s , { title : LANG . dv _exoy _l , convert : toFloat , modes : FDM } ) ,
extPad : newBlank ( { class : "grow" , modes : FDM } ) ,
separator : newBlank ( { class : "pop-sep" , modes : FDM , driven } ) ,
extActions : newRow ( [
ui . extPrev = newButton ( undefined , undefined , { icon : '<i class="fas fa-less-than"></i>' } ) ,
ui . extAdd = newButton ( undefined , undefined , { icon : '<i class="fas fa-plus"></i>' } ) ,
ui . extDel = newButton ( undefined , undefined , { icon : '<i class="fas fa-minus"></i>' } ) ,
ui . extNext = newButton ( undefined , undefined , { icon : '<i class="fas fa-greater-than"></i>' } )
] , { class : "dev-buttons ext-buttons var-row" , modes : FDM } ) ,
_ _ _ _ _ : newDiv ( { class : "f-col t-body t-inset" , addto : $ ( 'dev-config' ) , set : true , modes : CAM _LZR } ) ,
_ _ _ _ _ : newGroup ( LANG . dv _gr _out , null , { group : "dgco" , inline } ) ,
gcodeStrip : newBoolean ( LANG . dv _strc _s , onBooleanClick , { title : LANG . dv _strc _l , modes : CAM } ) ,
gcodeSpace : newBoolean ( LANG . dv _tksp _s , onBooleanClick , { title : LANG . dv _tksp _l , modes : CAM _LZR } ) ,
laserMaxPower : newInput ( LANG . ou _maxp _s , { title : LANG . ou _maxp _l , modes : LASER , size : 7 , text : true } ) ,
useLaser : newBoolean ( LANG . dv _lazr _s , onBooleanClick , { title : LANG . dv _lazr _l , modes : CAM } ) ,
gcodeFExt : newInput ( LANG . dv _fext _s , { title : LANG . dv _fext _l , modes : CAM _LZR , size : 7 , text : true } ) ,
gcodeEd : newGroup ( LANG . dv _gr _gco , $ ( 'dg' ) , { group : "dgcp" , inline , modes : GCODE } ) ,
gcodeMacros : newRow ( [
( ui . gcodePre = newGCode ( LANG . dv _head _s , { title : LANG . dv _head _l , modes : GCODE , area : gcode } ) ) . button ,
( ui . gcodePost = newGCode ( LANG . dv _foot _s , { title : LANG . dv _foot _l , modes : GCODE , area : gcode } ) ) . button ,
( ui . gcodeLayer = newGCode ( LANG . dv _layr _s , { title : LANG . dv _layr _l , modes : FDM , area : gcode } ) ) . button ,
( ui . gcodeTrack = newGCode ( LANG . dv _prog _s , { title : LANG . dv _prog _l , modes : FDM , area : gcode } ) ) . button ,
( ui . gcodeFan = newGCode ( LANG . dv _fanp _s , { title : LANG . dv _fanp _l , modes : FDM , area : gcode } ) ) . button ,
( ui . gcodeFeature = newGCode ( LANG . dv _feat _s , { title : LANG . dv _feat _l , modes : FDM , area : gcode } ) ) . button ,
( ui . gcodeLaserOn = newGCode ( LANG . dv _lzon _s , { title : LANG . dv _lzon _l , modes : LASER , area : gcode } ) ) . button ,
( ui . gcodeLaserOff = newGCode ( LANG . dv _lzof _s , { title : LANG . dv _lzof _l , modes : LASER , area : gcode } ) ) . button ,
( ui . gcodeWaterOn = newGCode ( LANG . dv _waon _s , { title : LANG . dv _waon _l , modes : WJET , area : gcode } ) ) . button ,
( ui . gcodeWaterOff = newGCode ( LANG . dv _waof _s , { title : LANG . dv _waof _l , modes : WJET , area : gcode } ) ) . button ,
( ui . gcodeKnifeDn = newGCode ( LANG . dv _dkon _s , { title : LANG . dv _dkon _l , modes : DRAG , area : gcode } ) ) . button ,
( ui . gcodeKnifeUp = newGCode ( LANG . dv _dkof _s , { title : LANG . dv _dkof _l , modes : DRAG , area : gcode } ) ) . button ,
( ui . gcodeChange = newGCode ( LANG . dv _tool _s , { title : LANG . dv _tool _l , modes : FDM _CAM , area : gcode } ) ) . button ,
( ui . gcodeDwell = newGCode ( LANG . dv _dwll _s , { title : LANG . dv _dwll _l , modes : CAM , area : gcode } ) ) . button ,
( ui . gcodeSpindle = newGCode ( LANG . dv _sspd _s , { title : LANG . dv _sspd _l , modes : CAM , area : gcode , show : ( ) => ui . spindleMax . value > 0 } ) ) . button
] , { class : "ext-buttons f-row gcode-macros" } ) ,
/** Preferences Menu */
_ _ _ _ _ : newGroup ( LANG . op _menu , $ ( 'prefs-gen1' ) , { inline } ) ,
antiAlias : newBoolean ( LANG . op _anta _s , booleanSave , { title : LANG . op _anta _l } ) ,
reverseZoom : newBoolean ( LANG . op _invr _s , booleanSave , { title : LANG . op _invr _l } ) ,
ortho : newBoolean ( LANG . op _orth _s , booleanSave , { title : LANG . op _orth _l } ) ,
dark : newBoolean ( LANG . op _dark _s , booleanSave , { title : LANG . op _dark _l } ) ,
drawer : newBoolean ( 'slide out' , booleanSave , { title : 'slide out settings drawer' } ) ,
scrolls : newBoolean ( 'scrollbars' , booleanSave , { title : 'show scrollbars' } ) ,
devel : newBoolean ( LANG . op _devl _s , booleanSave , { title : LANG . op _devl _l } ) ,
_ _ _ _ _ : newGroup ( LANG . op _disp , $ ( 'prefs-gen2' ) , { inline } ) ,
showOrigin : newBoolean ( LANG . op _shor _s , booleanSave , { title : LANG . op _shor _l } ) ,
showRulers : newBoolean ( LANG . op _shru _s , booleanSave , { title : LANG . op _shru _l } ) ,
showSpeeds : newBoolean ( LANG . op _sped _s , speedSave , { title : LANG . op _sped _l } ) ,
shiny : newBoolean ( LANG . op _shny _s , booleanSave , { title : LANG . op _shny _l , modes : FDM } ) ,
lineType : newSelect ( LANG . op _line _s , { title : LANG . op _line _l , action : lineTypeSave , modes : FDM } , "linetype" ) ,
animesh : newSelect ( LANG . op _anim _s , { title : LANG . op _anim _l , action : aniMeshSave , modes : CAM } , "animesh" ) ,
units : newSelect ( LANG . op _unit _s , { title : LANG . op _unit _l , action : unitsSave , modes : CAM } , "units" ) ,
edgeangle : newInput ( LANG . op _spoa _s , { title : LANG . op _spoa _l , convert : toFloat , size : 3 } ) ,
_ _ _ _ _ : newGroup ( LANG . lo _menu , $ ( 'prefs-lay' ) , { inline } ) ,
autoSave : newBoolean ( LANG . op _save _s , booleanSave , { title : LANG . op _save _l } ) ,
autoLayout : newBoolean ( LANG . op _auto _s , booleanSave , { title : LANG . op _auto _l } ) ,
freeLayout : newBoolean ( LANG . op _free _s , booleanSave , { title : LANG . op _free _l } ) ,
spaceRandoX : newBoolean ( LANG . op _spcx _s , booleanSave , { title : LANG . op _spcx _l , show : isBelt } ) ,
spaceLayout : newInput ( LANG . op _spcr _s , { title : LANG . op _spcr _l , convert : toFloat , size : 3 , units } ) ,
_ _ _ _ _ : newGroup ( LANG . xp _menu , $ ( 'prefs-xpo' ) , { inline : true } ) ,
exportLocal : newBoolean ( ` Grid:Local ` , booleanSave , { title : LANG . op _exgl _l } ) ,
exportGhost : newBoolean ( ` Grid:Host ` , booleanSave , { title : LANG . op _exgh _l } ) ,
exportOcto : newBoolean ( ` OctoPrint ` , booleanSave , { title : LANG . op _exop _l } ) ,
exportThumb : newBoolean ( ` Thumbnail ` , booleanSave , { modes : FDM } ) ,
exportPreview : newBoolean ( ` Code Preview ` , booleanSave ) ,
_ _ _ _ _ : newGroup ( LANG . pt _menu , $ ( 'prefs-prt' ) , { inline } ) ,
detail : newSelect ( LANG . pt _qual _s , { title : LANG . pt _qual _l , action : detailSave } , "detail" ) ,
healMesh : newBoolean ( LANG . pt _heal _s , booleanSave , { title : LANG . pt _heal _l } ) ,
threaded : newBoolean ( LANG . pt _thrd _s , booleanSave , { title : LANG . pt _thrd _l , modes : THREED } ) ,
assembly : newBoolean ( LANG . pt _assy _s , booleanSave , { title : LANG . pt _assy _l , modes : THREED } ) ,
prefadd : uc . checkpoint ( $ ( 'prefs-add' ) ) ,
/** FDM Settings */
_ _ _ _ _ : newGroup ( LANG . sl _menu , $ ( 'fdm-layers' ) , { modes : FDM , driven , hideable , separator , group : "fdm-layers" } ) ,
sliceHeight : newInput ( LANG . sl _lahi _s , { title : LANG . sl _lahi _l , convert : toFloat } ) ,
sliceMinHeight : newInput ( LANG . ad _minl _s , { title : LANG . ad _minl _l , convert : toFloat , bound : bound ( 0 , 3.0 ) , show : ( ) => ui . sliceAdaptive . checked } ) ,
sliceTopLayers : newInput ( LANG . sl _ltop _s , { title : LANG . sl _ltop _l , convert : toInt } ) ,
sliceBottomLayers : newInput ( LANG . sl _lbot _s , { title : LANG . sl _lbot _l , convert : toInt } ) ,
separator : newBlank ( { class : "set-sep" , driven } ) ,
sliceAdaptive : newBoolean ( LANG . ad _adap _s , onBooleanClick , { title : LANG . ad _adap _l } ) ,
_ _ _ _ _ : newGroup ( LANG . sw _menu , $ ( 'fdm-walls' ) , { modes : FDM , driven , hideable , separator , group : "fdm-walls" } ) ,
sliceShells : newInput ( LANG . sl _shel _s , { title : LANG . sl _shel _l , convert : toFloat } ) ,
sliceLineWidth : newInput ( LANG . sl _line _s , { title : LANG . sl _line _l , convert : toFloat , bound : bound ( 0 , 5 ) } ) ,
separator : newBlank ( { class : "set-sep" , driven } ) ,
sliceShellOrder : newSelect ( LANG . sl _ordr _s , { title : LANG . sl _ordr _l } , "shell" ) ,
sliceDetectThin : newSelect ( LANG . ad _thin _s , { title : LANG . ad _thin _l , action : thinWallSave } , "thin" ) ,
outputAlternating : newBoolean ( LANG . ad _altr _s , onBooleanClick , { title : LANG . ad _altr _l } ) ,
sliceZInterleave : newBoolean ( LANG . ad _zint _s , onBooleanClick , { title : LANG . ad _zint _l , show : zIntShow } ) ,
_ _ _ _ _ : newGroup ( LANG . fi _menu , $ ( 'fdm-fill' ) , { modes : FDM , driven , hideable , separator , group : "fdm-fill" } ) ,
sliceFillType : newSelect ( LANG . fi _type , { trigger } , "infill" ) ,
sliceFillSparse : newInput ( LANG . fi _pcnt _s , { title : LANG . fi _pcnt _l , convert : toFloat , bound : bound ( 0.0 , 1.0 ) , show : hasInfill } ) ,
sliceFillRepeat : newInput ( LANG . fi _rept _s , { title : LANG . fi _rept _l , convert : toInt , bound : bound ( 1 , 10 ) , show : fillIsLinear } ) ,
sliceFillOverlap : newInput ( LANG . fi _over _s , { title : LANG . fi _over _l , convert : toFloat , bound : bound ( 0.0 , 2.0 ) , show : hasInfill } ) ,
separator : newBlank ( { class : "set-sep" , driven } ) ,
sliceFillRate : newInput ( LANG . ou _feed _s , { title : LANG . ou _feed _l , convert : toInt , bound : bound ( 0 , 500 ) } ) ,
sliceSolidRate : newInput ( LANG . ou _fini _s , { title : LANG . ou _fini _l , convert : toInt , bound : bound ( 0 , 500 ) } ) ,
separator : newBlank ( { class : "set-sep" , driven } ) ,
sliceFillGrow : newInput ( LANG . fi _grow _s , { title : LANG . fi _grow _l , convert : toFloat } ) ,
sliceFillAngle : newInput ( LANG . fi _angl _s , { title : LANG . fi _angl _l , convert : toFloat } ) ,
_ _ _ _ _ : newGroup ( LANG . fh _menu , $ ( 'fdm-heat' ) , { modes : FDM , driven , hideable , separator , group : "fdm-heat" } ) ,
outputTemp : newInput ( LANG . ou _nozl _s , { title : LANG . ou _nozl _l , convert : toInt } ) ,
outputBedTemp : newInput ( LANG . ou _bedd _s , { title : LANG . ou _bedd _l , convert : toInt } ) ,
_ _ _ _ _ : newGroup ( LANG . fc _menu , $ ( 'fdm-cool' ) , { modes : FDM , driven , hideable , separator , group : "fdm-cool" } ) ,
outputFanLayer : newInput ( LANG . ou _fanl _s , { title : LANG . ou _fanl _l , convert : toInt , bound : bound ( 0 , 255 ) } ) ,
outputFanSpeed : newInput ( LANG . ou _fans _s , { title : LANG . ou _fans _l , convert : toInt , bound : bound ( 0 , 255 ) } ) ,
_ _ _ _ _ : newGroup ( LANG . sp _menu , $ ( 'fdm-support' ) , { modes : FDM , driven , hideable , separator , group : "fdm-supp" } ) ,
sliceSupportNozzle : newSelect ( LANG . sp _nozl _s , { title : LANG . sp _nozl _l , show : isMultiHead } , "extruders" ) ,
sliceSupportDensity : newInput ( LANG . sp _dens _s , { title : LANG . sp _dens _l , convert : toFloat , bound : bound ( 0.0 , 1.0 ) } ) ,
sliceSupportSize : newInput ( LANG . sp _size _s , { title : LANG . sp _size _l , convert : toFloat , bound : bound ( 1.0 , 200.0 ) } ) ,
sliceSupportOffset : newInput ( LANG . sp _offs _s , { title : LANG . sp _offs _l , convert : toFloat , bound : bound ( 0.0 , 200.0 ) } ) ,
sliceSupportGap : newInput ( LANG . sp _gaps _s , { title : LANG . sp _gaps _l , convert : toInt , bound : bound ( 0 , 5 ) } ) ,
sliceSupportArea : newInput ( LANG . sp _area _s , { title : LANG . sp _area _l , convert : toFloat , bound : bound ( 0.0 , 200.0 ) } ) ,
sliceSupportExtra : newInput ( LANG . sp _xpnd _s , { title : LANG . sp _xpnd _l , convert : toFloat , bound : bound ( 0.0 , 10.0 ) } ) ,
sliceSupportGrow : newInput ( LANG . sp _grow _s , { title : LANG . sp _grow _l , convert : toFloat , bound : bound ( 0.0 , 10.0 ) } ) ,
sliceSupportAngle : newInput ( LANG . sp _angl _s , { title : LANG . sp _angl _l , convert : toFloat , bound : bound ( 0.0 , 90.0 ) } ) ,
sliceSupportSpan : newInput ( LANG . sp _span _s , { title : LANG . sp _span _l , convert : toFloat , bound : bound ( 0.0 , 200.0 ) , show : ( ) => ui . sliceSupportEnable . checked } ) ,
separator : newBlank ( { class : "set-sep" , driven } ) ,
sliceSupportEnable : newBoolean ( LANG . sp _auto _s , onBooleanClick , { title : LANG . sp _auto _l , show : isNotBelt } ) ,
sliceSupportOutline : newBoolean ( LANG . sp _outl _s , onBooleanClick , { title : LANG . sp _outl _l } ) ,
separator : newBlank ( { class : "set-sep" , driven } ) ,
sliceSupportGen : newRow ( [
ui . ssaGen = newButton ( LANG . sp _detect , onButtonClick , { class : "f-col grow a-center" } )
] , { modes : FDM , class : "ext-buttons f-row grow" } ) ,
separator : newBlank ( { class : "set-sep" , driven } ) ,
sliceSupportManual : newRow ( [
( ui . ssmAdd = newButton ( undefined , onButtonClick , { icon : '<i class="fas fa-plus"></i>' } ) ) ,
( ui . ssmDun = newButton ( undefined , onButtonClick , { icon : '<i class="fas fa-check"></i>' } ) ) ,
( ui . ssmClr = newButton ( undefined , onButtonClick , { icon : '<i class="fas fa-trash-alt"></i>' } ) )
] , { class : "ext-buttons f-row" } ) ,
_ _ _ _ _ : newGroup ( LANG . fl _menu , $ ( 'fdm-base' ) , { modes : FDM , driven , hideable , separator , group : "fdm-base" } ) ,
firstSliceHeight : newInput ( LANG . fl _lahi _s , { title : LANG . fl _lahi _l , convert : toFloat , show : isNotBelt } ) ,
firstLayerNozzleTemp : newInput ( LANG . fl _nozl _s , { title : LANG . fl _nozl _l , convert : toInt , show : isNotBelt } ) ,
firstLayerBedTemp : newInput ( LANG . fl _bedd _s , { title : LANG . fl _bedd _l , convert : toInt , show : isNotBelt } ) ,
separator : newBlank ( { class : "set-sep" , driven } ) ,
firstLayerFanSpeed : newInput ( LANG . ou _fans _s , { title : LANG . ou _fans _l , convert : toInt , bound : bound ( 0 , 255 ) , show : isBelt } ) ,
firstLayerYOffset : newInput ( LANG . fl _zoff _s , { title : LANG . fl _zoff _l , convert : toFloat , show : isBelt } ) ,
firstLayerFlatten : newInput ( LANG . fl _flat _s , { title : LANG . fl _flat _l , convert : toFloat , show : isBelt } ) ,
firstLayerRate : newInput ( LANG . fl _rate _s , { title : LANG . fl _rate _l , convert : toFloat } ) ,
firstLayerFillRate : newInput ( LANG . fl _frat _s , { title : LANG . fl _frat _l , convert : toFloat , show : isNotBelt } ) ,
separator : newBlank ( { class : "set-sep" , driven , show : isNotBelt } ) ,
firstLayerLineMult : newInput ( LANG . fl _sfac _s , { title : LANG . fl _sfac _l , convert : toFloat , bound : bound ( 0.5 , 2 ) , show : isNotBelt } ) ,
firstLayerPrintMult : newInput ( LANG . fl _mult _s , { title : LANG . fl _mult _l , convert : toFloat } ) ,
separator : newBlank ( { class : "set-sep" , driven , show : isBelt } ) ,
firstLayerBrim : newInput ( LANG . fl _brim _s , { title : LANG . fl _brim _l , convert : toInt , show : isBelt } ) ,
firstLayerBrimIn : newInput ( LANG . fl _brin _s , { title : LANG . fl _brin _l , convert : toInt , show : isBelt } ) ,
firstLayerBrimTrig : newInput ( LANG . fl _brmn _s , { title : LANG . fl _brmn _l , convert : toInt , show : isBelt } ) ,
firstLayerBrimGap : newInput ( LANG . fl _brgp _s , { title : LANG . fl _brgp _l , convert : toFloat , show : isBelt } ) ,
separator : newBlank ( { class : "set-sep" , driven , show : isBelt } ) ,
firstLayerBeltLead : newInput ( LANG . fl _bled _s , { title : LANG . fl _bled _l , convert : toFloat , show : isBelt } ) ,
firstLayerBeltBump : newInput ( LANG . fl _blmp _s , { title : LANG . fl _blmp _l , convert : toFloat , bound : bound ( 0 , 10 ) , show : isBelt } ) ,
separator : newBlank ( { class : "set-sep" , driven , show : isNotBelt } ) ,
outputBrimCount : newInput ( LANG . fl _skrt _s , { title : LANG . fl _skrt _l , convert : toInt , show : isNotBelt } ) ,
outputBrimOffset : newInput ( LANG . fl _skro _s , { title : LANG . fl _skro _l , convert : toFloat , show : isNotBelt } ) ,
outputRaftSpacing : newInput ( LANG . fr _spac _s , { title : LANG . fr _spac _l , convert : toFloat , bound : bound ( 0.0 , 3.0 ) , show : ( ) => ui . outputRaft . checked && isNotBelt ( ) } ) ,
separator : newBlank ( { class : "set-sep" , driven , show : isNotBelt } ) ,
outputRaft : newBoolean ( LANG . fr _nabl _s , onBooleanClick , { title : LANG . fr _nabl _l , trigger , show : ( ) => isNotBelt ( ) } ) ,
outputDraftShield : newBoolean ( LANG . fr _draf _s , onBooleanClick , { title : LANG . fr _draf _l , trigger , show : ( ) => isNotBelt ( ) } ) ,
_ _ _ _ _ : newGroup ( LANG . ou _menu , $ ( 'fdm-output' ) , { modes : FDM , driven , hideable , separator , group : "fdm-out" } ) ,
outputFeedrate : newInput ( LANG . ou _feed _s , { title : LANG . ou _feed _l , convert : toInt } ) ,
outputFinishrate : newInput ( LANG . ou _fini _s , { title : LANG . ou _fini _l , convert : toInt } ) ,
outputSeekrate : newInput ( LANG . ou _move _s , { title : LANG . ou _move _l , convert : toInt } ) ,
separator : newBlank ( { class : "set-sep" , driven } ) ,
outputShellMult : newInput ( LANG . ou _shml _s , { title : LANG . ou _exml _l , convert : toFloat , bound : bound ( 0.0 , 2.0 ) } ) ,
outputFillMult : newInput ( LANG . ou _flml _s , { title : LANG . ou _exml _l , convert : toFloat , bound : bound ( 0.0 , 2.0 ) } ) ,
outputSparseMult : newInput ( LANG . ou _spml _s , { title : LANG . ou _exml _l , convert : toFloat , bound : bound ( 0.0 , 2.0 ) } ) ,
separator : newBlank ( { class : "set-sep" , driven } ) ,
outputRetractDist : newInput ( LANG . ad _rdst _s , { title : LANG . ad _rdst _l , convert : toFloat } ) ,
outputRetractSpeed : newInput ( LANG . ad _rrat _s , { title : LANG . ad _rrat _l , convert : toInt } ) ,
outputRetractWipe : newInput ( LANG . ad _wpln _s , { title : LANG . ad _wpln _l , convert : toFloat , bound : bound ( 0.0 , 10 ) } ) ,
separator : newBlank ( { class : "set-sep" , driven } ) ,
sliceLayerStart : newSelect ( LANG . sl _strt _s , { title : LANG . sl _strt _l } , "start" ) ,
outputLayerRetract : newBoolean ( LANG . ad _lret _s , onBooleanClick , { title : LANG . ad _lret _l } ) ,
outputAvoidGaps : newBoolean ( LANG . ad _agap _s , onBooleanClick , { title : LANG . ad _agap _l } ) ,
separator : newBlank ( { class : "set-sep" , driven , show : isBelt } ) ,
outputBeltFirst : newBoolean ( LANG . ad _lbir _s , onBooleanClick , { title : LANG . ad _lbir _l , show : isBelt } ) ,
_ _ _ _ _ : newGroup ( LANG . ad _menu , $ ( 'fdm-expert' ) , { modes : FDM , driven , hideable , separator , group : "fdm-xprt" } ) ,
sliceAngle : newInput ( LANG . sl _angl _s , { title : LANG . sl _angl _l , convert : toFloat , show : isBelt } ) ,
outputRetractDwell : newInput ( LANG . ad _rdwl _s , { title : LANG . ad _rdwl _l , convert : toInt } ) ,
sliceSolidMinArea : newInput ( LANG . ad _msol _s , { title : LANG . ad _msol _l , convert : toFloat } ) ,
outputMinSpeed : newInput ( LANG . ad _mins _s , { title : LANG . ad _mins _l , convert : toFloat , bound : bound ( 1 , 200 ) } ) ,
outputShortPoly : newInput ( LANG . ad _spol _s , { title : LANG . ad _spol _l , convert : toFloat , bound : bound ( 0 , 10000 ) } ) ,
outputCoastDist : newInput ( LANG . ad _scst _s , { title : LANG . ad _scst _l , convert : toFloat , bound : bound ( 0.0 , 10 ) } ) ,
zHopDistance : newInput ( LANG . ad _zhop _s , { title : LANG . ad _zhop _l , convert : toFloat , bound : bound ( 0 , 3.0 ) } ) ,
arcTolerance : newInput ( LANG . ad _arct _s , { title : LANG . ad _arct _l , convert : toFloat , bound : bound ( 0 , 1.0 ) , show : ( ) => { return isNotBelt ( ) } } ) ,
antiBacklash : newInput ( LANG . ad _abkl _s , { title : LANG . ad _abkl _l , convert : toInt , bound : bound ( 0 , 3 ) } ) ,
outputLoops : newInput ( LANG . ag _loop _s , { title : LANG . ag _loop _l , convert : toInt , bound : bound ( - 1 , 1000 ) , show : isBelt } ) ,
outputPurgeTower : newInput ( LANG . ad _purg _s , { title : LANG . ad _purg _l , convert : toInt , bound : bound ( 0 , 1000 ) } ) ,
fdmRanges : $ ( 'fdm-ranges' ) ,
/** CAM Settings */
_ _ _ _ _ : newGroup ( LANG . ct _menu , $ ( 'cam-tabs' ) , { modes : CAM , marker : true , driven , separator } ) ,
camTabsWidth : newInput ( LANG . ct _wdth _s , { title : LANG . ct _wdth _l , convert : toFloat , bound : bound ( 0.005 , 100 ) , units } ) ,
camTabsHeight : newInput ( LANG . ct _hght _s , { title : LANG . ct _hght _l , convert : toFloat , bound : bound ( 0.005 , 100 ) , units } ) ,
camTabsDepth : newInput ( LANG . ct _dpth _s , { title : LANG . ct _dpth _l , convert : toFloat , bound : bound ( 0.005 , 100 ) , units } ) ,
separator : newBlank ( { class : "set-sep" , driven } ) ,
camTabsMidline : newBoolean ( LANG . ct _midl _s , onBooleanClick , { title : LANG . ct _midl _l } ) ,
separator : newBlank ( { class : "set-sep" , driven } ) ,
camTabsManual : newRow ( [
( ui . tabAdd = newButton ( undefined , onButtonClick , { icon : '<i class="fas fa-plus"></i>' } ) ) ,
( ui . tabDun = newButton ( undefined , onButtonClick , { icon : '<i class="fas fa-check"></i>' } ) ) ,
( ui . tabClr = newButton ( undefined , onButtonClick , { icon : '<i class="fas fa-trash-alt"></i>' } ) )
] , { class : "ext-buttons f-row" } ) ,
_ _ _ _ _ : newGroup ( LANG . cs _menu , $ ( 'cam-stock' ) , { modes : CAM , driven , separator } ) ,
camStockX : newInput ( LANG . cs _wdth _s , { title : LANG . cs _wdth _l , convert : toFloat , bound : bound ( 0 , 9999 ) , units } ) ,
camStockY : newInput ( LANG . cs _dpth _s , { title : LANG . cs _dpth _l , convert : toFloat , bound : bound ( 0 , 9999 ) , units } ) ,
camStockZ : newInput ( LANG . cs _hght _s , { title : LANG . cs _hght _l , convert : toFloat , bound : bound ( 0 , 9999 ) , units } ) ,
separator : newBlank ( { class : "set-sep" , driven } ) ,
camStockOffset : newBoolean ( LANG . cs _offs _s , onBooleanClick , { title : LANG . cs _offs _l } ) ,
camStockClipTo : newBoolean ( LANG . cs _clip _s , onBooleanClick , { title : LANG . cs _clip _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 } ) ,
_ _ _ _ _ : 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 } ) ,
camZTop : newInput ( LANG . ou _ztop _s , { title : LANG . ou _ztop _l , convert : toFloat , units , trigger } ) ,
camZBottom : newInput ( LANG . ou _zbot _s , { title : LANG . ou _zbot _l , convert : toFloat , units , trigger } ) ,
camZThru : newInput ( LANG . ou _ztru _s , { title : LANG . ou _ztru _l , convert : toFloat , bound : bound ( 0.0 , 100 ) , units } ) ,
camZClearance : newInput ( LANG . ou _zclr _s , { title : LANG . ou _zclr _l , convert : toFloat , bound : bound ( 0.01 , 100 ) , units } ) ,
camFastFeedZ : newInput ( LANG . cc _rzpd _s , { title : LANG . cc _rzpd _l , convert : toFloat , units } ) ,
camFastFeed : newInput ( LANG . cc _rapd _s , { title : LANG . cc _rapd _l , convert : toFloat , units } ) ,
_ _ _ _ _ : newGroup ( LANG . ou _menu , $ ( 'cam-output' ) , { modes : CAM , driven , separator , group : "cam-output" } ) ,
camConventional : newBoolean ( LANG . ou _conv _s , onBooleanClick , { title : LANG . ou _conv _l } ) ,
camEaseDown : newBoolean ( LANG . cr _ease _s , onBooleanClick , { title : LANG . cr _ease _l } ) ,
camDepthFirst : newBoolean ( LANG . ou _depf _s , onBooleanClick , { title : LANG . ou _depf _l } ) ,
camToolInit : newBoolean ( LANG . ou _toin _s , onBooleanClick , { title : LANG . ou _toin _l } ) ,
separator : newBlank ( { class : "set-sep" , driven } ) ,
camFirstZMax : newBoolean ( LANG . ou _z1st _s , onBooleanClick , { title : LANG . ou _z1st _l } ) ,
camForceZMax : newBoolean ( LANG . ou _forz _s , onBooleanClick , { title : LANG . ou _forz _l } ) ,
separator : newBlank ( { class : "set-sep" , driven } ) ,
camEaseAngle : newInput ( LANG . ou _eang _s , { title : LANG . ou _eang _l , convert : toFloat , bound : bound ( 0.1 , 85 ) , show : ( ) => ui . camEaseDown . checked } ) ,
camFullEngage : newInput ( LANG . ou _feng _s , { title : LANG . ou _feng _l , convert : toFloat , bound : bound ( 0.1 , 1.0 ) } ) ,
_ _ _ _ _ : newGroup ( LANG . co _menu , $ ( 'cam-origin' ) , { modes : CAM , driven , separator } ) ,
camOriginTop : newBoolean ( LANG . or _topp _s , onBooleanClick , { title : LANG . or _topp _l } ) ,
camOriginCenter : newBoolean ( LANG . or _cntr _s , onBooleanClick , { title : LANG . or _cntr _l } ) ,
separator : newBlank ( { class : "set-sep" , driven } ) ,
camOriginOffX : newInput ( LANG . co _offx _s , { title : LANG . co _offx _l , convert : toFloat , units } ) ,
camOriginOffY : newInput ( LANG . co _offy _s , { title : LANG . co _offy _l , convert : toFloat , units } ) ,
camOriginOffZ : newInput ( LANG . co _offz _s , { title : LANG . co _offz _l , convert : toFloat , units } ) ,
_ _ _ _ _ : newGroup ( LANG . op _xprt _s , $ ( 'cam-expert' ) , { group : "cam_expert" , modes : CAM , marker : false , driven , separator } ) ,
camExpertFast : newBoolean ( LANG . cx _fast _s , onBooleanClick , { title : LANG . cx _fast _l , show : ( ) => ! ui . camTrueShadow . checked } ) ,
camTrueShadow : newBoolean ( LANG . cx _true _s , onBooleanClick , { title : LANG . cx _true _l , show : ( ) => ! ui . camExpertFast . checked } ) ,
separator : newBlank ( { class : "set-sep" , driven } ) ,
camArcTolerance : newInput ( LANG . cx _arct _s , { title : LANG . cx _arct _l , convert : toFloat , units , bound : bound ( 0 , 100 ) } ) ,
camArcResolution : newInput ( LANG . cx _arcr _s , { title : LANG . cx _arcr _l , convert : toFloat , bound : bound ( 0 , 180 ) , show : ( ) => ui . camArcTolerance . value > 0 } ) ,
/** LASER/DRAG/WJET/WEDM cut tool Settings */
_ _ _ _ _ : newGroup ( LANG . sl _menu , $ ( 'lzr-slice' ) , { modes : TWOD , driven , separator } ) ,
ctSliceKerf : newInput ( LANG . ls _offs _s , { title : LANG . ls _offs _l , convert : toFloat } ) ,
ctSliceHeight : newInput ( LANG . ls _lahi _s , { title : LANG . ls _lahi _l , convert : toFloat , trigger } ) ,
ctSliceHeightMin : newInput ( LANG . ls _lahm _s , { title : LANG . ls _lahm _l , convert : toFloat , show : ( ) => ui . ctSliceHeight . value == 0 && ! ui . ctSliceSingle . checked } ) ,
separator : newBlank ( { class : "set-sep" , driven } ) ,
ctSliceSingle : newBoolean ( LANG . ls _sngl _s , onBooleanClick , { title : LANG . ls _sngl _l } ) ,
ctOmitInner : newBoolean ( LANG . we _omit _s , onBooleanClick , { title : LANG . we _omit _l , modes : WEDM } ) ,
_ _ _ _ _ : newGroup ( 'surfaces' , $ ( 'lzr-surface' ) , { modes : [ - 1 ] , driven , separator } ) ,
ctSurfaces : newRow ( [
( ui . faceAdd = newButton ( undefined , onButtonClick , { icon : '<i class="fas fa-plus"></i>' } ) ) ,
( ui . faceDun = newButton ( undefined , onButtonClick , { icon : '<i class="fas fa-check"></i>' } ) ) ,
( ui . faceClr = newButton ( undefined , onButtonClick , { icon : '<i class="fas fa-trash-alt"></i>' } ) )
] , { class : "ext-buttons f-row" , modes : WEDM } ) ,
_ _ _ _ _ : newGroup ( LANG . dk _menu , $ ( 'lzr-knife' ) , { modes : DRAG , marker : true , driven , separator } ) ,
ctOutKnifeDepth : newInput ( LANG . dk _dpth _s , { title : LANG . dk _dpth _l , convert : toFloat , bound : bound ( 0.0 , 5.0 ) } ) ,
ctOutKnifePasses : newInput ( LANG . dk _pass _s , { title : LANG . dk _pass _l , convert : toInt , bound : bound ( 0 , 5 ) } ) ,
ctOutKnifeTip : newInput ( LANG . dk _offs _s , { title : LANG . dk _offs _l , convert : toFloat , bound : bound ( 0.0 , 10.0 ) } ) ,
_ _ _ _ _ : newGroup ( LANG . lo _menu , $ ( 'lzr-layout' ) , { modes : TWOD , driven , separator } ) ,
ctOutTileSpacing : newInput ( LANG . ou _spac _s , { title : LANG . ou _spac _l , convert : toInt } ) ,
ctOutMerged : newBoolean ( LANG . ou _mrgd _s , onBooleanClick , { title : LANG . ou _mrgd _l , modes : TWONED , show : ( ) => ! ui . ctOutStack . checked } ) ,
ctOutGroup : newBoolean ( LANG . ou _grpd _s , onBooleanClick , { title : LANG . ou _grpd _l , show : ( ) => ! ( ui . ctOutMark . checked || ui . ctOutStack . checked ) } ) ,
_ _ _ _ _ : newGroup ( LANG . ou _menu , $ ( 'lzr-output' ) , { modes : TWOD , driven , separator , group : "lzr-output" } ) ,
ctOutPower : newInput ( LANG . ou _powr _s , { title : LANG . ou _powr _l , convert : toInt , bound : bound ( 1 , 100 ) , modes : TWONED } ) ,
ctOutSpeed : newInput ( LANG . ou _sped _s , { title : LANG . ou _sped _l , convert : toInt } ) ,
ctAdaptive : newBoolean ( 'adaptive speed' , onBooleanClick , { modes : WEDM , title : 'controller determines best cutting speed based on material feedback at runtime' } ) ,
separator : newBlank ( { class : "set-sep" , driven } ) ,
ctOriginBounds : newBoolean ( LANG . or _bnds _s , onBooleanClick , { title : LANG . or _bnds _l , show : ( ) => ! ui . ctOriginCenter . checked } ) ,
ctOriginCenter : newBoolean ( LANG . or _cntr _s , onBooleanClick , { title : LANG . or _cntr _l , show : ( ) => ! ui . ctOriginBounds . checked } ) ,
separator : newBlank ( { class : "set-sep" , driven , modes : WEDM , show : ( ) => ui . ctOriginBounds . checked } ) ,
ctOriginOffX : newInput ( LANG . or _offx _s , { title : LANG . or _offx _l , convert : toFloat , modes : WEDM , show : ( ) => ui . ctOriginBounds . checked } ) ,
ctOriginOffY : newInput ( LANG . or _offy _s , { title : LANG . or _offy _l , convert : toFloat , modes : WEDM , show : ( ) => ui . ctOriginBounds . checked } ) ,
separator : newBlank ( { class : "set-sep" , driven , modes : TWONED } ) ,
ctOutZColor : newBoolean ( LANG . ou _layo _s , onBooleanClick , { title : LANG . ou _layo _l , modes : TWONED , show : ( ) => ! ui . ctOutMerged . checked } ) ,
ctOutLayer : newBoolean ( LANG . ou _layr _s , onBooleanClick , { title : LANG . ou _layr _l , modes : TWONED , show : ( ) => ! ui . ctOutStack . checked } ) ,
ctOutMark : newBoolean ( LANG . ou _lays _s , onBooleanClick , { title : LANG . ou _lays _l , modes : TWONED , show : ( ) => ! ui . ctOutStack . checked } ) ,
separator : newBlank ( { class : "set-sep" , driven , modes : LASER } ) ,
ctOutInches : newBoolean ( LANG . ou _inch _s , onBooleanClick , { title : LANG . ou _inch _l , modes : LASER } ) ,
ctOutStack : newBoolean ( LANG . ou _stak _s , onBooleanClick , { title : LANG . ou _stak _l , modes : LASER } ) ,
ctOutShaper : newBoolean ( LANG . ou _shap _s , onBooleanClick , { title : LANG . ou _shap _l , modes : LASER , show : ( ) => ui . ctOutStack . checked } ) ,
/** SLA SETTINGS */
slaProc : newGroup ( LANG . sa _menu , $ ( 'sla-slice' ) , { modes : SLA , group : "sla-slice" , driven , separator } ) ,
slaSlice : newInput ( LANG . sa _lahe _s , { title : LANG . sa _lahe _l , convert : toFloat } ) ,
slaShell : newInput ( LANG . sa _shel _s , { title : LANG . sa _shel _l , convert : toFloat } ) ,
slaOpenTop : newBoolean ( LANG . sa _otop _s , onBooleanClick , { title : LANG . sa _otop _l } ) ,
slaOpenBase : newBoolean ( LANG . sa _obas _s , onBooleanClick , { title : LANG . sa _obas _l } ) ,
slaLayers : newGroup ( LANG . sa _layr _m , $ ( 'sla-layers' ) , { modes : SLA , group : "sla-layers" , driven , separator } ) ,
slaLayerOn : newInput ( LANG . sa _lton _s , { title : LANG . sa _lton _l , convert : toFloat } ) ,
slaLayerOff : newInput ( LANG . sa _ltof _s , { title : LANG . sa _ltof _l , convert : toFloat } ) ,
slaPeelDist : newInput ( LANG . sa _pldi _s , { title : LANG . sa _pldi _l , convert : toFloat } ) ,
slaPeelLiftRate : newInput ( LANG . sa _pllr _s , { title : LANG . sa _pllr _l , convert : toFloat } ) ,
slaPeelDropRate : newInput ( LANG . sa _pldr _s , { title : LANG . sa _pldr _l , convert : toFloat } ) ,
slaBase : newGroup ( LANG . sa _base _m , $ ( 'sla-base' ) , { modes : SLA , group : "sla-base" , driven , separator } ) ,
slaBaseLayers : newInput ( LANG . sa _balc _s , { title : LANG . sa _balc _l , convert : toInt } ) ,
slaBaseOn : newInput ( LANG . sa _lton _s , { title : LANG . sa _bltn _l , convert : toFloat } ) ,
slaBaseOff : newInput ( LANG . sa _ltof _s , { title : LANG . sa _bltf _l , convert : toFloat } ) ,
slaBasePeelDist : newInput ( LANG . sa _pldi _s , { title : LANG . sa _pldi _l , convert : toFloat } ) ,
slaBasePeelLiftRate : newInput ( LANG . sa _pllr _s , { title : LANG . sa _pllr _l , convert : toFloat } ) ,
slaFill : newGroup ( LANG . sa _infl _m , $ ( 'sla-fill' ) , { modes : SLA , group : "sla-infill" , driven , separator } ) ,
slaFillDensity : newInput ( LANG . sa _ifdn _s , { title : LANG . sa _ifdn _l , convert : toFloat , bound : bound ( 0 , 1 ) } ) ,
slaFillLine : newInput ( LANG . sa _iflw _s , { title : LANG . sa _iflw _l , convert : toFloat , bound : bound ( 0 , 5 ) } ) ,
slaSupport : newGroup ( LANG . sa _supp _m , $ ( 'sla-support' ) , { modes : SLA , group : "sla-support" , driven , separator } ) ,
slaSupportLayers : newInput ( LANG . sa _slyr _s , { title : LANG . sa _slyr _l , convert : toInt , bound : bound ( 5 , 100 ) } ) ,
slaSupportGap : newInput ( LANG . sa _slgp _s , { title : LANG . sa _slgp _l , convert : toInt , bound : bound ( 3 , 30 ) } ) ,
slaSupportDensity : newInput ( LANG . sa _sldn _s , { title : LANG . sa _sldn _l , convert : toFloat , bound : bound ( 0.01 , 0.9 ) } ) ,
slaSupportSize : newInput ( LANG . sa _slsz _s , { title : LANG . sa _slsz _l , convert : toFloat , bound : bound ( 0.1 , 1 ) } ) ,
slaSupportPoints : newInput ( LANG . sa _slpt _s , { title : LANG . sa _slpt _l , convert : toInt , bound : bound ( 3 , 10 ) } ) ,
slaSupportEnable : newBoolean ( LANG . enable , onBooleanClick , { title : LANG . sl _slen _l } ) ,
slaOutput : newGroup ( LANG . sa _outp _m , $ ( 'sla-output' ) , { modes : SLA , driven , separator , group : "sla-output" } ) ,
slaFirstOffset : newInput ( LANG . sa _opzo _s , { title : LANG . sa _opzo _l , convert : toFloat , bound : bound ( 0 , 1 ) } ) ,
slaAntiAlias : newSelect ( LANG . sa _opaa _s , { title : LANG . sa _opaa _l } , "antialias" ) ,
layers : uc . setGroup ( $ ( "layers" ) ) ,
settingsName : $ ( 'settingsName' ) ,
settingsSave : $ ( 'settingsSave' ) ,
} ) ;
// run client mode initializations right after UI is bound
initFDM ( ) ;
initCAM ( ) ;
2025-07-05 17:54:01 -04:00
initLaser ( ) ;
2025-07-05 18:52:53 -04:00
initSLA ( ) ;
2025-07-04 22:18:48 -04:00
// override old style settings two-button menu
ui . settingsSave . onclick = ( ) => {
settingsSave ( undefined , ui . settingsName . value ) ;
} ;
function optSelected ( sel ) {
let opt = sel . options [ sel . selectedIndex ] ;
return opt ? opt . value : undefined ;
}
function hasInfill ( ) {
return optSelected ( ui . sliceFillType ) !== 'none'
}
function fillIsLinear ( ) {
return hasInfill ( ) && optSelected ( ui . sliceFillType ) === 'linear' ;
}
function spindleShow ( ) {
return settings ( ) . device . spindleMax > 0 ;
}
function zIntShow ( ) {
return settings ( ) . controller . devel ;
}
// slider setup
const mobile = space . info . mob ;
const slbar = mobile ? 80 : 30 ;
const slbar2 = slbar * 2 ;
const slider = ui . sliderRange ;
const drag = { } ;
if ( mobile ) {
ui . slider . classList . add ( 'slider-mobile' ) ;
ui . sliderLo . classList . add ( 'slider-mobile' ) ;
ui . sliderHi . classList . add ( 'slider-mobile' ) ;
// add css style for mobile devices
DOC . body . classList . add ( 'mobile' ) ;
}
function pxToInt ( txt ) {
return txt ? parseInt ( txt . substring ( 0 , txt . length - 2 ) ) : 0 ;
}
function sliderUpdate ( ) {
let start = drag . low / drag . maxval ;
let end = ( drag . low + drag . mid - slbar ) / drag . maxval ;
api . event . emit ( 'slider.pos' , { start , end } ) ;
api . var . layer _lo = Math . round ( start * api . var . layer _max ) ;
api . var . layer _hi = Math . round ( end * api . var . layer _max ) ;
api . show . layer ( ) ;
space . scene . active ( ) ;
}
function dragit ( el , delta ) {
el . ontouchstart = el . onmousedown = ( ev ) => {
// el.classList.add('sli-drag-el');
tracker . style . display = 'block' ;
ev . stopPropagation ( ) ;
let obj = ( ev . touches ? ev . touches [ 0 ] : ev ) ;
drag . width = slider . clientWidth ;
drag . maxval = drag . width - slbar2 ;
drag . start = obj . screenX ;
drag . loat = drag . low = pxToInt ( ui . sliderHold . style . marginLeft ) ;
drag . mdat = drag . mid = ui . sliderMid . clientWidth ;
drag . hiat = pxToInt ( ui . sliderHold . style . marginRight ) ;
drag . mdmax = drag . width - slbar - drag . loat ;
drag . himax = drag . width - slbar - drag . mdat ;
let cancel _drag = tracker . ontouchend = tracker . onmouseup = ( ev ) => {
// el.classList.remove('sli-drag-el');
if ( ev ) {
ev . stopPropagation ( ) ;
ev . preventDefault ( ) ;
}
slider . onmousemove = undefined ;
tracker . style . display = 'none' ;
} ;
el . ontouchend = cancel _drag ;
el . ontouchmove = tracker . ontouchmove = tracker . onmousemove = ( ev ) => {
ev . stopPropagation ( ) ;
ev . preventDefault ( ) ;
if ( ev . buttons === 0 ) {
return cancel _drag ( ) ;
}
if ( delta ) {
let obj = ( ev . touches ? ev . touches [ 0 ] : ev ) ;
delta ( obj . screenX - drag . start ) ;
}
} ;
} ;
}
dragit ( ui . sliderLo , ( delta ) => {
let midval = drag . mdat - delta ;
let lowval = drag . loat + delta ;
if ( midval < slbar || lowval < 0 ) {
return ;
}
ui . sliderHold . style . marginLeft = ` ${ lowval } px ` ;
ui . sliderMid . style . width = ` ${ midval } px ` ;
drag . low = lowval ;
drag . mid = midval ;
sliderUpdate ( ) ;
} ) ;
dragit ( ui . sliderMid , ( delta ) => {
let loval = drag . loat + delta ;
let hival = drag . hiat - delta ;
if ( loval < 0 || hival < 0 ) return ;
ui . sliderHold . style . marginLeft = ` ${ loval } px ` ;
ui . sliderHold . style . marginRight = ` ${ hival } px ` ;
drag . low = loval ;
sliderUpdate ( ) ;
} ) ;
dragit ( ui . sliderHi , ( delta ) => {
let midval = drag . mdat + delta ;
let hival = drag . hiat - delta ;
if ( midval < slbar || midval > drag . mdmax || hival < 0 ) return ;
ui . sliderMid . style . width = ` ${ midval } px ` ;
ui . sliderHold . style . marginRight = ` ${ hival } px ` ;
drag . mid = midval ;
sliderUpdate ( ) ;
} ) ;
ui . load . onchange = function ( event ) {
api . platform . load _files ( event . target . files ) ;
ui . load . value = '' ; // reset so you can re-import the same filee
} ;
ui . sliderMin . onclick = ( ) => {
api . show . layer ( 0 , 0 ) ;
}
ui . sliderMax . onclick = ( ) => {
api . show . layer ( api . var . layer _max , 0 ) ;
}
ui . slider . onmouseover = ( ev ) => {
api . event . emit ( 'slider.label' ) ;
} ;
ui . slider . onmouseleave = ( ev ) => {
if ( ! ev . buttons ) api . event . emit ( 'slider.unlabel' ) ;
} ;
api . event . on ( 'slider.unlabel' , ( values ) => {
} ) ;
api . event . on ( 'slider.label' , ( values ) => {
let digits = api . var . layer _max . toString ( ) . length ;
$ ( 'slider-zero' ) . style . width = ` ${ digits } em ` ;
$ ( 'slider-max' ) . style . width = ` ${ digits } em ` ;
$ ( 'slider-zero' ) . innerText = api . var . layer _lo ;
$ ( 'slider-max' ) . innerText = api . var . layer _hi ;
} ) ;
api . event . on ( 'slider.set' , ( values ) => {
let width = slider . clientWidth ;
let maxval = width - slbar2 ;
let start = Math . max ( 0 , Math . min ( 1 , values . start ) ) ;
let end = Math . max ( start , Math . min ( 1 , values . end ) ) ;
let lowval = start * maxval ;
let midval = ( ( end - start ) * maxval ) + slbar ;
let hival = maxval - end * maxval ;
ui . sliderHold . style . marginLeft = ` ${ lowval } px ` ;
ui . sliderMid . style . width = ` ${ midval } px ` ;
ui . sliderHold . style . marginRight = ` ${ hival } px ` ;
} ) ;
// store layer preferences
api . event . on ( 'stack.show' , label => {
let mode = api . mode . get ( ) ;
let view = api . view . get ( ) ;
api . conf . get ( ) . labels [ ` ${ mode } - ${ view } - ${ label } ` ] = true ;
} ) ;
api . event . on ( 'stack.hide' , label => {
let mode = api . mode . get ( ) ;
let view = api . view . get ( ) ;
api . conf . get ( ) . labels [ ` ${ mode } - ${ view } - ${ label } ` ] = false ;
} ) ;
// bind language choices
$ ( 'lset-en' ) . onclick = function ( ) {
sdb . setItem ( 'kiri-lang' , 'en-us' ) ;
api . space . reload ( ) ;
} ;
$ ( 'lset-da' ) . onclick = function ( ) {
sdb . setItem ( 'kiri-lang' , 'da-dk' ) ;
api . space . reload ( ) ;
} ;
$ ( 'lset-de' ) . onclick = function ( ) {
sdb . setItem ( 'kiri-lang' , 'de-de' ) ;
api . space . reload ( ) ;
} ;
$ ( 'lset-fr' ) . onclick = function ( ) {
sdb . setItem ( 'kiri-lang' , 'fr-fr' ) ;
api . space . reload ( ) ;
} ;
$ ( 'lset-pl' ) . onclick = function ( ) {
sdb . setItem ( 'kiri-lang' , 'pl-pl' ) ;
api . space . reload ( ) ;
} ;
$ ( 'lset-pt' ) . onclick = function ( ) {
sdb . setItem ( 'kiri-lang' , 'pt-pt' ) ;
api . space . reload ( ) ;
} ;
$ ( 'lset-es' ) . onclick = function ( ) {
sdb . setItem ( 'kiri-lang' , 'es-es' ) ;
api . space . reload ( ) ;
} ;
$ ( 'lset-zh' ) . onclick = function ( ) {
sdb . setItem ( 'kiri-lang' , 'zh' ) ;
api . space . reload ( ) ;
} ;
space . event . addHandlers ( self , [
'keyup' , keyUpHandler ,
'keydown' , keyDownHandler ,
'keypress' , keyHandler ,
'dragover' , dragOverHandler ,
'dragleave' , dragLeave ,
'drop' , dropHandler
] ) ;
function selectionSize ( e ) {
let dv = parseFloat ( e . target . value || 1 ) ,
pv = parseFloat ( e . target . was || 1 ) ,
ra = dv / pv ,
xv = parseFloat ( ui . sizeX . was ? ? ui . scaleX . value ) || 1 ,
yv = parseFloat ( ui . sizeY . was ? ? ui . scaleY . value ) || 1 ,
zv = parseFloat ( ui . sizeZ . was ? ? ui . scaleZ . value ) || 1 ,
ta = e . target ,
xc = ui . lockX . checked ,
yc = ui . lockY . checked ,
zc = ui . lockZ . checked ,
xt = ta === ui . sizeX ,
yt = ta === ui . sizeY ,
zt = ta === ui . sizeZ ,
tl = ( xt && xc ) || ( yt && yc ) || ( zt && zc ) ,
xr = ( ( tl && xc ) || ( ! tl && xt ) ? ra : 1 ) ,
yr = ( ( tl && yc ) || ( ! tl && yt ) ? ra : 1 ) ,
zr = ( ( tl && zc ) || ( ! tl && zt ) ? ra : 1 ) ;
// prevent null scale
if ( xr * yr * zr === 0 ) {
return ;
}
selection . scale ( xr , yr , zr ) ;
ui . sizeX . was = ui . sizeX . value = xv * xr ;
ui . sizeY . was = ui . sizeY . value = yv * yr ;
ui . sizeZ . was = ui . sizeZ . value = zv * zr ;
}
function selectionScale ( e ) {
let dv = parseFloat ( e . target . value || 1 ) ,
pv = parseFloat ( e . target . was || 1 ) ,
ra = dv / pv ,
xv = parseFloat ( ui . scaleX . was ? ? ui . scaleX . value ) || 1 ,
yv = parseFloat ( ui . scaleY . was ? ? ui . scaleY . value ) || 1 ,
zv = parseFloat ( ui . scaleZ . was ? ? ui . scaleY . value ) || 1 ,
ta = e . target ,
xc = ui . lockX . checked ,
yc = ui . lockY . checked ,
zc = ui . lockZ . checked ,
xt = ta === ui . scaleX ,
yt = ta === ui . scaleY ,
zt = ta === ui . scaleZ ,
tl = ( xt && xc ) || ( yt && yc ) || ( zt && zc ) ,
xr = ( ( tl && xc ) || ( ! tl && xt ) ? ra : 1 ) ,
yr = ( ( tl && yc ) || ( ! tl && yt ) ? ra : 1 ) ,
zr = ( ( tl && zc ) || ( ! tl && zt ) ? ra : 1 ) ;
selection . scale ( xr , yr , zr ) ;
ui . scaleX . was = ui . scaleX . value = xv * xr ;
ui . scaleY . was = ui . scaleY . value = yv * yr ;
ui . scaleZ . was = ui . scaleZ . value = zv * zr ;
}
function selectionRotate ( e ) {
let val = parseFloat ( e . target . value ) || 0 ;
e . target . value = val ;
}
space . event . onEnterKey ( [
ui . scaleX , selectionScale ,
ui . scaleY , selectionScale ,
ui . scaleZ , selectionScale ,
ui . sizeX , selectionSize ,
ui . sizeY , selectionSize ,
ui . sizeZ , selectionSize ,
ui . toolName , updateTool ,
ui . toolNum , updateTool ,
ui . toolFluteDiam , updateTool ,
ui . toolFluteLen , updateTool ,
ui . toolShaftDiam , updateTool ,
ui . toolShaftLen , updateTool ,
ui . toolTaperTip , updateTool ,
ui . toolTaperAngle , updateTool ,
$ ( 'rot_x' ) , selectionRotate ,
$ ( 'rot_y' ) , selectionRotate ,
$ ( 'rot_z' ) , selectionRotate
] , true ) ;
$ ( 'lab-axis' ) . onclick = ( ) => {
ui . lockX . checked =
ui . lockY . checked =
ui . lockZ . checked = ! (
ui . lockX . checked ||
ui . lockY . checked ||
ui . lockZ . checked
) ;
} ;
$ ( 'scale-reset' ) . onclick = $ ( 'lab-scale' ) . onclick = ( ) => {
selection . scale ( 1 / ui . scaleX . was , 1 / ui . scaleY . was , 1 / ui . scaleZ . was ) ;
ui . scaleX . value = ui . scaleY . value = ui . scaleZ . value =
ui . scaleX . was = ui . scaleY . was = ui . scaleZ . was = 1 ;
} ;
$ ( 'app-xpnd' ) . onclick = ( ) => {
try {
DOC . body . requestFullscreen ( ) ;
} catch ( e ) {
event . emit ( 'resize' ) ;
moto . space . event . onResize ( ) ;
}
} ;
if ( ! DOC . body . requestFullscreen ) {
$ ( 'app-xpnd' ) . style . display = 'none' ;
}
uc . onBlur ( [
ui . toolName ,
ui . toolNum ,
ui . toolFluteDiam ,
ui . toolFluteLen ,
ui . toolShaftDiam ,
ui . toolShaftLen ,
ui . toolTaperTip ,
] , updateTool ) ;
ui . toolMetric . onclick = updateTool ;
ui . toolType . onchange = updateTool ;
// default show gcode pre
ui . gcodePre . button . click ( ) ;
function mksvg ( src ) {
let svg = DOC . createElement ( 'svg' ) ;
svg . innerHTML = src ;
return svg ;
}
function mklbl ( src ) {
let lbl = DOC . createElement ( 'label' ) ;
lbl . innerText = src ;
return lbl ;
}
api . platform . update _size ( ) ;
function mouseOnHover ( int , event , ints ) {
if ( ! api . feature . hover ) return ;
if ( ! int ) return api . feature . hovers || api . widgets . meshes ( ) ;
api . event . emit ( 'mouse.hover' , { int , ints , event , point : int . point , type : 'widget' } ) ;
}
function platformOnHover ( int , event ) {
if ( ! api . feature . hover ) return ;
if ( int ) api . event . emit ( 'mouse.hover' , { point : int , event , type : 'platform' } ) ;
}
api . event . on ( "feature.hover" , enable => {
space . mouse . onHover ( enable ? mouseOnHover : undefined ) ;
space . platform . onHover ( enable ? platformOnHover : undefined ) ;
} ) ;
// block standard browser context menu
DOC . oncontextmenu = ( event ) => {
let et = event . target ;
if ( et . tagName === 'CANVAS' || et . id === 'context-menu' || et . classList . contains ( 'draggable' ) ) {
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
return false ;
}
} ;
space . mouse . downSelect ( ( int , event ) => {
if ( api . feature . on _mouse _down ) {
if ( int ) {
api . feature . on _mouse _down ( int , event ) ;
return ;
}
}
if ( api . feature . hover ) {
if ( int ) {
return api . event . emit ( 'mouse.hover.down' , { int , point : int . point } ) ;
} else {
return selection . meshes ( ) ;
}
}
// lay flat with meta or ctrl clicking a selected face
if ( int && ( event . ctrlKey || event . metaKey || api . feature . on _face _select ) ) {
let q = new THREE . Quaternion ( ) ;
// find intersecting point, look "up" on Z and rotate to face that
q . setFromUnitVectors ( int . face . normal , new THREE . Vector3 ( 0 , 0 , - 1 ) ) ;
selection . rotate ( q ) ;
}
if ( api . view . get ( ) !== VIEWS . ARRANGE ) {
// return no selection in modes other than arrange
return null ;
} else {
// return selected meshes for further mouse processing
return api . feature . hovers || selection . meshes ( ) ;
}
} ) ;
space . mouse . upSelect ( ( object , event ) => {
if ( api . feature . on _mouse _up ) {
if ( event && object ) {
return api . feature . on _mouse _up ( object , event ) ;
} else {
return api . widgets . meshes ( ) ;
}
}
if ( event && api . feature . hover ) {
api . event . emit ( 'mouse.hover.up' , { object , event } ) ;
return ;
}
if ( event && event . target . nodeName === "CANVAS" ) {
if ( object && object . object ) {
if ( object . object . widget ) {
platform . select ( object . object . widget , event . shiftKey , false ) ;
}
} else {
platform . deselect ( ) ;
}
} else {
return api . feature . hovers || api . widgets . meshes ( ) ;
}
} ) ;
space . mouse . onDrag ( function ( delta , offset , up = false ) {
if ( api . feature . hover ) {
return ;
}
if ( up ) {
api . event . emit ( 'mouse.drag.done' , offset ) ;
}
if ( delta && ui . freeLayout . checked ) {
let set = settings ( ) ;
let dev = set . device ;
let bound = set . bounds _sel ;
let width = dev . bedWidth / 2 ;
let depth = dev . bedDepth / 2 ;
let isout = (
bound . min . x <= - width ||
bound . min . y <= - depth ||
bound . max . x >= width ||
bound . max . y >= depth
) ;
if ( ! isout ) {
if ( bound . min . x + delta . x <= - width ) return ;
if ( bound . min . y + delta . y <= - depth ) return ;
if ( bound . max . x + delta . x >= width ) return ;
if ( bound . max . y + delta . y >= depth ) return ;
}
selection . move ( delta . x , delta . y , 0 ) ;
api . event . emit ( 'selection.drag' , delta ) ;
} else {
return selection . meshes ( ) . length > 0 ;
}
} ) ;
api . space . restore ( init _two ) || checkSeed ( init _two ) || init _two ( ) ;
} ;
// SECOND STAGE INIT AFTER UI RESTORED
function init _two ( ) {
api . event . emit ( 'init.two' ) ;
// load script extensions
if ( SETUP . s ) SETUP . s . forEach ( function ( lib ) {
let scr = DOC . createElement ( 'script' ) ;
scr . setAttribute ( 'defer' , true ) ;
2025-07-05 12:41:00 -04:00
scr . setAttribute ( 'src' , ` /code/ ${ lib } .js? ${ version } ` ) ;
2025-07-04 22:18:48 -04:00
DOC . body . appendChild ( scr ) ;
stats . add ( 'load_' + lib ) ;
api . event . emit ( 'load.lib' , lib ) ;
} ) ;
// load CSS extensions
if ( SETUP . ss ) SETUP . ss . forEach ( function ( style ) {
style = style . charAt ( 0 ) === '/' ? style : ` /kiri/style- ${ style } ` ;
let ss = DOC . createElement ( 'link' ) ;
ss . setAttribute ( "type" , "text/css" ) ;
ss . setAttribute ( "rel" , "stylesheet" ) ;
2025-07-05 12:41:00 -04:00
ss . setAttribute ( "href" , ` ${ style } .css? ${ version } ` ) ;
2025-07-04 22:18:48 -04:00
DOC . body . appendChild ( ss ) ;
} ) ;
// override stored settings
if ( SETUP . v ) SETUP . v . forEach ( function ( kv ) {
kv = kv . split ( '=' ) ;
sdb . setItem ( kv [ 0 ] , kv [ 1 ] ) ;
} ) ;
// import octoprint settings
if ( SETUP . ophost ) {
let ohost = api . const . OCTO = {
host : SETUP . ophost [ 0 ] ,
apik : SETUP . opkey ? SETUP . opkey [ 0 ] : ''
} ;
sdb [ 'octo-host' ] = ohost . host ;
sdb [ 'octo-apik' ] = ohost . apik ;
console . log ( { octoprint : ohost } ) ;
}
// load workspace from url
if ( SETUP . wrk ) {
set _ctrl . import _url ( ` ${ proto } // ${ SETUP . wrk [ 0 ] } ` , false ) ;
}
// load an object from url
if ( SETUP . load ) {
console . log ( { load : SETUP } ) ;
api . platform . load _url ( ` ${ proto } // ${ SETUP . load [ 0 ] } ` ) ;
}
// bind this to UI so main can call it on settings import
ui . sync = function ( ) {
const current = settings ( ) ;
const control = current . controller ;
if ( ! control . devel ) {
// TODO: hide thin type 3 during development
2025-07-05 12:41:00 -04:00
consts . LISTS . thin . length = 3 ;
2025-07-04 22:18:48 -04:00
}
platform . deselect ( ) ;
catalog . addFileListener ( updateCatalog ) ;
space . view . setZoom ( control . reverseZoom , control . zoomSpeed ) ;
space . platform . setGridZOff ( undefined ) ;
space . platform . setZOff ( 0.05 ) ;
// restore UI state from settings
ui . showOrigin . checked = control . showOrigin ;
ui . showRulers . checked = control . showRulers ;
ui . showSpeeds . checked = control . showSpeeds ;
ui . freeLayout . checked = control . freeLayout ;
ui . autoLayout . checked = control . autoLayout ;
ui . spaceRandoX . checked = control . spaceRandoX ;
ui . antiAlias . checked = control . antiAlias ;
ui . reverseZoom . checked = control . reverseZoom ;
ui . autoSave . checked = control . autoSave ;
ui . healMesh . checked = control . healMesh ;
ui . threaded . checked = setThreaded ( control . threaded ) ;
ui . assembly . checked = control . assembly ;
ui . ortho . checked = control . ortho ;
ui . devel . checked = control . devel ;
lineTypeSave ( ) ;
detailSave ( ) ;
updateStats ( ) ;
// optional set-and-lock mode (hides mode menu)
let SETMODE = SETUP . mode ? SETUP . mode [ 0 ] : null ;
// optional set-and-lock device (hides device menu)
let DEVNAME = SETUP . dev ? SETUP . dev [ 0 ] : null ;
// setup default mode and enable mode locking, if set
api . mode . set ( SETMODE || STARTMODE || current . mode , SETMODE ) ;
// fill device list
api . devices . refresh ( ) ;
// update ui fields from settings
api . conf . update _fields ( ) ;
// default to ARRANGE view mode
api . view . set ( VIEWS . ARRANGE ) ;
// add ability to override
api . show . controls ( api . feature . controls ) ;
// update everything dependent on the platform size
platform . update _size ( ) ;
// load wasm if indicated
client . wasm ( control . assembly === true ) ;
} ;
ui . sync ( ) ;
// clear alerts as they build up
setInterval ( api . event . alerts , 1000 ) ;
// add hide-alerts-on-alert-click
ui . alert . dialog . onclick = function ( ) {
api . event . alerts ( true ) ;
} ;
// enable modal hiding
$ ( 'mod-x' ) . onclick = api . modal . hide ;
if ( ! SETUP . s ) console . log ( ` kiri | init main | ${ version } ` ) ;
// send init-done event
api . event . emit ( 'init-done' , stats ) ;
// show gdpr if it's never been seen and we're not iframed
const isLocal = LOCAL || WIN . location . host . split ( ':' ) [ 0 ] === 'localhost' ;
if ( ! sdb . gdpr && WIN . self === WIN . top && ! SETUP . debug && ! isLocal ) {
$ ( 'gdpr' ) . style . display = 'flex' ;
}
// warn of degraded functionality when SharedArrayBuffers are missing
if ( api . feature . work _alerts && ! window . SharedArrayBuffer ) {
api . alerts . show ( "The security context of this" , 10 ) ;
api . alerts . show ( "Window blocks important functionality." , 10 ) ;
api . alerts . show ( "Try a Chromium-base Browser instead" , 10 ) ;
}
// add keyboard focus handler (must use for iframes)
WIN . addEventListener ( 'load' , function ( ) {
WIN . focus ( ) ;
DOC . body . addEventListener ( 'click' , function ( ) {
WIN . focus ( ) ;
} , false ) ;
} ) ;
// dismiss gdpr alert
$ ( 'gotit' ) . onclick = ( ) => {
$ ( 'gdpr' ) . style . display = 'none' ;
sdb . gdpr = Date . now ( ) ;
} ;
// bind interface action elements
$ ( 'mode-device' ) . onclick = api . show . devices ;
$ ( 'mode-profile' ) . onclick = settingsLoad ;
$ ( 'mode-fdm' ) . onclick = ( ) => api . mode . set ( 'FDM' ) ;
$ ( 'mode-cam' ) . onclick = ( ) => api . mode . set ( 'CAM' ) ;
$ ( 'mode-sla' ) . onclick = ( ) => api . mode . set ( 'SLA' ) ;
$ ( 'mode-laser' ) . onclick = ( ) => api . mode . set ( 'LASER' ) ;
$ ( 'mode-drag' ) . onclick = ( ) => api . mode . set ( 'DRAG' ) ;
$ ( 'mode-wjet' ) . onclick = ( ) => api . mode . set ( 'WJET' ) ;
$ ( 'mode-wedm' ) . onclick = ( ) => api . mode . set ( 'WEDM' ) ;
$ ( 'set-device' ) . onclick = ( ev ) => { ev . stopPropagation ( ) ; api . show . devices ( ) } ;
$ ( 'set-profs' ) . onclick = ( ev ) => { ev . stopPropagation ( ) ; api . conf . show ( ) } ;
$ ( 'set-tools' ) . onclick = ( ev ) => { ev . stopPropagation ( ) ; api . show . tools ( ) } ;
$ ( 'set-prefs' ) . onclick = ( ev ) => { ev . stopPropagation ( ) ; api . modal . show ( 'prefs' ) } ;
ui . acct . help . onclick = ( ev ) => { ev . stopPropagation ( ) ; api . help . show ( ) } ;
ui . acct . don8 . onclick = ( ev ) => { ev . stopPropagation ( ) ; api . modal . show ( 'don8' ) } ;
ui . acct . mesh . onclick = ( ev ) => { ev . stopPropagation ( ) ; WIN . location = "/mesh" } ;
ui . acct . export . onclick = ( ev ) => { ev . stopPropagation ( ) ; profileExport ( ) } ;
ui . acct . export . title = LANG . acct _xpo ;
$ ( 'file-new' ) . onclick = ( ev ) => { ev . stopPropagation ( ) ; workspaceNew ( ) } ;
$ ( 'file-recent' ) . onclick = ( ) => { api . modal . show ( 'files' ) } ;
$ ( 'file-import' ) . onclick = ( ev ) => { api . event . import ( ev ) ; } ;
ui . func . slice . onclick = ( ev ) => { ev . stopPropagation ( ) ; api . function . slice ( ) } ;
ui . func . preview . onclick = ( ev ) => { ev . stopPropagation ( ) ; api . function . print ( ) } ;
ui . func . animate . onclick = ( ev ) => { ev . stopPropagation ( ) ; api . function . animate ( ) } ;
ui . func . export . onclick = ( ev ) => { ev . stopPropagation ( ) ; api . function . export ( ) } ;
$ ( 'view-arrange' ) . onclick = api . platform . layout ;
$ ( 'view-top' ) . onclick = space . view . top ;
$ ( 'view-home' ) . onclick = space . view . home ;
$ ( 'view-front' ) . onclick = space . view . front ;
$ ( 'view-back' ) . onclick = space . view . back ;
$ ( 'view-left' ) . onclick = space . view . left ;
$ ( 'view-right' ) . onclick = space . view . right ;
$ ( 'unrotate' ) . onclick = ( ) => {
api . widgets . for ( w => w . unrotate ( ) ) ;
selection . update _info ( ) ;
} ;
// attach button handlers to support targets
for ( let btn of [ "don8pt" , "don8gh" , "don8pp" ] ) {
$ ( btn ) . onclick = ( ev ) => {
window . open ( ev . target . children [ 0 ] . href ) ;
}
}
// rotation buttons
let d = ( Math . PI / 180 ) ;
$ ( 'rot_x_lt' ) . onclick = ( ) => { selection . rotate ( - d * $ ( 'rot_x' ) . value , 0 , 0 ) } ;
$ ( 'rot_x_gt' ) . onclick = ( ) => { selection . rotate ( d * $ ( 'rot_x' ) . value , 0 , 0 ) } ;
$ ( 'rot_y_lt' ) . onclick = ( ) => { selection . rotate ( 0 , - d * $ ( 'rot_y' ) . value , 0 ) } ;
$ ( 'rot_y_gt' ) . onclick = ( ) => { selection . rotate ( 0 , d * $ ( 'rot_y' ) . value , 0 ) } ;
$ ( 'rot_z_lt' ) . onclick = ( ) => { selection . rotate ( 0 , 0 , d * $ ( 'rot_z' ) . value ) } ;
$ ( 'rot_z_gt' ) . onclick = ( ) => { selection . rotate ( 0 , 0 , - d * $ ( 'rot_z' ) . value ) } ;
// rendering options
$ ( 'render-edges' ) . onclick = ( ) => { api . view . edges ( { toggle : true } ) ; api . conf . save ( ) } ;
$ ( 'render-ghost' ) . onclick = ( ) => { api . view . wireframe ( false , 0 , api . view . is _arrange ( ) ? 0.4 : 0.25 ) ; } ;
$ ( 'render-wire' ) . onclick = ( ) => { api . view . wireframe ( true , 0 , api . space . is _dark ( ) ? 0.25 : 0.5 ) ; } ;
$ ( 'render-solid' ) . onclick = ( ) => { api . view . wireframe ( false , 0 , 1 ) ; } ;
$ ( 'mesh-export-stl' ) . onclick = ( ) => { objectsExport ( 'stl' ) } ;
$ ( 'mesh-export-obj' ) . onclick = ( ) => { objectsExport ( 'obj' ) } ;
$ ( 'mesh-merge' ) . onclick = selection . merge ;
$ ( 'context-duplicate' ) . onclick = duplicateSelection ;
$ ( 'context-mirror' ) . onclick = mirrorSelection ;
$ ( 'context-layflat' ) . onclick = ( ) => { api . event . emit ( "tool.mesh.lay-flat" ) } ;
$ ( 'context-setfocus' ) . onclick = ( ) => {
api . event . emit (
"tool.camera.focus" ,
ev => api . space . set _focus ( undefined , ev . object . point )
) ;
} ;
// ui.modal.onclick = api.modal.hide;
ui . modalBox . onclick = ( ev ) => { ev . stopPropagation ( ) } ;
// add app name hover info
$ ( 'app-info' ) . innerText = version ;
// show topline separator when iframed
try { if ( WIN . self !== WIN . top ) $ ( 'top-sep' ) . style . display = 'flex' } catch ( e ) { console . log ( e ) }
// warn users they are running a beta release
2025-07-05 12:41:00 -04:00
if ( beta && beta > 0 && sdb . kiri _beta != beta ) {
2025-07-04 22:18:48 -04:00
api . show . alert ( "CAUTION" ) ;
api . show . alert ( "this is a development release" ) ;
api . show . alert ( "and may not function properly" ) ;
sdb . kiri _beta = beta ;
}
// lift curtain
$ ( 'curtain' ) . style . display = 'none' ;
}
// update static html elements with language overrides
function lang _rewrite ( ) {
// lk attribute causes inner text to be replaced with lang value
for ( let el of [ ... DOC . querySelectorAll ( "[lk]" ) ] ) {
let key = el . getAttribute ( 'lk' ) ;
let val = LANG [ key ] ;
if ( val ) {
el . innerText = val ;
} else {
console . log ( { missing _ln : key } ) ;
}
}
// lkt attribute causes a title attribute to be set from lang value
for ( let el of [ ... DOC . querySelectorAll ( "[lkt]" ) ] ) {
let key = el . getAttribute ( 'lkt' ) ;
let val = LANG [ key ] ;
if ( val ) {
el . setAttribute ( "title" , val ) ;
} else {
console . log ( { missing _ln : key } ) ;
}
}
}
// init lang must happen before all other init functions
function init _lang ( ) {
// if a language needs to load, the script is injected and loaded
// first. once this loads, or doesn't, the initialization begins
let lang = SETUP . ln ? SETUP . ln [ 0 ] : sdb . getItem ( 'kiri-lang' ) || api . language . get ( ) ;
api . event . emit ( 'init.lang' , lang ) ;
// inject language script if not english
if ( lang && lang !== 'en' && lang !== 'en-us' ) {
let map = api . language . map ( lang ) ;
let scr = DOC . createElement ( 'script' ) ;
// scr.setAttribute('defer',true);
scr . setAttribute ( 'src' , ` /kiri/lang/ ${ map } .js? ${ version } ` ) ;
( DOC . body || DOC . head ) . appendChild ( scr ) ;
stats . set ( 'll' , lang ) ;
scr . onload = function ( ) {
api . language . set ( map ) ;
lang _rewrite ( ) ;
init _one ( ) ;
} ;
scr . onerror = function ( err ) {
console . log ( { language _load _error : err , lang } )
api . language . set ( ) ;
lang _rewrite ( ) ;
init _one ( ) ;
}
} else {
// set to browser default which will be overridden
// by any future script loads (above)
api . language . set ( ) ;
lang _rewrite ( ) ;
init _one ( ) ;
}
}
export function run ( ) {
init _lang ( ) ;
2025-07-05 12:41:00 -04:00
return api ;
2025-07-04 22:18:48 -04:00
}