Merge pull request #1 from GridSpace/master

Latest pull
This commit is contained in:
Albert Møller Nielsen 2021-03-15 20:24:18 +01:00 • committed by GitHub
commit 365427ede7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
76 changed files with 7225 additions and 2047 deletions

201
app.js
View file

@ -21,10 +21,10 @@ const load = [];
const synth = {};
const api = {};
let level;
let setupFn;
let cacheDir;
let startTime;
let oversion;
let lastmod;
let logger;
let debug;
@ -43,25 +43,8 @@ function init(mod) {
dir = mod.dir;
log = mod.log;
if (mod.util.globdir) {
// when globdir available, look for shared kvstore
// and if not present, create and share in through env
if (mod.env._level) {
level = mod.env._level;
logger.log("using shared kvstore");
} else {
level = require('level')(mod.util.globdir("kvstore"), {valueEncoding:"json"});
mod.env._level = level;
logger.log("creating shared kvstore");
}
} else {
logger.log("fallback to local kvstore");
level = require('level')(mod.util.datadir("kvstore"), {valueEncoding:"json"});
}
cacheDir = mod.util.datadir("cache");
mod.on.reload(() => level.close());
mod.on.test((req) => {
let cookie = cookieValue(req.headers.cookie, "version") || undefined;
let vmatch = mod.meta.version || "*";
@ -85,7 +68,6 @@ function init(mod) {
mod.add(handleVersion);
mod.add(prepath([
[ "/code/", handleCode ],
[ "/data/", handleData ],
[ "/wasm/", handleWasm ]
]));
mod.add(fixedmap("/api/", api));
@ -144,7 +126,8 @@ function initModule(mod, file, dir) {
require_fresh(file)({
api: api,
adm: {
reload: prepareScripts
reload: prepareScripts,
setver: (ver) => { oversion = ver }
},
const: {
args: {},
@ -166,6 +149,8 @@ function initModule(mod, file, dir) {
time: time,
guid: guid,
mkdirs: util.mkdir,
isfile: util.isfile,
confdir: util.confdir,
datadir: util.datadir,
lastmod: lastmod,
obj2string: obj2string,
@ -173,10 +158,6 @@ function initModule(mod, file, dir) {
getCookieValue: cookieValue,
logger: log.new
},
db: {
api: db,
level: level
},
inject: (code, file, options) => {
if (!script[code]) {
return logger.log(`inject missing target "${code}"`);
@ -239,11 +220,13 @@ const script = {
kiri : [
"kiri",
"ext/three",
"ext/three-bgu",
"license",
"ext/clip2", // work.test
"ext/tween",
"ext/fsave",
"ext/earcut", // work.test
"ext/base64",
"add/array",
"add/three",
"geo/base",
@ -267,11 +250,11 @@ const script = {
"kiri/ui",
"kiri/lang",
"kiri/lang-en",
"kiri/fill",
"kiri/db",
"kiri/slice",
"kiri/layers",
"kiri/client",
"mode/fdm/fill",
"mode/fdm/driver",
"mode/fdm/client",
"mode/sla/driver",
@ -297,6 +280,7 @@ const script = {
"kiri",
"ext/three",
"ext/pngjs",
"ext/jszip",
"license",
"ext/clip2",
"ext/earcut",
@ -315,11 +299,11 @@ const script = {
"geo/polygons",
"geo/gyroid",
"moto/pack",
"kiri/fill",
"kiri/slice",
"kiri/slicer",
"kiri/slicer2",
"kiri/layers",
"mode/fdm/fill",
"mode/fdm/driver",
"mode/fdm/slice",
"mode/fdm/prepare",
@ -404,40 +388,6 @@ const script = {
// prevent caching of specified modules
const cachever = {};
const db = {
// --------
key: arr => arr.join("/"),
// --------
get: key => {
if (Array.isArray(key)) key = db.key(key);
return promise((resolve,reject) => {
level.get(key,(err,record) => {
resolve(record,err);
});
});
},
// --------
put: (key, value) => {
if (Array.isArray(key)) key = db.key(key);
return promise((resolve,reject) => {
level.put(key,value,(err) => {
if (err) reject(err);
else resolve();
});
});
},
// --------
del: key => {
return promise((resolve,reject) => {
level.del(key, (err) => {
if (err) reject(err);
else resolve();
});
});
}
};
function promise(resolve, reject) {
return new Promise(resolve, reject);
}
@ -471,11 +421,12 @@ function handleSetup(req, res, next) {
}
function handleVersion(req, res, next) {
if (req.app.path === "/kiri/" && req.url.indexOf(version) < 0) {
let vstr = oversion || version;
if (req.app.path === "/kiri/" && req.url.indexOf(vstr) < 0) {
if (req.url.indexOf("?") > 0) {
return http.redirect(res, `${req.url},ver:${version}`);
return http.redirect(res, `${req.url},ver:${vstr}`);
} else {
return http.redirect(res, `${req.url}?ver:${version}`);
return http.redirect(res, `${req.url}?ver:${vstr}`);
}
} else {
next();
@ -496,118 +447,6 @@ function handleOptions(req, res, next) {
}
}
function handleData(req, res, next) {
addCorsHeaders(req, res);
res.setHeader('Cache-Control', 'private, no-cache, max-age=0');
let tok = req.app.path.split('/'),
muid = req.headers['x-moto-ajax'],
space = tok[2] || null,
version = tok[3],
valid = space && space.length >= 4 && space.length <= 8;
function genKey() {
while (true) {
let k = Math.round(Math.random() * 9999999999).toString(36);
if (k.length >= 4 && k.length <= 8) return k;
}
}
function countKey(space) {
return db.key(['meta/counter',space]);
}
function ownerKey(space) {
return db.key(['meta/owner',muid,'space',space]);
}
function recordKey(space, version) {
return db.key(["meta/space",space,version]);
}
// retrieve latest space data
if (valid && req.method === 'GET' && valid) {
function send(rec, version) {
if (rec) {
res.write(obj2string({space:space,ver:version,rec:rec}));
res.end();
} else {
res.end();
}
}
function retrieve(version) {
return db.get(recordKey(space,version))
.then(record => {
send(record || null, version);
})
}
if (version) {
retrieve(version)
} else {
db.get(countKey(space)).then(version => retrieve(version));
}
} else if (valid && req.method === 'POST') {
let dbOwner = null,
dbVersion = null,
postBody = null,
spacein = space,
version = 0,
body = '';
function checkDone() {
if (!(dbVersion && postBody)) return;
// if not owner, assign new space id
if (dbVersion > 1) {
if (!dbOwner) {
space = genKey();
version = 1;
logger.log({forked:space,from:spacein,by:muid});
}
}
// log what we have
logger.log({
space: space,
ver: dbVersion,
uid: muid,
size: postBody.length
});
if (muid && muid.length > 0) {
level.put(recordKey(space, dbVersion), body);
level.put(ownerKey(space), {time: time(), ver: dbVersion});
level.put(countKey(space), dbVersion);
}
res.end(obj2string({space: space, ver: dbVersion}));
}
// accumulate post body
req.on('data', data => { body += data });
req.on('end', () => {
postBody = body;
checkDone();
});
// fetch owner and version information
db.get(ownerKey(space))
.then(owner => {
dbOwner = owner;
return db.get(countKey(space));
})
.then(version => {
dbVersion = parseInt(version || "0") + 1;
checkDone();
});
return;
} else {
next();
}
}
function handleWasm(req, res, next) {
let [root, file] = req.app.path.split('/').slice(1);
let ext = (file || '').split('.')[1];
@ -775,10 +614,14 @@ function getCachedFile(file, fn) {
let smod = lastmod(filePath),
cmod = cached.mtime;
if (!smod) throw "missing source file";
if (smod > cmod) cached = null;
cached.lastcheck = now;
if (!smod) {
throw "missing source file";
}
if (smod > cmod) {
cached = null;
} else {
cached.lastcheck = now;
}
}
}

View file

@ -2,46 +2,49 @@
## `C` cosmetic, `F` functional, `P` performance, `B` bug fix
* `B` saved profile should be restored / defaulted when switching devices?
* `B` normals possibly inverted on faces in 2D to 3D conversion
* `B` normalize left, bottom, right pop menu time/click and value persistence
* `B` origin bug (Onshape?) when switching modes in 2.5 and 2.6
* `B` origin (and bed size) bug (Onshape?) when switching modes in 2.5 and 2.6
* `P` duplicate objects should share same slice data unless rotated or scaled
* `P` allow selection to me decimated on demand (context menu?)
* `P` improve decimation speed: omit point conversion or use wasm
* `P` client/worker interface normalization
* `P` explore widget vertex reloading / replacing
* `P` client/worker message interface normalization
* `P` explore widget vertex reloading / replacing (Onshape) (issue #48)
* `F` SVG import to 3D part like images
* `F` show slider range values in workspace units (on hover?)
* `F` allow select of a range by typing in values in slices or workspace units
* `F` add % field type with conversion (like units)
* `F` complete G2/G3 arc output / input (85% now)
* `F` expose grouping feature
* `F` add svgnest-like arrange algorithm
# FDM
* `B` large # linear points with linear infill (belt only?)
* `B` fix adaptive slicing with multi-extruder
* `B` fix supports with grouped parts
* `B` re-calibrate slicing progress weighting
* `B` infill densities are inaccurate
* `B` fix support projection/end with grouped parts
* `B` multi-extruder rendering of raft fails to offset the rest of the print
* `B` multi-extruder purge blocks fail to generate properly for rafts
* `F` slicing controls per user-defined z-region
* `F` support pillar should conform to part
* `F` support pillar should have solid top/bottom
* `F` more explicit line width control with ranges and min/max adaptive
* `F` test outlining solid projected areas (internally)
* `F` control for size of purge block (with 0=disabled)
* `F` add separate fill speed control
* `F` polishing and other non-planar work
* `F` gradient infill https://www.youtube.com/watch?v=hq53gsYREHU&feature=emb_logo
* `F` first layer segment large flat areas for better fill reliability
* `F` feather sharp tips by reducing extrusion in area of overlap
* `F` first layer segment large flat areas for better fill reliability
* `F` enable purge blocks when quick layers are detected
* `F` option to support interior bridges when 0% infill
* `F` trim support offset from layer below
* `F` calculate filament use per extruder per print
* `F` first layer support speed should be same as shell speed
* `F` apply finish speed to exposed top and underside flat areas
* `F` expand internal supporting flats / solids before projection
* `P` extruder + filament max flow rate cap in planner
* `P` revisit path routing / optimization
* `P` implement infill clipping in wasm
* `P` solid fill the tops of supports for down facing flats
* `P` sparse infill should follow polys between intersect points
# FDM - SLA
@ -51,27 +54,33 @@
# FDM - BELT
* `B` fix supports for belt printing
* `B` auto bed resizing leaves the origin in the wrong place at first
* `B` ranges don't work with anchors
* `B` multiple parts with one angled against origin "floats" in preview
* `B` re-add progress calls for all work units
* `F` slightly angle supports to lean into the Z of the part
* `F` arrange should just align down Z, not side to side.
# CAM
* `B` tabs are not cut to exact height
* `B` first rough step too far down in certain circumstances?
* `B` trace open polys are not wound consistently
* `B` path routing with tabs sometimes makes no sense
* `B` clicking on a pop-op should pin it until clicked or [esc]
* `B` need to force cut line at synthetic z bottom (midlines, etc)
* `B` non-clear-voids duplicates thru-holes
* `B` failure to go *up* when moving between parts?
* `B` starting export during animation unleashes chaos
* `B` climb vs conventional not fully consistent after refactor
* `B` outside cutting direction in roughing mode inverted
* `F` limit cut depth to flute length of selected tool
* `F` add ease-down support to trace op
* `F` add linear clearing strategy
* `F` add adaptive clearing strategy
* `F` user-selectable origin (issue #28)
* `F` intelligently turn circle hole pocket clear into spiral down
* `F` trace follow hole that matches endmill should turn into a drill op
* `F` add climb/conventional into each operation
* `F` update analyzer to detect overhangs
* `F` add linear clearing strategy option for roughing / pocket
* `F` adaptive clearing in roughing mode
* `F` extend acute roughing on inside polys to clear small voids
* `F` option to use part / STL coordinate space to determine X,Y origin
* `F` validate muti-part layout and spacing exceeds largest outside tool diameter
@ -88,14 +97,13 @@
* `F` can animation clear the mesh in areas where the cuts go through the stock?
* `F` support lathe mode / A-axis / rotary
* `F` gcode output option as zip for multiple or flip ops or tool change
* `F` ease-down should be engaged in all descending cutting operations
* `F` entirely user-selectable origin
* `F` maintain several part orientations + op chains in a single profile
* `P` common part pre-analyze to speed up 'slice' and improve shadow (overhangs)
* `P` redo all path route / planning in prepare to account for terrain before camOut
* `P` detect render message backlog and pause or warn
* `P` detect render message backlog and pause or warn?
* `P` allow faster z movements when contouring (not plunging)
* `P` common / faster shadow generator using vertices shared with ledges
# Laser
@ -108,6 +116,7 @@
* http://wiki.imal.org/howto/cnc-milling-introduction-cutting-tools
* http://www.twak.co.uk/2011/01/degeneracy-in-weighted-straight.html
* http://photon.chrisgraf.de/
* https://github.com/Jack000/SVGnest
# More CNC
@ -118,3 +127,7 @@
* https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip.html
* https://en.wikipedia.org/wiki/ZIP_(file_format)
* https://stuk.github.io/jszip/
# Meta
* free-space tetrahedron modeler with vertex mating

View file

@ -1,6 +1,6 @@
{
"name": "grid-apps",
"version": "2.6.8D",
"version": "2.8.1D",
"description": "grid.space 3d slice & modeling tools",
"author": "Stewart Allen <sa@grid.space>",
"license": "MIT",
@ -21,18 +21,17 @@
"slicer"
],
"dependencies": {
"base64-js": "^1.5.1",
"buffer-crc32": "^0.2.13",
"compression": "^1.7.4",
"connect": "^3.7.0",
"earcut": "^2.2.2",
"express-useragent": "^1.0.13",
"level": "^6.0.1",
"leveldown": "^5.6.0",
"levelup": "^4.4.0",
"jszip": "^3.5.0",
"moment": "^2.24.0",
"pixi.js": "^4.8.9",
"serve-static": "^1.14.1",
"three": "^0.122.0",
"three": "^0.126.0",
"tween.js": "^0.14.0",
"uglify-es": "3.3.9",
"validator": "^8.2.0",

View file

@ -1,6 +1,6 @@
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://paypal.me/gridspace3d?locale.x=en_US)
Contributions in all forms (code, bug reports, community engagenment, cash money, etc) are warmly welcomed. They all go to the bottom line of making better apps.
Contributions in all forms (code, bug reports, community engagement, localization, etc) are warmly welcomed.
[![Discord](https://img.shields.io/discord/688863523207774209)](https://discord.com/channels/688863523207774209/688863523211968535)
![GitHub](https://img.shields.io/github/license/GridSpace/grid-apps)
@ -19,6 +19,8 @@ Contributions in all forms (code, bug reports, community engagenment, cash money
![GitHub package.json version (branch)](https://img.shields.io/github/package-json/v/GridSpace/grid-apps/rel-2.2)
![GitHub package.json version (branch)](https://img.shields.io/github/package-json/v/GridSpace/grid-apps/rel-2.3)
![GitHub package.json version (branch)](https://img.shields.io/github/package-json/v/GridSpace/grid-apps/rel-2.4)
![GitHub package.json version (branch)](https://img.shields.io/github/package-json/v/GridSpace/grid-apps/rel-2.5)
![GitHub package.json version (branch)](https://img.shields.io/github/package-json/v/GridSpace/grid-apps/rel-2.6)
### Development Activity
@ -54,6 +56,21 @@ to start a local instance of the apps. then open
[Kiri:Moto](http://localhost:8080/kiri) or
[Meta:Moto](http://localhost:8080/meta) on your local host
Alternatively, if you are using a packaged version of npm that ships with
a Linux distribution, but still want to install in your home directory, you
can use
```
npm config set prefix ~/.local
```
If gs-app-server is not found, then perhaps ~/.local/bin is not in
your path. You can either add it to your path, or you can run:
```
~/local/bin/gs-app-server --debug
```
### Windows Developers
this git repo requires symbolic link support. on Windows, this means you have to clone the repo in a command shell with Administrator privileges.
@ -68,8 +85,9 @@ web site.
requires node.js 12+
### Javascript Slicing API
### Javascript Slicing APIs
A script include that injects a web worker into the page that will asynchronously perform any of Kiri’s slicing and gcode generation functions.
A script include that injects a web worker into the page that will asynchronously perform any of Kiri’s slicing and gcode generation functions. And a frame messaging API for controlling Kiri:Moto inside an IFrame.
https://grid.space/kiri/engine.html
* https://grid.space/kiri/engine.html
* https://grid.space/kiri/frame.html

View file

@ -162,6 +162,10 @@
})
};
AP.uniq = function() {
return this.slice().sort().filter((x, i, a) => !i || x != a[i-1]);
};
/** ******************************************************************
* String prototype helpers
******************************************************************* */

View file

@ -5,9 +5,18 @@
(function() {
var MP = THREE.Mesh.prototype,
GP = THREE.Geometry.prototype,
BP = THREE.BufferGeometry.prototype;
THREE.computeFaceNormal = function(vA,vB,vC) {
const ab = new THREE.Vector3();
const cb = new THREE.Vector3();
cb.subVectors( vC, vB );
ab.subVectors( vA, vB );
cb.cross( ab );
cb.normalize();
return cb;
};
MP.getBoundingBox = function(update) {
return this.geometry.getBoundingBox(update);
};
@ -46,7 +55,7 @@
};
// center geometry on x,y,z coordinates (defaults to 0,0,0)
GP.center = function(x,y,z) {
BP.center = function(x,y,z) {
var box = this.getBoundingBox(),
mid = box.dim.clone().multiplyScalar(0.5),
dif = mid.clone().add(box.min),
@ -69,14 +78,17 @@
};
// return cached or refreshed (when update = true) bounding box
GP.getBoundingBox = function(update) {
if (update || !this.boundingBox) this.computeBoundingBox();
BP.getBoundingBox = function(update) {
if (update || !this.boundingBox) {
this.boundingBox = null;
this.computeBoundingBox();
}
this.boundingBox.dim = this.boundingBox.max.clone().sub(this.boundingBox.min);
return this.boundingBox;
};
// uniformly scale any mesh to a max x/y/z dim of 'unit' (defaults to 1)
GP.unitScale = function(unit) {
BP.unitScale = function(unit) {
var bbox = this.getBoundingBox().clone(),
scale = (unit || 1) / Math.max(
bbox.max.x - bbox.min.x,
@ -89,20 +101,21 @@
return this;
};
BP.center = GP.center;
BP.getBoundingBox = GP.getBoundingBox;
BP.unitScale = GP.unitScale;
BP.fixNormals = function() {
this.computeVertexNormals();
return this;
};
THREE.Geometry.fromVertices = function(vertices) {
var geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
return geometry;
};
// may be faster than the equivalent matrix transform. to be tested
// BP.translate = function(x,y,z) {
// let off = [x,y,z],
// arr = this.attributes.position.array,
// len = arr.length;
// for (let i=0; i<len; i++) {
// arr[i] += off[i%3];
// }
// return this;
// }
THREE.Object3D.prototype.newGroup = function() {
var group = new THREE.Group();

View file

@ -8,7 +8,7 @@
"M03 S20000 ; spindle on"
],
"post":[
"M05 ; spindle off",
"M05 ; spindle off",
"M30 ; program end"
],
"tool-change":[

21
src/dev/cam/Roland.MDX-50 Normal file
View file

@ -0,0 +1,21 @@
{
"file-ext": "rml",
"token-space": "",
"strip-comments": true,
"pre":[
"PA;PA;VS4;!VZ4;!PZ0,80;!MC1;"
],
"post":[
"PA;PA;!PZ0,2420;PU0,6096;!MC0;"
],
"dwell":[
"W{time};"
],
"spindle": [
"!RC{speed};"
],
"settings": {
"bed_width": 400,
"bed_depth": 305
}
}

View file

@ -15,8 +15,7 @@
"G1 X200 E50 F800 ; extruder purge line",
"G1 Z0.3 ; shift belt away 0.3mm",
"G1 X0 E100 ; extruder purge line",
"G1 Z2 E98 ; shift belt away 2mm, retract extruder",
"G92 Z0 E-2 ; reset bed + extruder position",
"G92 Z0 E0 ; reset bed + extruder position",
"M117 CR-30 Printing..."
],
"post":[
@ -54,12 +53,12 @@
"origin_center": false,
"extrude_abs": true,
"bed_belt": true,
"bed_width": 215,
"bed_width": 220,
"bed_depth": 300,
"build_height": 170
},
"profiles":[{
"processName": "CR30",
"processName": "CR30 PLA",
"sliceHeight": 0.25,
"sliceShells": 3,
"sliceShellOrder": "in-out",
@ -68,43 +67,100 @@
"sliceFillOverlap": 0.3,
"sliceFillSparse": 0.2,
"sliceFillType": "hex",
"sliceAdaptive": false,
"sliceMinHeight": 0,
"sliceSupportDensity": 0.25,
"sliceSupportOffset": 1.0,
"sliceSupportGap": 0.75,
"sliceSupportSize": 5,
"sliceSupportArea": 0.1,
"sliceSupportOffset": 0.4,
"sliceSupportGap": 0,
"sliceSupportSize": 6,
"sliceSupportArea": 0.25,
"sliceSupportExtra": 0,
"sliceSupportAngle": 50,
"sliceSupportAngle": 1,
"sliceSupportNozzle": 0,
"sliceSolidMinArea": 1,
"sliceSolidLayers": 3,
"sliceBottomLayers": 3,
"sliceTopLayers": 3,
"firstLayerRate": 15,
"firstLayerPrintMult": 1.1,
"firstLayerRate": 10,
"firstLayerPrintMult": 1.10,
"firstLayerYOffset": 0,
"firstLayerBrim": 0,
"firstLayerBeltLead": 0,
"firstLayerFanSpeed": 0,
"outputTemp": 210,
"outputFanMax": 255,
"outputBedTemp": 70,
"outputFanSpeed": 255,
"outputFeedrate": 50,
"outputFinishrate": 50,
"outputFinishrate": 40,
"outputSeekrate": 80,
"outputShellMult": 1.25,
"outputFillMult": 1.25,
"outputSparseMult": 1.25,
"outputFanLayer": 1,
"outputRetractDist": 3,
"outputShellMult": 1.2,
"outputFillMult": 1.2,
"outputSparseMult": 1.2,
"outputFanLayer": 0,
"outputRetractDist": 5,
"outputRetractSpeed": 40,
"outputRetractDwell": 30,
"outputShortPoly": 100.0,
"outputRetractWipe": 1,
"outputRetractDwell": 20,
"outputShortPoly": 60.0,
"outputMinSpeed": 10.0,
"outputCoastDist": 0,
"outputWipeDistance": 0,
"outputCoastDist": 0.1,
"outputWipeDistance": 1,
"outputLayerRetract": false,
"detectThinWalls": true,
"sliceMinHeight": 0,
"zHopDistance": 0,
"antiBacklash": 0
},{
"processName": "CR30 PETG",
"sliceHeight": 0.25,
"sliceShells": 3,
"sliceShellOrder": "in-out",
"sliceLayerStart": "last",
"sliceFillAngle": 45,
"sliceFillOverlap": 0.3,
"sliceFillSparse": 0.2,
"sliceFillType": "hex",
"sliceAdaptive": false,
"sliceMinHeight": 0,
"sliceSupportDensity": 0.25,
"sliceSupportOffset": 0.4,
"sliceSupportGap": 0,
"sliceSupportSize": 6,
"sliceSupportArea": 0.25,
"sliceSupportExtra": 0,
"sliceSupportAngle": 1,
"sliceSupportNozzle": 0,
"sliceSolidMinArea": 1,
"sliceSolidLayers": 3,
"sliceBottomLayers": 3,
"sliceTopLayers": 3,
"firstLayerRate": 10,
"firstLayerPrintMult": 1.15,
"firstLayerYOffset": 0,
"firstLayerBrim": 0,
"firstLayerBeltLead": 0,
"firstLayerFanSpeed": 0,
"outputTemp": 240,
"outputFanMax": 0,
"outputBedTemp": 70,
"outputFanSpeed": 0,
"outputFeedrate": 40,
"outputFinishrate": 30,
"outputSeekrate": 80,
"outputShellMult": 1.2,
"outputFillMult": 1.2,
"outputSparseMult": 1.2,
"outputFanLayer": 0,
"outputRetractDist": 5.5,
"outputRetractSpeed": 30,
"outputRetractWipe": 1,
"outputRetractDwell": 20,
"outputShortPoly": 60.0,
"outputMinSpeed": 10.0,
"outputCoastDist": 0.1,
"outputWipeDistance": 1,
"outputLayerRetract": false,
"detectThinWalls": true,
"zHopDistance": 0,
"antiBacklash": 0
}]

View file

@ -39,5 +39,59 @@
"bed_width": 220,
"bed_depth": 220,
"build_height": 300
}
},
"profiles":[{
"processName": "Ender3 PLA",
"sliceHeight": 0.25,
"sliceShells": 3,
"sliceShellOrder": "in-out",
"sliceLayerStart": "last",
"sliceFillAngle": 45,
"sliceFillOverlap": 0.3,
"sliceFillSparse": 0.2,
"sliceFillType": "hex",
"sliceAdaptive": false,
"sliceMinHeight": 0,
"sliceSupportDensity": 0.25,
"sliceSupportOffset": 0.4,
"sliceSupportGap": 0,
"sliceSupportSize": 6,
"sliceSupportArea": 0.25,
"sliceSupportExtra": 0,
"sliceSupportAngle": 1,
"sliceSupportNozzle": 0,
"sliceSolidMinArea": 1,
"sliceSolidLayers": 3,
"sliceBottomLayers": 3,
"sliceTopLayers": 3,
"firstLayerRate": 10,
"firstLayerPrintMult": 1.10,
"firstLayerYOffset": 0,
"firstLayerBrim": 0,
"firstLayerBeltLead": 0,
"firstLayerFanSpeed": 0,
"outputTemp": 210,
"outputFanMax": 255,
"outputBedTemp": 70,
"outputFanSpeed": 255,
"outputFeedrate": 50,
"outputFinishrate": 40,
"outputSeekrate": 80,
"outputShellMult": 1.2,
"outputFillMult": 1.2,
"outputSparseMult": 1.2,
"outputFanLayer": 0,
"outputRetractDist": 5,
"outputRetractSpeed": 40,
"outputRetractWipe": 1,
"outputRetractDwell": 20,
"outputShortPoly": 60.0,
"outputMinSpeed": 10.0,
"outputCoastDist": 0.1,
"outputWipeDistance": 1,
"outputLayerRetract": false,
"detectThinWalls": true,
"zHopDistance": 0,
"antiBacklash": 0
}]
}

View file

@ -11,7 +11,6 @@
"G1 Z0.25 F180 ; move z to 0.25mm over bed",
"G92 E0 ; zero the extruded",
"M109 S{temp} T{tool} ; wait for extruder to reach target temp",
"G1 E15 F200 ; purge 15mm from extruder",
"G92 E0 ; zero the extruded",
"G1 F225 ; set feed speed"
],
@ -19,7 +18,6 @@
"M107 ; turn off filament cooling fan",
"M104 S0 T{tool} ; turn off right extruder",
"M140 S0 T{tool} ; turn off bed",
"G1 Z{z_max} F1200 ; drop bed",
"G28 X0 Y0 ; home XY axes",
"M84 ; disable stepper motors"
],

View file

@ -11,7 +11,6 @@
"G1 Z0.25 F180 ; move z to 0.25mm over bed",
"G92 E0 ; zero the extruded",
"M109 S{temp} T{tool} ; wait for extruder to reach target temp",
"G1 E15 F200 ; purge 15mm from extruder",
"G92 E0 ; zero the extruded",
"G1 F225 ; set feed speed"
],
@ -19,7 +18,6 @@
"M107 ; turn off filament cooling fan",
"M104 S0 T{tool} ; turn off right extruder",
"M140 S0 T{tool} ; turn off bed",
"G1 Z{z_max} F1200 ; drop bed",
"G28 X0 Y0 ; home XY axes",
"M84 ; disable stepper motors"
],

View file

@ -11,7 +11,6 @@
"G1 Z0.25 F180 ; move z to 0.25mm over bed",
"G92 E0 ; zero the extruded",
"M109 S{temp} T{tool} ; wait for extruder to reach target temp",
"G1 E15 F200 ; purge 15mm from extruder",
"G92 E0 ; zero the extruded",
"G1 F225 ; set feed speed"
],
@ -19,7 +18,6 @@
"M107 ; turn off filament cooling fan",
"M104 S0 T{tool} ; turn off right extruder",
"M140 S0 T{tool} ; turn off bed",
"G1 Z{z_max} F1200 ; drop bed",
"G28 X0 Y0 ; home XY axes",
"M84 ; disable stepper motors"
],

View file

@ -11,7 +11,6 @@
"G1 Z0.25 F180 ; move z to 0.25mm over bed",
"G92 E0 ; zero the extruded",
"M109 S{temp} T{tool} ; wait for extruder to reach target temp",
"G1 E15 F200 ; purge 15mm from extruder",
"G92 E0 ; zero the extruded",
"G1 F225 ; set feed speed"
],
@ -19,7 +18,6 @@
"M107 ; turn off filament cooling fan",
"M104 S0 T{tool} ; turn off right extruder",
"M140 S0 T{tool} ; turn off bed",
"G1 Z{z_max} F1200 ; drop bed",
"G28 X0 Y0 ; home XY axes",
"M84 ; disable stepper motors"
],

View file

@ -22,8 +22,6 @@
"cmd":{
"fan_power": "M106 S{fan_speed}"
},
"proc": "kiri_fdm_xyz_mini_w",
"file-ext": "3w",
"settings":{
"origin_center": false,
"extrude_abs": true,

1
src/ext/base64.js Symbolic link
View file

@ -0,0 +1 @@
../../node_modules/base64-js/base64js.min.js

1
src/ext/jszip.js Symbolic link
View file

@ -0,0 +1 @@
../../node_modules/jszip/dist/jszip.js

1
src/ext/three-bgu.js Symbolic link
View file

@ -0,0 +1 @@
../../node_modules/three/examples/js/utils/BufferGeometryUtils.js

View file

@ -15,7 +15,7 @@
function time() { return Date.now() }
function lerp(from, to, maxInc) {
function lerp(from, to, maxInc, incFrom) {
let dir = Math.sign(to - from);
let delta = Math.abs(to - from);
let steps = Math.floor(delta / maxInc);
@ -25,7 +25,7 @@
steps++;
per = delta / steps;
}
let out = [];
let out = incFrom ? [from] : [];
while (steps-- > 0) {
from += per * dir;
out.push(from);
@ -316,6 +316,94 @@
return (d2y * d1x) - (d2x * d1y);
}
/**
* return circle center given three points
* from https://math.stackexchange.com/questions/1076177/3d-coordinates-of-circle-center-given-three-point-on-the-circle
*/
function center3d(a,b,c) {
let ax = a.x,
ay = a.y,
az = a.z || 0,
bx = b.x,
by = b.y,
bz = b.z || 0,
cx = c.x,
cy = c.y,
cz = c.z || 0,
Cx = bx-ax,
Cy = by-ay,
Cz = bz-az,
Bx = cx-ax,
By = cy-ay,
Bz = cz-az,
B2 = (ax*ax)-(cx*cx)+(ay*ay)-(cy*cy)+(az*az)-(cz*cz),
C2 = (ax*ax)-(bx*bx)+(ay*ay)-(by*by)+(az*az)-(bz*bz),
CByz = Cy*Bz-Cz*By,
CBxz = Cx*Bz-Cz*Bx,
CBxy = Cx*By-Cy*Bx,
ZZ1 = -(Bz-Cz*Bx/Cx)/(By-Cy*Bx/Cx),
Z01 = -(B2-Bx/Cx*C2)/(2*(By-Cy*Bx/Cx)),
ZZ2 = -(ZZ1*Cy+Cz)/Cx,
Z02 = -(2*Z01*Cy+C2)/(2*Cx),
dz = -((Z02-ax)*CByz-(Z01-ay)*CBxz-az*CBxy)/(ZZ2*CByz-ZZ1*CBxz+CBxy),
dx = ZZ2*dz + Z02,
dy = ZZ1*dz + Z01;
return {x:dx, y:dy, z:dz};
}
/**
* return circle center given three points
* from https://stackoverflow.com/questions/4103405/what-is-the-algorithm-for-finding-the-center-of-a-circle-from-three-points
*/
function center2d(A,B,C,rad) {
// let yDelta_a = B.y - A.y,
// xDelta_a = B.x - A.x,
// yDelta_b = C.y - B.y,
// xDelta_b = C.x - B.x,
// aSlope = yDelta_a/xDelta_a,
// bSlope = yDelta_b/xDelta_b,
// center = {x:0, y:0, z:0};
// center.x = (aSlope*bSlope*(A.y - C.y) + bSlope*(A.x + B.x) - aSlope*(B.x+C.x) )/(2* (bSlope-aSlope) );
// center.y = -1*(center.x - (A.x+B.x)/2)/aSlope + (A.y+B.y)/2;
let center = center3d(A,B,C);
if (rad) {
let dx = center.x - A.x;
let dy = center.y - A.y;
center.r = Math.sqrt(dx*dx + dy*dy)
// if (isNaN(center.r)) console.log('NaN',{dx,dy,A,B,C,center,xDelta_a,xDelta_b,yDelta_a,yDelta_b,aSlope,bSlope});
}
return center;
}
/**
* return two possible circle centers given two points and a radius
* https://stackoverflow.com/questions/36211171/finding-center-of-a-circle-given-two-points-and-radius
*/
function center2pr(p1, p2, r) {
let x1 = p1.x,
x2 = p2.x,
y1 = p1.y,
y2 = p2.y,
q = Math.sqrt(Math.pow((x2-x1),2) + Math.pow((y2-y1),2)),
y3 = (y1+y2)/2,
x3 = (x1+x2)/2,
basex = Math.sqrt(Math.pow(r,2)-Math.pow((q/2),2))*(y1-y2)/q, //calculate once
basey = Math.sqrt(Math.pow(r,2)-Math.pow((q/2),2))*(x2-x1)/q, //calculate once
centerx1 = x3 + basex, //center x of circle 1
centery1 = y3 + basey, //center y of circle 1
centerx2 = x3 - basex, //center x of circle 2
centery2 = y3 - basey; //center y of circle 2
return [{x:centerx1, y:centery1}, {x:centerx2, y:centery2}];
}
function thetaDiff(n1, n2, sign) {
let diff = n2 - n1;
while (diff < -Math.PI) diff += Math.PI * 2;
while (diff > Math.PI) diff -= Math.PI * 2;
return sign ? diff : Math.abs(diff);
}
/** ******************************************************************
* Connect to base
******************************************************************* */
@ -399,6 +487,10 @@
time,
round,
area2,
center2d,
center3d,
center2pr,
thetaDiff,
distSq : dist2,
distSqv2 : dist2v2,
inRange,

View file

@ -260,6 +260,198 @@
return Math.abs(diff);
}
// generate center crossing point cloud
PRO.centers = function(step, z, min, max, opt = {}) {
let cloud = [],
bounds = this.bounds,
lines = opt.lines || false,
stepoff = step / 2,
set = [this.points];
if (this.inner) {
for (let inner of this.inner) {
set.push(inner.points);
}
}
for (let y of UTIL.lerp(bounds.miny + stepoff, bounds.maxy - stepoff, step, true)) {
let ints = [];
for (let points of set) {
let length = points.length;
for (let i=0; i<length; i++) {
let p1 = points[i % length];
let p2 = points[(i+1) % length];
if (
(p1.y <= y && p2.y > y) ||
(p1.y > y && p2.y <= y)
) ints.push([p1,p2]);
}
}
let cntr = [];
if (ints.length && ints.length % 2 === 0) {
for (let int of ints) {
let [p1, p2] = int;
if (p2.y < p1.y) {
let tp = p1;
p1 = p2;
p2 = tp;
}
let minx = Math.min(p1.x, p2.x);
let maxx = Math.max(p1.x, p2.x);
let miny = Math.min(p1.y, p2.y);
let maxy = Math.max(p1.y, p2.y);
let dx = p2.x - p1.x;
let dy = maxy - miny;
let pct = (y - miny) / dy;
let xpo = p1.x + pct * dx;
cntr.push(xpo);
}
}
cntr.sort((a, b) => {
return b - a;
});
let lp, eo = 0;
for (let x of cntr) {
let p = BASE.newPoint(x, y, z);
if (eo++ % 2) {
let d = lp.distTo2D(p);
if (d >= min && d <= max) {
if (lines) {
cloud.push(lp);
cloud.push(p);
} else {
cloud.push(BASE.newPoint(
(lp.x + p.x) / 2, y, z
));
}
}
} else {
lp = p;
}
}
}
for (let x of UTIL.lerp(bounds.minx + stepoff, bounds.maxx - stepoff, step, true)) {
let ints = [];
for (let points of set) {
let length = points.length;
for (let i=0; i<length; i++) {
let p1 = points[i % length];
let p2 = points[(i+1) % length];
if (
(p1.x <= x && p2.x > x) ||
(p1.x > x && p2.x <= x)
) ints.push([p1,p2]);
}
}
let cntr = [];
if (ints.length && ints.length % 2 === 0) {
for (let int of ints) {
let [p1, p2] = int;
if (p2.x < p1.x) {
let tp = p1;
p1 = p2;
p2 = tp;
}
let minx = Math.min(p1.x, p2.x);
let maxx = Math.max(p1.x, p2.x);
let miny = Math.min(p1.y, p2.y);
let maxy = Math.max(p1.y, p2.y);
let dx = maxx - minx;
let dy = p2.y - p1.y;
let pct = (x - minx) / dx;
let ypo = p1.y + pct * dy;
cntr.push(ypo);
}
}
cntr.sort((a, b) => {
return b - a;
});
let lp, eo = 0;
for (let y of cntr) {
let p = BASE.newPoint(x, y, z);
if (eo++ % 2) {
let d = lp.distTo2D(p);
if (d >= min && d <= max) {
if (lines) {
cloud.push(lp);
cloud.push(p);
} else {
cloud.push(BASE.newPoint(
x, (lp.y + p.y) / 2, z
));
}
}
} else {
lp = p;
}
}
}
if (lines) {
return cloud;
}
let mindist = opt.mindist || step * 1.5;
function build(poly) {
let lastp = poly.last();
let minp;
let mind = Infinity;
for (let point of cloud) {
let dist = point.distTo2D(lastp);
if (dist < mindist && dist < mind) {
mind = dist;
minp = point;
}
}
if (minp) {
cloud = cloud.filter(p => p !== minp);
poly.push(minp);
return true;
}
return false;
}
// join points into polys
let polys = [];
let poly = [];
while (cloud.length) {
if (poly.length === 0) {
poly = [ cloud.shift() ];
polys.push(poly);
continue;
}
if (build(poly)) {
continue;
}
if (!poly.flip) {
poly.reverse();
poly.flip = true;
continue;
}
if (poly.length) {
poly = [];
} else {
throw "whoop there it is";
}
}
return polys
.filter(poly => poly.length > 1)
.map(poly => {
let np = BASE.newPolygon().setOpen();
for (let p of poly) {
np.push(p);
}
if (np.last().distTo2D(np.first()) <= max) {
np.setClosed();
}
np = np.clean();
return np;
});
};
PRO.debur = function(dist) {
if (this.len < 2) {
return null;
@ -666,6 +858,16 @@
return this;
};
PRO.addObj = function(obj) {
if (Array.isArray(obj)) {
for (let o of obj) {
this.addObj(o);
}
return this;
}
return this.add(obj.x, obj.y, obj.z);
}
/**
* append array of points to polygon and return polygon
*
@ -1201,9 +1403,11 @@
return this;
};
function fromClipperPath(path,z) {
function fromClipperPath(path, z) {
let poly = newPolygon(), i = 0, l = path.length;
while (i < l) poly.push(newPoint(null,null,z,null,path[i++]));
while (i < l) {
poly.push(newPoint(null,null,z,null,path[i++]));
}
return poly;
};
@ -1223,6 +1427,31 @@
poly.parent = parent || this.parent;
poly.area2 = this.area2;
poly.open = this.open;
if (this.open) {
// when open, ensure first point on new poly matches old
let start = this.points[0];
let points = poly.points;
let length = points.length;
let mi, min = Infinity;
for (let i=0; i<length; i++) {
let d = points[i].distTo2D(start);
if (d < min) {
min = d;
mi = i;
}
}
// mi > 0 means first point didn't match
if (mi) {
let nupoints = [];
for (let i=mi; i<length; i++) {
nupoints.push(points[i]);
}
for (let i=0; i<mi; i++) {
nupoints.push(points[i]);
}
poly.points = nupoints;
}
}
return poly;
};

View file

@ -30,6 +30,7 @@
expand,
expand_lines,
union,
inset,
nest,
diff,
setZ,
@ -540,6 +541,43 @@
return opts.flat ? opts.outs : polys;
}
/**
* progressive insetting that does inset + outset to debur as well
* as performing subtractive analysis between initial layer shell (ref)
* and last offset (cmp) to produce gap candidates (for thinfill)
*/
function inset(polys, dist, count, z) {
let total = count;
let layers = [];
let ref = polys;
let depth = 0;
while (count-- > 0 && ref && ref.length) {
let off = offset(ref, -dist, {z});
let mid = offset(off, dist / 2, {z});
let cmp = offset(off, dist, {z});
let gap = [];
let aref = ref.map(p => p.areaDeep()).reduce((a,p) => a +p);
let cref = cmp.length ? cmp.map(p => p.areaDeep()).reduce((a,p) => a + p) : 0;
// threshold subtraction to area deltas > 0.1 % to filter out false
// positives where inset/outset are identical floating point error
if (Math.abs(aref - cref) > 1 - (Math.abs(aref / cref) / 1000)) {
subtract(ref, cmp, gap, null, z);
}
layers.push({idx: total-count, off, mid, gap});
// fixup depth cues
for (let m of mid) {
m.depth = depth++;
if (m.inner) {
for (let mi of m.inner) {
mi.depth = m.depth;
}
}
}
ref = off;
}
return layers;
}
/**
* todo use clipper opne poly clipping?
*

View file

@ -30,9 +30,10 @@
*/
function wireframe(group, points, color) {
if (points.length % 3 != 0) throw "invalid line : "+points.length;
let lines = new THREE.Geometry(),
let lines = new THREE.BufferGeometry(),
hash = {},
added = 0;
added = 0,
vertices = [];
for (let i = 0; i < points.length; i += 3) {
let p1 = points[i],
p2 = points[i + 1],
@ -41,22 +42,28 @@
l2 = newOrderedLine(p2, p3),
l3 = newOrderedLine(p3, p1);
if (!hash[l1.key]) {
lines.vertices.push(new THREE.Vector3(p1.x, p1.y, p1.z));
lines.vertices.push(new THREE.Vector3(p2.x, p2.y, p2.z));
vertices.appendAll([
p1.x, p1.y, p1.z,
p2.x, p2.y, p2.z
]);
hash[l1.key] = ++added;
}
if (!hash[l2.key]) {
lines.vertices.push(new THREE.Vector3(p2.x, p2.y, p2.z));
lines.vertices.push(new THREE.Vector3(p3.x, p3.y, p3.z));
vertices.appendAll([
p2.x, p2.y, p2.z,
p3.x, p3.y, p3.z
]);
hash[l2.key] = ++added;
}
if (!hash[l3.key]) {
lines.vertices.push(new THREE.Vector3(p3.x, p3.y, p3.z));
lines.vertices.push(new THREE.Vector3(p1.x, p1.y, p1.z));
vertices.appendAll([
p3.x, p3.y, p3.z,
p1.x, p1.y, p1.z
]);
hash[l3.key] = ++added;
}
}
lines.verticesNeedUpdate = true;
lines.setAttribute('position', new THREE.BufferAttribute( vertices.toFloat32(), 3 ) );
let mesh = new THREE.LineSegments(lines, getMaterial(color));
group.add(mesh);
return mesh;

View file

@ -6,7 +6,7 @@
if (!self.kiri) {
self.kiri = {
beta: 2608,
beta: 2801,
driver: {}, // driver modules
loader: [] // module loading: array of functions
};

View file

@ -14,8 +14,10 @@ let KIRI = self.kiri = self.kiri || {},
seqid = 1,
syncd = {},
running = {},
slicing = {},
worker = null;
worker = null,
restarting = false
// occ = new Worker("/kiri/ext/occ-worker.js", {type:"module"}),
;
/**
* @param {Function} fn name of function in KIRI.worker
@ -26,8 +28,11 @@ let KIRI = self.kiri = self.kiri || {},
function send(fn, data, onreply, zerocopy) {
let seq = seqid++;
running[seq] = { fn:onreply };
// console.log('send', data);
if (onreply) {
// track it only when we expect and can handle a reply
running[seq] = { fn:onreply };
}
// console.log('send', {fn, data, onreply});
try {
worker.postMessage({
@ -55,25 +60,35 @@ KIRI.work = {
}
},
isSlicing: function() {
isBusy: function() {
let current = 0;
for (let key in slicing) {
current++;
for (let rec of Object.values(running)) {
if (rec.fn) current++;
}
return current > 0;
},
restart: function() {
// prevent re-entry from cancel callback
if (restarting) {
return;
}
if (worker) {
worker.terminate();
}
for (let key in slicing) {
slicing[key]({error: "cancelled slicing"});
restarting = true;
for (let key in running) {
let rec = running[key];
if (rec.fn) {
rec.fn({error: "cancelled operation"});
}
}
syncd = {};
slicing = {};
running = {};
worker = KIRI.work.newWorker();
@ -81,7 +96,7 @@ KIRI.work = {
let now = time(),
reply = e.data,
record = running[reply.seq],
onreply = record.fn;
onreply = record ? record.fn : undefined;
// console.log('recv', reply.data)
if (reply.done) {
@ -91,8 +106,14 @@ KIRI.work = {
// calculate and replace recv time
reply.time_recv = now - reply.time_recv;
onreply(reply.data, reply);
if (onreply) {
onreply(reply.data, reply);
} else {
console.log({unexpected_reply: reply});
}
};
restarting = false;
},
decimate: function(vertices, options, callback) {
@ -105,35 +126,36 @@ KIRI.work = {
},
config: function(obj) {
send("config", obj, function(reply) {
// console.log({config:reply});
});
send("config", obj, function(reply) { });
},
clear: function(widget) {
send("clear", widget ? {id:widget.id} : {}, function(reply) {
syncd = {};
});
clear: function() {
syncd = {};
send("clear", {}, function(reply) { });
},
snap: function(data) {
send("snap", data, function(reply) {
// console.log({snap:reply})
});
send("snap", data, function(reply) { });
},
// widget sync
sync: function(widget) {
let widgets = widget ? [ widget ] : KIRI.api.widgets.all();
sync: function(widgets) {
if (!widgets) {
widgets = KIRI.api.widgets.all();
}
// send list of currently valid widgets
send("sync", { valid: widgets.map(w => w.id) }, () => {});
// sync any widget that has changed
widgets.forEach(widget => {
if (widget.modified || !syncd[widget.id]) {
syncd[widget.id] = true;
let vertices = widget.getGeoVertices().buffer.slice(0);
send("sync", {
id: widget.id,
group: widget.group.id,
track: widget.track,
vertices: vertices,
position: widget.mesh.position,
tracking: widget.track
}, done => {
widget.modified = false;
}, [vertices]);
@ -141,18 +163,42 @@ KIRI.work = {
});
},
slice: function(settings, widget, callback) {
CLIENT.sync(widget);
slicing[widget.id] = callback;
rotate: function(settings, callback) {
send("rotate", { settings }, reply => {
if (reply.group) {
for (let widget of KIRI.Widget.Groups.forid(reply.group)) {
widget.belt = reply.belt;
}
} else if (callback) {
callback();
}
});
},
unrotate: function(settings, callback) {
send("unrotate", { settings }, reply => {
if (reply.group) {
for (let widget of KIRI.Widget.Groups.forid(reply.group)) {
widget.rotinfo = reply.rotinfo;
}
} else if (callback) {
callback();
}
});
},
slice: function(settings, widget, callback) {
send("slice", {
id: widget.id,
settings: settings
}, function(reply) {
if (reply.done || reply.error) {
delete slicing[widget.id];
}
callback(reply);
if (reply.done || reply.error) {
// in belt mode, slicing modifies a widget and requires re-sync
if (settings.device.bedBelt) {
widget.modified = true;
}
}
});
},
@ -164,6 +210,9 @@ KIRI.work = {
if (reply.done) {
done(reply.output, reply.maxSpeed);
}
if (reply.error) {
done(reply);
}
});
},
@ -175,6 +224,9 @@ KIRI.work = {
if (reply.done) {
ondone(reply.output);
}
if (reply.error) {
ondone(null, reply.error);
}
});
},
@ -204,7 +256,7 @@ KIRI.work = {
}
send("parse", args, function(reply) {
if (reply.progress) {
progress(reply);
progress(reply.progress);
}
if (reply.parsed) {
done(KIRI.codec.decode(reply.parsed), reply.maxSpeed);
@ -221,6 +273,16 @@ KIRI.work = {
output(reply.done);
}
}, [ data.png ]);
},
zip: function(files, progress, output) {
send("zip", {files}, reply => {
if (reply.percent !== undefined) {
progress(reply);
} else {
output(reply);
}
});
}
};

View file

@ -11,6 +11,7 @@
const zeroOut = true;
const codec = KIRI.codec = {
undef: undefined,
encode: encode,
decode: decode,
registerDecoder: registerDecoder,
@ -154,7 +155,7 @@
polys: encode(layer.polys, state),
lines: encodePointArray(layer.lines, state),
faces: codec.allocFloat32Array(layer.faces),
cface: layer.cface,
cface: layer.cface || codec.undef,
color: layer.color,
paths: layer.paths.map(lp => {
const pe = {
@ -167,13 +168,13 @@
}
return pe;
}),
cpath: layer.cpath,
cpath: layer.cpath || codec.undef,
off: layer.off
};
if (zeroOut && state.zeros && e.faces.length) {
state.zeros.push(e.faces.buffer);
}
// console.log('-->',e);
// console.log('-->',layer,e);
return e;
})
};

View file

@ -77,6 +77,7 @@
extFilament: device.filamentSize,
extNozzle: device.nozzleSize,
extSelect: ["T0"],
extDeselect: [],
extOffsetX: 0,
extOffsetY: 0
}];
@ -143,6 +144,7 @@
if (rec.offset_x) e.extOffsetX = rec.offset_x;
if (rec.offset_y) e.extOffsetY = rec.offset_y;
if (rec.select) e.extSelect = rec.select;
if (rec.deselect) e.extDeselect = rec.deselect;
device.extruders.push(e);
});
} else {
@ -259,6 +261,7 @@
bedHeight: 2.5,
bedRound: false,
bedBelt: false,
fwRetract: false,
originCenter: false,
maxHeight: 150,
gcodePre: [],
@ -273,6 +276,7 @@
extFilament: 1.75,
extNozzle: 0.4,
extSelect: ["T0"],
extDeselect: [],
extOffsetX: 0,
extOffsetY: 0
}]
@ -285,12 +289,14 @@
sliceShellOrder: "in-out",
sliceLayerStart: "last",
sliceFillAngle: 45,
sliceFillWidth: 1,
sliceFillOverlap: 0.3,
sliceFillSparse: 0.5,
sliceFillRate: 0,
sliceFillType: "hex",
sliceSupportDensity: 0.25,
sliceSupportOffset: 1.0,
sliceSupportGap: 0.75,
sliceSupportGap: 1,
sliceSupportSize: 5,
sliceSupportArea: 0.1,
sliceSupportExtra: 0,
@ -311,10 +317,11 @@
firstLayerBedTemp: 0,
firstLayerBrim: 0,
firstLayerBrimTrig: 0,
firstLayerBeltLead: 3,
firstLayerFanSpeed: 0,
outputRaft: false,
outputRaftSpacing: 0.2,
outputTemp: 200,
outputFanMax: 255,
outputBedTemp: 60,
outputFeedrate: 50,
outputFinishrate: 50,
@ -322,27 +329,30 @@
outputShellMult: 1.25,
outputFillMult: 1.25,
outputSparseMult: 1.25,
outputFanLayer: 1,
outputFanSpeed: 255,
outputRetractDist: 1.5,
outputRetractSpeed: 40,
outputRetractWipe: 0,
outputRetractDwell: 20,
outputBrimCount: 2,
outputBrimOffset: 2,
outputShortPoly: 100.0,
outputMinSpeed: 10.0,
outputCoastDist: 0,
outputWipeDistance: 0,
outputLayerRetract: false,
outputOriginCenter: true,
outputLoopLayers: "",
outputPeelGuard: 0,
outputInvertX: false,
outputInvertY: false,
detectThinWalls: false,
sliceMinHeight: 0,
sliceAdaptive: false,
zHopDistance: 0.2,
arcTolerance: 0,
antiBacklash: 1,
gcodePause: ""
gcodePause: "",
ranges: []
}
},
sla:{
@ -458,6 +468,7 @@
camTraceDown: 0,
camTraceSpeed: 250,
camTracePlunge: 200,
camTraceLines: false,
camDrillTool: 1000,
camDrillSpindle: 1000,
camDrillDownSpeed: 250,
@ -630,6 +641,7 @@
view: null,
dark: false,
decals: true,
danger: false,
zoomSpeed: 1.0,
lineType: "path",
autoSave: true,
@ -639,6 +651,7 @@
showSpeeds: true,
freeLayout: true,
autoLayout: true,
spaceLayout: 1,
units: "mm",
exportOcto: false,
exportGhost: false,
@ -646,7 +659,16 @@
exportPreview: false,
decimate: true,
detail: "good",
animesh: "200"
animesh: "200",
healMesh: false,
ortho: false
},
// label state preferences
labels: {
'FDM-3-engage': false,
'FDM-3-retract': false,
'FDM-3-arrows': false,
'FDM-3-move': false
},
// for passing temporary slice hints (topo currently)
synth: {},

View file

@ -7,6 +7,14 @@
let KIRI = self.kiri = self.kiri || {};
KIRI.newEngine = ()=> { return new Engine ()};
if (!KIRI.api) {
KIRI.api = {
event: {
emit: () => {}
}
};
}
class Engine {
constructor() {
this.widget = KIRI.newWidget();
@ -27,7 +35,7 @@ class Engine {
try {
new moto.STL().load(url, vertices => {
this.listener({loaded: url, vertices});
this.widget.loadVertices(vertices)
this.widget.loadVertices(vertices).center();
accept(this);
});
} catch (error) {
@ -39,11 +47,10 @@ class Engine {
parse(data) {
return new Promise((accept, reject) => {
try {
new moto.STL().parse(data, vertices => {
this.listener({parsed: data, vertices});
this.widget.loadVertices(vertices)
accept(this);
});
let vertices = new moto.STL().parse(data);
this.listener({parsed: data, vertices});
this.widget.loadVertices(vertices).center();
accept(this);
} catch (error) {
reject(error);
}
@ -97,6 +104,8 @@ class Engine {
slice() {
return new Promise((accept, reject) => {
KIRI.client.clear();
KIRI.client.sync([ this.widget ]);
KIRI.client.slice(this.settings, this.widget, msg => {
this.listener({slice:msg});
if (msg.error) {

View file

@ -31,11 +31,15 @@
}
function callExport(callback, mode) {
let alert = API.feature.work_alerts ? API.show.alert("Exporting") : null;
const gcode = [];
KIRI.client.export(API.conf.get(), (line) => {
gcode.push(line);
}, (output) => {
if (callback) {
}, (output, error) => {
API.hide.alert(alert);
if (error) {
API.show.alert(error, 5);
} else if (callback) {
callback(gcode.join('\r\n'), output);
} else {
exportGCodeDialog(gcode.join('\r\n'), output);
@ -46,8 +50,12 @@
function callExportLaser(options) {
KIRI.client.export(API.conf.get(), (line) => {
console.log({unexpected_line: line});
}, (output) => {
exportLaserDialog(output);
}, (output, error) => {
if (error) {
API.show.alert(error, 5);
} else {
exportLaserDialog(output);
}
});
}
@ -58,9 +66,13 @@
if (line.data) {
preview.push(line.data);
}
}, (output) => {
}, (output, error) => {
API.show.progress(0);
KIRI.driver.SLA.printDownload(preview, output, API);
if (error) {
API.show.alert(error, 5);
} else {
KIRI.driver.SLA.printDownload(preview, output, API);
}
});
}
@ -112,7 +124,14 @@
let settings = API.conf.get(),
MODE = API.mode.get_id(),
pre = (MODE === MODES.CAM ? "cnc-" : "print-") + (printSeq.toString().padStart(3,"0")),
names = API.widgets.all()
.map(w => w.meta ? w.meta.file : undefined)
.filter(v => v)
.map(v => v.replace(/ /g,'-'))
.map(v => v.substring(0,20))
.map(v => v.split('.')[0]),
name = names[0] || (MODE === MODES.CAM ? "cnc" : "print"),
pre = `${name}-${(printSeq.toString().padStart(3,"0"))}`,
filename = pre,// + (new Date().getTime().toString(36)),
fileext = settings.device.gcodeFExt || "gcode",
codeproc = settings.device.gcodeProc,
@ -489,8 +508,8 @@
$('admin-gridlocal').onclick = admin_gridlocal;
} catch (e) { console.log(e) }
// preview of the generated GCODE
if (preview && gcode) $('code-preview-textarea').value = gcode;
// preview of the generated GCODE (first 64k max)
if (preview && gcode) $('code-preview-textarea').value = gcode.substring(0,65535);
// show dialog
API.modal.show('print');

View file

@ -46,7 +46,6 @@
platform = API.platform,
selection = API.selection,
proto = location.protocol,
root = "grid.space",
ver = Date.now().toString(36);
let currentDevice = null,
@ -58,7 +57,8 @@
editTools = null,
maxTool = 0,
fpsTimer = null,
platformColor;
platformColor,
contextInt;
// extend KIRI API with local functions
API.show.devices = showDevices;
@ -138,7 +138,9 @@
function booleanSave() {
let control = settings().controller;
let isDark = control.dark;
let doAlert = UI.ortho.checked !== control.ortho;
control.decals = UI.decals.checked;
control.danger = UI.danger.checked;
control.showOrigin = UI.showOrigin.checked;
control.showRulers = UI.showRulers.checked;
control.autoLayout = UI.autoLayout.checked;
@ -151,8 +153,10 @@
control.exportLocal = UI.exportLocal.checked;
control.exportPreview = UI.exportPreview.checked;
control.decimate = UI.decimate.checked;
control.healMesh = UI.healMesh.checked;
control.ortho = UI.ortho.checked;
SPACE.view.setZoom(control.reverseZoom, control.zoomSpeed);
platform.layout();
// platform.layout();
API.conf.save();
API.platform.update_size();
API.catalog.setOptions({
@ -166,6 +170,9 @@
clearDeviceTexture();
}
API.event.emit('boolean.update');
if (doAlert) {
API.show.alert("change requires page refresh");
}
}
function updateFPS() {
@ -191,7 +198,7 @@
// }
API.conf.update();
DOC.activeElement.blur();
// API.event.emit("boolean.click");
API.event.emit("boolean.click");
}
function onButtonClick(ev) {
@ -240,7 +247,7 @@
// hide all dialogs
API.dialog.hide();
// cancel slicing
if (KIRI.work.isSlicing()) KIRI.work.restart();
API.function.cancel();
// kill any poppers in compact mode
UC.hidePoppers();
// and send an event (used by FDM client)
@ -321,16 +328,19 @@
}
function keyHandler(evt) {
let handled = true,
current = settings(),
style, sel, i, m, bb,
ncc = evt.charCode - 48;
let handled = true;
if (API.modal.visible() || inputHasFocus()) {
return false;
}
if (API.feature.on_key) {
if (API.feature.on_key({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;
@ -357,14 +367,6 @@
case cca('i'): // file import
API.event.import();
break;
case cca('U'): // full settings url
storeSettingsToServer(true);
break;
case cca('u'): // full settings url
UC.prompt("settings id to load", "").then(id => {
if (id) loadSettingsFromServer(id);
});
break;
case cca('S'): // slice
case cca('s'): // slice
if (evt.shiftKey) {
@ -414,6 +416,9 @@
API.show.local();
break;
case cca('v'): // toggle single slice view mode
if (API.view.get() === VIEWS.ARRANGE) {
API.space.set_focus(API.selection.widgets());
}
if (API.var.layer_hi == API.var.layer_lo) {
API.var.layer_lo = 0;
} else {
@ -422,52 +427,75 @@
API.show.slices();
break;
case cca('d'): // duplicate object
sel = API.selection.meshes();
platform.deselect();
for (i=0; i<sel.length; i++) {
let mesh = sel[i].clone();
mesh.geometry = mesh.geometry.clone();
mesh.material = mesh.material.clone();
bb = mesh.getBoundingBox();
let ow = sel[i].widget;
let nw = API.widgets.new().loadGeometry(mesh.geometry);
nw.meta.file = ow.meta.file;
nw.meta.vertices = ow.meta.vertices;
nw.move(bb.max.x - bb.min.x + 1, 0, 0);
platform.add(nw,true);
let owa = API.widgets.annotate(ow.id);
let nwa = API.widgets.annotate(nw.id);
if (owa.tab) {
nwa.tab = Object.clone(owa.tab);
nwa.tab.forEach((tab,i) => {
tab.id = Date.now() + i
});
}
KIRI.driver.CAM.restoreTabs([nw]);
}
duplicateSelection();
break;
case cca('m'): // mirror object
API.selection.for_widgets(function(widget) {
widget.mirror();
});
SPACE.update();
mirrorSelection();
break;
case cca('R'): // toggle slice render mode
renderMode++;
API.function.slice();
break;
case cca('a'): // auto arrange items on platform
platform.layout();
case cca('a'):
if (API.view.get() === VIEWS.ARRANGE) {
// auto arrange items on platform
platform.layout();
API.space.set_focus(API.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();
if (handled) {
evt.preventDefault();
evt.stopPropagation();
}
return false;
}
function layFlat() {
let int = contextInt[0];
if (int && int.object && int.object.widget) {
let q = new THREE.Quaternion();
q.setFromUnitVectors(contextInt[0].face.normal, new THREE.Vector3(0,0,-1));
API.selection.rotate(q);
}
}
function duplicateSelection() {
API.selection.for_widgets(function(widget) {
let mesh = widget.mesh;
let bb = mesh.getBoundingBox();
let ow = widget;
let nw = API.widgets.new().loadGeometry(mesh.geometry.clone());
nw.meta.file = ow.meta.file;
nw.meta.vertices = ow.meta.vertices;
nw.move(bb.max.x - bb.min.x + 1, 0, 0);
platform.add(nw,true);
let owa = API.widgets.annotate(ow.id);
let nwa = API.widgets.annotate(nw.id);
if (owa.tab) {
nwa.tab = Object.clone(owa.tab);
nwa.tab.forEach((tab,i) => {
tab.id = Date.now() + i
});
}
KIRI.driver.CAM.restoreTabs([nw]);
});
}
function mirrorSelection() {
API.selection.for_widgets(function(widget) {
widget.mirror();
});
SPACE.update();
}
function keys(o) {
let key, list = [];
for (key in o) { if (o.hasOwnProperty(key)) list.push(key) }
@ -515,43 +543,6 @@
moveSelection(x, y, z, true);
}
function loadSettingsFromServer(tok) {
let hash = (tok || LOC.hash.substring(1)).split("/");
if (hash.length === 2) {
new moto.Ajax(function(reply) {
if (reply) {
let res = JSON.parse(reply);
if (res && res.ver && res.rec) {
let set = JSON.parse(atob(res.rec));
set.id = res.space;
set.ver = res.ver;
API.conf.put(set);
API.event.settings();
API.ui.sync();
LOC.hash = '';
}
}
}).request("/data/"+ hash[0] + "/" + hash[1]);
}
}
function storeSettingsToServer(display) {
let set = btoa(JSON.stringify(settings()));
new moto.Ajax(function(reply) {
if (reply) {
let res = JSON.parse(reply);
if (res && res.ver) {
LOC.hash = res.space + "/" + res.ver;
if (display) {
UC.alert(`unique settings id is: <b>${res.space}/${res.ver}</b>`);
}
}
} else {
updateSpaceState();
}
}).request("/data/"+ settings().id + "/" + settings().ver, set);
}
function deviceExport(exp, name) {
name = (name || "device")
.toLowerCase()
@ -559,10 +550,7 @@
.replace(/\./g,'_');
UC.prompt("Export Device Filename", name).then(name => {
if (name) {
let blob = new Blob([exp], {type: "octet/stream"}),
url = WIN.URL.createObjectURL(blob);
$('mod-any').innerHTML = `<a id="sexport" href="${url}" download="${name}.km">x</a>`;
$('sexport').click();
API.util.download(exp, `${name}.km`);
}
});
}
@ -574,13 +562,7 @@
if (name.toLowerCase().indexOf(".stl") < 0) {
name = `${name}.stl`;
}
let xprt = API.selection.export(),
blob = new Blob([xprt], {type: "octet/stream"}),
url = WIN.URL.createObjectURL(blob);
$('mod-any').innerHTML = [
`<a id="sexport" href="${url}" download="${name}">x</a>`
].join('');
$('sexport').click();
API.util.download(API.selection.export(), name);
});
}
@ -601,13 +583,8 @@
UC.confirm("Export Filename", {ok:true, cancel: false}, "workspace", opt).then(name => {
if (name) {
let work = $('incwork').checked,
json = API.conf.export({work}),
blob = new Blob([json], {type: "octet/stream"}),
url = WIN.URL.createObjectURL(blob);
$('mod-any').innerHTML = [
`<a id="sexport" href="${url}" download="${name}.km">x</a>`
].join('');
$('sexport').click();
json = API.conf.export({work});
API.util.download(json, `${name}.km`);
}
});
}
@ -630,10 +607,12 @@
if (name) {
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();
@ -694,28 +673,28 @@
let mode = API.mode.get(),
current = settings(),
local = isLocalDevice(devicename),
dproc = current.devproc[devicename],
dev = current.device = CONF.device_from_code(code,mode),
proc = current.process,
newdev = dproc === undefined;
dproc = current.devproc[devicename], // last process name for this device
newdev = dproc === undefined, // first time device is selected
predev = current.filter[mode], // previous device selection
chgdev = predev !== devicename; // device is changing
// first time device use, add any print profiles and set to default if present
// if (newdev) {
// console.log('new device', code);
if (code.profiles) {
for (let profile of code.profiles) {
let profname = profile.processName;
if (!current.sproc[mode][profname]) {
console.log('adding profile', profname, 'to', mode);
current.sproc[mode][profname] = profile;
}
if (newdev && !current.devproc[devicename]) {
console.log('setting default profile for new device', devicename, 'to', profname);
current.devproc[devicename] = dproc = profname;
}
if (code.profiles) {
for (let profile of code.profiles) {
let profname = profile.processName;
// if no saved profile by that name for this mode...
if (!current.sproc[mode][profname]) {
console.log('adding profile', profname, 'to', mode);
current.sproc[mode][profname] = profile;
}
// if it's a new device, seed the new profile name as last profile
if (newdev && !current.devproc[devicename]) {
console.log('setting default profile for new device', devicename, 'to', profname);
current.devproc[devicename] = dproc = profname;
}
}
// }
}
dev.new = false;
dev.deviceName = devicename;
@ -724,9 +703,16 @@
UI.deviceBelt.checked = dev.bedBelt;
UI.deviceRound.checked = dev.bedRound;
UI.deviceOrigin.checked = dev.outputOriginCenter || dev.originCenter;
UI.fwRetract.checked = dev.fwRetract;
// add extruder selection buttons
if (dev.extruders) {
for (let ext of dev.extruders) {
// add missing deselect field from legacy configs
if (!ext.extDeselect) {
ext.extDeselect = [];
}
}
let ext = API.lists.extruders = [];
dev.internal = 0;
let selext = $('pop-nozzle');
@ -756,10 +742,10 @@
UI.bedDepth,
UI.bedWidth,
UI.maxHeight,
UI.extrudeAbs,
UI.deviceOrigin,
UI.deviceRound,
UI.deviceBelt,
UI.fwRetract,
UI.gcodeFan,
UI.gcodeTrack,
UI.gcodeLayer,
@ -780,12 +766,12 @@
UI.extDel,
UI.extOffsetX,
UI.extOffsetY,
UI.extSelect
UI.extSelect,
UI.extDeselect
].forEach(function(e) {
e.disabled = !local;
});
UI.extrudeAbs.style.display = mode === 'CAM' ? 'none' : 'flex';
UI.deviceSave.disabled = !local;
UI.deviceDelete.disabled = !local;
UI.deviceExport.disabled = !local;
@ -802,8 +788,12 @@
API.view.update_fields();
platform.update_size();
platform.update_origin();
platform.update();
// store current device name for this mode
current.filter[mode] = devicename;
// cache device record for this mode (restored in setMode)
current.cdev[mode] = currentDevice = dev;
if (dproc) {
@ -814,6 +804,13 @@
}
API.conf.save();
API.const.SPACE.view.setHome(dev.bedBelt ? Math.PI/2 : 0);
// when changing devices, update focus on widgets
if (chgdev) {
setTimeout(API.space.set_focus, 0);
}
UC.refresh();
if (dev.imageURL) {
@ -844,12 +841,14 @@
}
function loadDeviceImage(dev, url) {
new THREE.TextureLoader().load(url || dev.imageURL, texture => {
let turl = url || dev.imageURL;
if (!turl) return;
new THREE.TextureLoader().load(turl, texture => {
loadDeviceTexture(dev, texture);
}, inc => {
console.log({load_inc: inc});
}, error => {
console.log({load_error: error});
console.log({load_error: error, turl});
clearDeviceTexture();
});
}
@ -949,12 +948,13 @@
showDevices();
};
UI.deviceExport.onclick = function() {
let exp = btoa(JSON.stringify({
let exp = API.util.b64enc({
version: KIRI.version,
device: selected,
process: API.process.code(),
code: devs[selected],
time: Date.now()
}));
});
deviceExport(exp, selected);
};
@ -1236,6 +1236,12 @@
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;
@ -1249,6 +1255,11 @@
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;
@ -1352,13 +1363,15 @@
return !isBelt();
}
function isDanger() {
return UI.danger.checked;
}
// MAIN INITIALIZATION FUNCTION
function init_one() {
API.event.emit('init.one');
$('gsbox').src = `${proto}//${root}/kiri/gsbox.png?ver=${ver}`;
// ensure we have settings from last session
API.conf.restore();
@ -1387,7 +1400,7 @@
SPACE.setSkyColor(controller.dark ? 0 : 0xffffff);
SPACE.init(container, function (delta) {
if (API.var.layer_max === 0 || !delta) return;
if (settings().controller.reverseZoom) delta = -delta;
if (controller.reverseZoom) delta = -delta;
let same = API.var.layer_hi === API.var.layer_lo;
let track = API.var.layer_lo > 0;
if (delta > 0) {
@ -1406,7 +1419,7 @@
}
API.view.update_slider();
API.show.slices();
});
}, controller.ortho);
SPACE.platform.onMove(API.conf.save);
SPACE.platform.setRound(true);
SPACE.useDefaultKeys(API.feature.on_key === undefined || API.feature.on_key_defaults);
@ -1448,6 +1461,10 @@
back: $('lt-back'),
trash: $('lt-trash'),
ltsetup: $('lt-setup'),
ltfile: $('lt-file'),
ltview: $('lt-view'),
ltact: $('act-slice'),
rotate: $('lt-rotate'),
scale: $('lt-scale'),
nozzle: $('lt-nozzle'),
@ -1534,6 +1551,7 @@
deviceOrigin: UC.newBoolean(LANG.dv_orgc_s, onBooleanClick, {title:LANG.dv_orgc_l, modes:FDM_LASER_SLA}),
deviceRound: UC.newBoolean(LANG.dv_bedc_s, onBooleanClick, {title:LANG.dv_bedc_l, modes:FDM, trigger:true, show:isNotBelt}),
deviceBelt: UC.newBoolean(LANG.dv_belt_s, onBooleanClick, {title:LANG.dv_belt_l, modes:FDM, trigger:true, show:() => !UI.deviceRound.checked}),
fwRetract: UC.newBoolean(LANG.dv_retr_s, onBooleanClick, {title:LANG.dv_retr_l, modes:FDM}),
extruder: UC.newGroup(LANG.dv_gr_ext, $('device2'), {group:"dext", inline:true, modes:FDM}),
extFilament: UC.newInput(LANG.dv_fila_s, {title:LANG.dv_fila_l, convert:UC.toFloat, modes:FDM}),
@ -1541,7 +1559,7 @@
extOffsetX: UC.newInput(LANG.dv_exox_s, {title:LANG.dv_exox_l, convert:UC.toFloat, modes:FDM}),
extOffsetY: UC.newInput(LANG.dv_exoy_s, {title:LANG.dv_exoy_l, convert:UC.toFloat, modes:FDM}),
extSelect: UC.newText(LANG.dv_exts_s, {title:LANG.dv_exts_l, modes:FDM, size:14, height:3, modes:FDM, area:gcode}),
extrudeAbs: UC.newBoolean(LANG.dv_xtab_s, onBooleanClick, {title:LANG.dv_xtab_l, modes:FDM}),
extDeselect: UC.newText(LANG.dv_dext_s, {title:LANG.dv_dext_l, modes:FDM, size:14, height:3, modes:FDM, area:gcode}),
extActions: UC.newRow([
UI.extPrev = UC.newButton(undefined, undefined, {icon:'<i class="fas fa-less-than"></i>'}),
UI.extAdd = UC.newButton(undefined, undefined, {icon:'<i class="fas fa-plus"></i>'}),
@ -1571,8 +1589,10 @@
lprefs: UC.newGroup(LANG.op_menu, $('prefs-gen1'), {inline: true}),
reverseZoom: UC.newBoolean(LANG.op_invr_s, booleanSave, {title:LANG.op_invr_l}),
ortho: UC.newBoolean(LANG.op_orth_s, booleanSave, {title:LANG.op_orth_l}),
dark: UC.newBoolean(LANG.op_dark_s, booleanSave, {title:LANG.op_dark_l}),
decals: UC.newBoolean(LANG.op_decl_s, booleanSave, {title:LANG.op_decl_s}),
danger: UC.newBoolean(LANG.op_dang_s, booleanSave, {title:LANG.op_dang_l}),
lprefs: UC.newGroup(LANG.op_disp, $('prefs-gen2'), {inline: true}),
showOrigin: UC.newBoolean(LANG.op_shor_s, booleanSave, {title:LANG.op_shor_l}),
@ -1580,12 +1600,13 @@
showSpeeds: UC.newBoolean(LANG.op_sped_s, speedSave, {title:LANG.op_sped_l}),
lineType: UC.newSelect(LANG.op_line_s, {title: LANG.op_line_l, action: lineTypeSave, modes:FDM}, "linetype"),
animesh: UC.newSelect(LANG.op_anim_s, {title: LANG.op_anim_l, action: aniMeshSave, modes:CAM}, "animesh"),
units: UC.newSelect(LANG.op_unit_s, {title: LANG.op_unit_l, action:unitsSave, modes:CAM, trace:true}, "units"),
units: UC.newSelect(LANG.op_unit_s, {title: LANG.op_unit_l, action: unitsSave, modes:CAM, trace:true}, "units"),
layout: UC.newGroup(LANG.lo_menu, $('prefs-lay'), {inline: true}),
autoSave: UC.newBoolean(LANG.op_save_s, booleanSave, {title:LANG.op_save_l}),
autoLayout: UC.newBoolean(LANG.op_auto_s, booleanSave, {title:LANG.op_auto_l}),
freeLayout: UC.newBoolean(LANG.op_free_s, booleanSave, {title:LANG.op_free_l}),
autoSave: UC.newBoolean(LANG.op_save_s, booleanSave, {title:LANG.op_save_l}),
spaceLayout: UC.newInput(LANG.op_spcr_s, {title:LANG.op_spcr_l, convert:UC.toFloat, size:3, units:true}),
export: UC.newGroup(LANG.xp_menu, $('prefs-xpo'), {inline: true}),
exportOcto: UC.newBoolean(`OctoPrint`, booleanSave),
@ -1596,6 +1617,7 @@
parts: UC.newGroup(LANG.pt_menu, $('prefs-prt'), {inline: true}),
detail: UC.newSelect(LANG.pt_qual_s, {title: LANG.pt_qual_l, action: detailSave}, "detail"),
decimate: UC.newBoolean(LANG.pt_deci_s, booleanSave, {title: LANG.pt_deci_l}),
healMesh: UC.newBoolean(LANG.pt_heal_s, booleanSave, {title: LANG.pt_heal_l}),
prefadd: UC.checkpoint($('prefs-add')),
@ -1603,7 +1625,7 @@
sliceHeight: UC.newInput(LANG.sl_lahi_s, {title:LANG.sl_lahi_l, convert:UC.toFloat, modes:FDM}),
sliceMinHeight: UC.newInput(LANG.ad_minl_s, {title:LANG.ad_minl_l, bound:UC.bound(0,3.0), convert:UC.toFloat, modes:FDM, show: () => UI.sliceAdaptive.checked}),
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
sliceShells: UC.newInput(LANG.sl_shel_s, {title:LANG.sl_shel_l, convert:UC.toInt, modes:FDM}),
sliceShells: UC.newInput(LANG.sl_shel_s, {title:LANG.sl_shel_l, convert:UC.toFloat, modes:FDM}),
sliceTopLayers: UC.newInput(LANG.sl_ltop_s, {title:LANG.sl_ltop_l, convert:UC.toInt, modes:FDM}),
sliceSolidLayers: UC.newInput(LANG.sl_lsld_s, {title:LANG.sl_lsld_l, convert:UC.toInt, modes:FDM}),
sliceBottomLayers: UC.newInput(LANG.sl_lbot_s, {title:LANG.sl_lbot_l, convert:UC.toInt, modes:FDM}),
@ -1621,15 +1643,20 @@
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM, show:isNotBelt}),
firstLayerNozzleTemp:UC.newInput(LANG.fl_nozl_s, {title:LANG.fl_nozl_l, convert:UC.toInt, modes:FDM, show:isNotBelt}),
firstLayerBedTemp: UC.newInput(LANG.fl_bedd_s, {title:LANG.fl_bedd_l, convert:UC.toInt, modes:FDM, show:isNotBelt}),
firstLayerFanSpeed: UC.newInput(LANG.ou_fans_s, {title:LANG.ou_fans_l, convert:UC.toInt, bound:UC.bound(0,255), modes:FDM}),
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM, show:isNotBelt}),
firstLayerYOffset: UC.newInput(LANG.fl_zoff_s, {title:LANG.fl_zoff_l, convert:UC.toFloat, modes:FDM, show:isBelt}),
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM, show:isBelt}),
firstLayerRate: UC.newInput(LANG.fl_rate_s, {title:LANG.fl_rate_l, convert:UC.toFloat, modes:FDM}),
firstLayerFillRate: UC.newInput(LANG.fl_frat_s, {title:LANG.fl_frat_l, convert:UC.toFloat, modes:FDM, show:isNotBelt}),
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM, show:isNotBelt}),
firstLayerLineMult: UC.newInput(LANG.fl_sfac_s, {title:LANG.fl_sfac_l, convert:UC.toFloat, bound:UC.bound(0.5,2), modes:FDM, show:isNotBelt}),
firstLayerPrintMult: UC.newInput(LANG.fl_mult_s, {title:LANG.fl_mult_l, convert:UC.toFloat, modes:FDM}),
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM, show:isBelt}),
firstLayerBrim: UC.newInput(LANG.fl_brim_s, {title:LANG.fl_brim_l, convert:UC.toFloat, modes:FDM, show:isBelt}),
firstLayerBrimTrig: UC.newInput(LANG.fl_brmn_s, {title:LANG.fl_brmn_l, convert:UC.toFloat, modes:FDM, show:isBelt}),
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM, show:isBelt}),
firstLayerBeltLead: UC.newInput(LANG.fl_bled_s, {title:LANG.fl_bled_l, convert:UC.toFloat, modes:FDM, show:isBelt}),
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM, show:isNotBelt}),
outputBrimCount: UC.newInput(LANG.fl_skrt_s, {title:LANG.fl_skrt_l, convert:UC.toInt, modes:FDM, show:isNotBelt}),
outputBrimOffset: UC.newInput(LANG.fl_skro_s, {title:LANG.fl_skro_l, convert:UC.toFloat, modes:FDM, show:isNotBelt}),
@ -1639,29 +1666,32 @@
fdmInfill: UC.newGroup(LANG.fi_menu, $('settings'), {modes:FDM}),
sliceFillType: UC.newSelect(LANG.fi_type, {modes:FDM}, "infill"),
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
sliceFillSparse: UC.newInput(LANG.fi_pcnt_s, {title:LANG.fi_pcnt_l, convert:UC.toFloat, bound:UC.bound(0.0,1.0), modes:FDM}),
sliceFillAngle: UC.newInput(LANG.fi_angl_s, {title:LANG.fi_angl_l, convert:UC.toFloat, modes:FDM}),
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
sliceFillOverlap: UC.newInput(LANG.fi_over_s, {title:LANG.fi_over_l, convert:UC.toFloat, bound:UC.bound(0.0,2.0), modes:FDM}),
sliceFillRate: UC.newInput(LANG.ou_feed_s, {title:LANG.fi_rate_l, convert:UC.toInt, bound:UC.bound(0,300), modes:FDM}),
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
sliceFillAngle: UC.newInput(LANG.fi_angl_s, {title:LANG.fi_angl_l, convert:UC.toFloat, modes:FDM}),
// sliceFillWidth: UC.newInput(LANG.fi_wdth_s, {title:LANG.fi_wdth_l, convert:UC.toFloat, modes:FDM}),
fdmSupport: UC.newGroup(LANG.sp_menu, null, {modes:FDM, marker:false}),
sliceSupportNozzle: UC.newSelect(LANG.sp_nozl_s, {title:LANG.sp_nozl_l, modes:FDM}, "extruders"),
sliceSupportDensity: UC.newInput(LANG.sp_dens_s, {title:LANG.sp_dens_l, convert:UC.toFloat, bound:UC.bound(0.05,1.0), modes:FDM}),
sliceSupportDensity: UC.newInput(LANG.sp_dens_s, {title:LANG.sp_dens_l, convert:UC.toFloat, bound:UC.bound(0.0,1.0), modes:FDM}),
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
sliceSupportSize: UC.newInput(LANG.sp_size_s, {title:LANG.sp_size_l, bound:UC.bound(1.0,200.0), convert:UC.toFloat, modes:FDM}),
sliceSupportOffset: UC.newInput(LANG.sp_offs_s, {title:LANG.sp_offs_l, bound:UC.bound(0.0,200.0), convert:UC.toFloat, modes:FDM}),
sliceSupportGap: UC.newInput(LANG.sp_gaps_s, {title:LANG.sp_gaps_l, bound:UC.bound(0,5), convert:UC.toInt, modes:FDM}),
sliceSupportGap: UC.newInput(LANG.sp_gaps_s, {title:LANG.sp_gaps_l, bound:UC.bound(0,5), convert:UC.toInt, modes:FDM, show:isNotBelt}),
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
sliceSupportArea: UC.newInput(LANG.sp_area_s, {title:LANG.sp_area_l, bound:UC.bound(0.0,200.0), convert:UC.toFloat, modes:FDM}),
sliceSupportExtra: UC.newInput(LANG.sp_xpnd_s, {title:LANG.sp_xpnd_l, bound:UC.bound(0.0,200.0), convert:UC.toFloat, modes:FDM}),
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
sliceSupportAngle: UC.newInput(LANG.sp_angl_s, {title:LANG.sp_angl_l, bound:UC.bound(0.0,90.0), convert:UC.toFloat, modes:FDM}),
sliceSupportAngle: UC.newInput(LANG.sp_angl_s, {title:LANG.sp_angl_l, bound:UC.bound(0.0,90.0), convert:UC.toFloat, modes:FDM, xshow:isNotBelt}),
sliceSupportEnable: UC.newBoolean(LANG.sp_auto_s, onBooleanClick, {title:LANG.sp_auto_l, modes:FDM, show:isNotBelt}),
sliceSupportGen: UC.newRow([
UI.ssaGen = UC.newButton(LANG.sp_detect, onButtonClick, {class: "f-col grow a-center"})
], { modes: FDM, class: "ext-buttons f-row grow" }),
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM, show:isNotBelt}),
sliceSupportManual: UC.newRow([
(UI.ssmAdd = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-plus"></i>'})),
(UI.ssmDun = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-check"></i>'})),
@ -1720,6 +1750,7 @@
outputTemp: UC.newInput(LANG.ou_nozl_s, {title:LANG.ou_nozl_l, convert:UC.toInt, modes:FDM}),
outputBedTemp: UC.newInput(LANG.ou_bedd_s, {title:LANG.ou_bedd_l, convert:UC.toInt, modes:FDM}),
outputFanSpeed: UC.newInput(LANG.ou_fans_s, {title:LANG.ou_fans_l, convert:UC.toInt, bound:UC.bound(0,255), modes:FDM}),
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
outputFeedrate: UC.newInput(LANG.ou_feed_s, {title:LANG.ou_feed_l, convert:UC.toInt, modes:FDM}),
outputFinishrate: UC.newInput(LANG.ou_fini_s, {title:LANG.ou_fini_l, convert:UC.toInt, modes:FDM}),
@ -1729,7 +1760,6 @@
outputFillMult: UC.newInput(LANG.ou_flml_s, {title:LANG.ou_exml_l, convert:UC.toFloat, bound:UC.bound(0.0,2.0), modes:FDM}),
outputSparseMult: UC.newInput(LANG.ou_spml_s, {title:LANG.ou_exml_l, convert:UC.toFloat, bound:UC.bound(0.0,2.0), modes:FDM}),
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
outputFanLayer: UC.newInput(LANG.ou_fanl_s, {title:LANG.ou_fanl_l, convert:UC.toInt, bound:UC.bound(0,100), modes:FDM}),
sliceShellOrder: UC.newSelect(LANG.sl_ordr_s, {title:LANG.sl_ordr_l, modes:FDM}, "shell"),
sliceLayerStart: UC.newSelect(LANG.sl_strt_s, {title:LANG.sl_strt_l, modes:FDM}, "start"),
camConventional: UC.newBoolean(LANG.ou_conv_s, onBooleanClick, {title:LANG.ou_conv_l, modes:CAM}),
@ -1745,18 +1775,20 @@
advanced: UC.newGroup(LANG.ad_menu, null, {modes:FDM}),
outputRetractDist: UC.newInput(LANG.ad_rdst_s, {title:LANG.ad_rdst_l, convert:UC.toFloat, modes:FDM}),
outputRetractSpeed: UC.newInput(LANG.ad_rrat_s, {title:LANG.ad_rrat_l, convert:UC.toInt, modes:FDM}),
outputRetractWipe: UC.newInput(LANG.ad_wpln_s, {title:LANG.ad_wpln_l, bound:UC.bound(0.0,10), convert:UC.toFloat, modes:FDM}),
outputRetractDwell: UC.newInput(LANG.ad_rdwl_s, {title:LANG.ad_rdwl_l, convert:UC.toInt, modes:FDM}),
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
// outputWipeDistance: UC.newInput("wipe", {title:"non-printing move at\close of polygon\nin millimeters", bound:UC.bound(0.0,10), convert:UC.toFloat, modes:FDM}),
sliceSolidMinArea: UC.newInput(LANG.ad_msol_s, {title:LANG.ad_msol_l, convert:UC.toFloat, modes:FDM}),
outputMinSpeed: UC.newInput(LANG.ad_mins_s, {title:LANG.ad_mins_l, bound:UC.bound(5,200), convert:UC.toFloat, modes:FDM}),
outputCoastDist: UC.newInput(LANG.ad_scst_s, {title:LANG.ad_scst_l, bound:UC.bound(0.0,10), convert:UC.toFloat, modes:FDM}),
outputShortPoly: UC.newInput(LANG.ad_spol_s, {title:LANG.ad_spol_l, bound:UC.bound(0,10000), convert:UC.toFloat, modes:FDM}),
outputCoastDist: UC.newInput(LANG.ad_scst_s, {title:LANG.ad_scst_l, bound:UC.bound(0.0,10), convert:UC.toFloat, modes:FDM}),
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
zHopDistance: UC.newInput(LANG.ad_zhop_s, {title:LANG.ad_zhop_l, bound:UC.bound(0,3.0), convert:UC.toFloat, modes:FDM}),
arcTolerance: UC.newInput(LANG.ad_arct_s, {title:LANG.ad_arct_l, bound:UC.bound(0,1.0), convert:UC.toFloat, modes:FDM, show:() => { return isDanger() && isNotBelt() }}),
antiBacklash: UC.newInput(LANG.ad_abkl_s, {title:LANG.ad_abkl_l, bound:UC.bound(0,3), convert:UC.toInt, modes:FDM}),
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
gcodePauseLayers: UC.newInput(LANG.ag_paws_s, {title:LANG.ag_paws_l, modes:FDM, comma:true}),
outputPeelGuard: UC.newInput(LANG.ag_peel_s, {title:LANG.ag_peel_l, convert:UC.toInt, modes:FDM, comma:true, show:isBelt}),
gcodePauseLayers: UC.newInput(LANG.ag_paws_s, {title:LANG.ag_paws_l, modes:FDM, comma:true, show:isNotBelt}),
outputLoopLayers: UC.newInput(LANG.ag_loop_s, {title:LANG.ag_loop_l, modes:FDM, comma:true, show:isBelt}),
outputLayerRetract: UC.newBoolean(LANG.ad_lret_s, onBooleanClick, {title:LANG.ad_lret_l, modes:FDM}),
@ -1798,6 +1830,9 @@
slaFirstOffset: UC.newInput(LANG.sa_opzo_s, {title:LANG.sa_opzo_l, convert:UC.toFloat, bound:UC.bound(0,1), modes:SLA}),
slaAntiAlias: UC.newSelect(LANG.sa_opaa_s, {title:LANG.sa_opaa_l, modes:SLA}, "antialias"),
rangeGroup: UC.newGroup("ranges", null, {modes:FDM, group:"ranges"}),
rangeList: UC.newRow([], {}),
settingsGroup: UC.newGroup(LANG.se_menu, $('settings')),
settingsTable: UC.newRow([ UI.settingsLoad = UC.newButton(LANG.se_load, settingsLoad) ]),
settingsTable: UC.newRow([ UI.settingsSave = UC.newButton(LANG.se_save, settingsSave) ]),
@ -1955,6 +1990,19 @@
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');
@ -1964,6 +2012,18 @@
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();
};
SPACE.addEventHandlers(self, [
'keyup', keyUpHandler,
@ -1981,9 +2041,17 @@
xv = parseFloat(UI.sizeX.was),
yv = parseFloat(UI.sizeY.was),
zv = parseFloat(UI.sizeZ.was),
xr = (UI.lockX.checked ? ra : 1),
yr = (UI.lockY.checked ? ra : 1),
zr = (UI.lockZ.checked ? ra : 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);
API.selection.scale(xr,yr,zr);
UI.sizeX.was = UI.sizeX.value = xv * xr;
UI.sizeY.was = UI.sizeY.value = yv * yr;
@ -1997,9 +2065,17 @@
xv = parseFloat(UI.scaleX.was),
yv = parseFloat(UI.scaleY.was),
zv = parseFloat(UI.scaleZ.was),
xr = (UI.lockX.checked ? ra : 1),
yr = (UI.lockY.checked ? ra : 1),
zr = (UI.lockZ.checked ? ra : 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);
API.selection.scale(xr,yr,zr);
UI.scaleX.was = UI.scaleX.value = xv * xr;
UI.scaleY.was = UI.scaleY.value = yv * yr;
@ -2035,6 +2111,35 @@
$('rot_z'), selectionRotate
]);
$('lab-axis').onclick = () => {
UI.lockX.checked =
UI.lockY.checked =
UI.lockZ.checked = !(
UI.lockX.checked ||
UI.lockY.checked ||
UI.lockZ.checked
);
};
$('lab-scale').onclick = () => {
API.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;
};
let hpops = [];
UC.hoverPop(UI.ltsetup, { group: hpops, target: $('set-pop') });
UC.hoverPop(UI.ltfile, { group: hpops, target: $('file-pop') });
UC.hoverPop(UI.ltview, { group: hpops, target: $('pop-view') });
UC.hoverPop(UI.ltact, { group: hpops, target: $('pop-slice') });
UC.hoverPop(UI.render, { group: hpops, target: $('pop-render'), sticky: true });
UC.hoverPop(UI.rotate, { group: hpops, target: $('pop-rotate'), sticky: true });
UC.hoverPop(UI.scale, { group: hpops, target: $('pop-scale'), sticky: true });
UC.hoverPop(UI.nozzle, { group: hpops, target: $('pop-nozzle'), sticky: true });
UC.hoverPop($('app-acct'), { group: hpops, target: $('acct-pop') } );
UC.hoverPop($('app-mode'), { group: hpops, target: $('mode-info') } );
UC.hoverPop($('app-name'), { group: hpops, target: $('app-info') } );
UC.onBlur([
UI.toolName,
UI.toolNum,
@ -2080,8 +2185,21 @@
if (int) API.event.emit('mouse.hover', {point: int, event, type: 'platform'});
});
// block standard browser context menu
DOC.oncontextmenu = (event) => {
if (event.target.tagName === 'CANVAS') {
event.preventDefault();
event.stopPropagation();
return false;
}
};
SPACE.mouse.up((event, int) => {
if (event.button === 2 && API.view.isArrange()) {
if (event.button === 2) {
let full = API.view.isArrange();
for (let key of ["layflat","mirror","duplicate"]) {
$(`context-${key}`).disabled = !full;
}
let style = UI.context.style;
style.display = 'flex';
style.left = `${event.clientX-3}px`;
@ -2089,6 +2207,9 @@
UI.context.onmouseleave = () => {
style.display = '';
};
event.preventDefault();
event.stopPropagation();
contextInt = int;
}
});
@ -2114,6 +2235,10 @@
// return selected meshes for further mouse processing
return API.feature.hovers || API.selection.meshes();
}
if (event.button === 2 && API.view.isArrange()) {
event.preventDefault();
event.stopPropagation();
}
});
SPACE.mouse.upSelect(function(object, event) {
@ -2172,6 +2297,8 @@
function init_two() {
API.event.emit('init.two');
// API.space.set_focus();
// call driver initializations, if present
Object.values(KIRI.driver).forEach(driver => {
if (driver.init) try {
@ -2238,6 +2365,8 @@
UI.reverseZoom.checked = control.reverseZoom;
UI.autoSave.checked = control.autoSave;
UI.decimate.checked = control.decimate;
UI.healMesh.checked = control.healMesh;
UI.ortho.checked = control.ortho;
lineTypeSave();
detailSave();
updateFPS();
@ -2272,9 +2401,6 @@
UI.sync();
// load settings provided in url hash
loadSettingsFromServer();
// clear alerts as they build up
setInterval(API.event.alerts, 1000);
@ -2292,7 +2418,7 @@
API.event.emit('init-done', STATS);
// show gdpr if it's never been seen and we're not iframed
if (!SDB.gdpr && WIN.self === WIN.top) {
if (!SDB.gdpr && WIN.self === WIN.top && !SETUP.debug) {
$('gdpr').style.display = 'flex';
}
@ -2359,7 +2485,14 @@
// context menu
$('context-export-stl').onclick = () => { objectsExport() };
$('context-export-workspace').onclick = () => { profileExport(true) };
$('context-clear-workspace').onclick = () => { API.platform.clear(); UI.context.onmouseleave() };
$('context-clear-workspace').onclick = () => {
API.view.set(VIEWS.ARRANGE);
API.platform.clear();
UI.context.onmouseleave();
};
$('context-duplicate').onclick = duplicateSelection;
$('context-mirror').onclick = mirrorSelection;
$('context-layflat').onclick = layFlat;
UI.modal.onclick = API.modal.hide;
UI.modalBox.onclick = (ev) => { ev.stopPropagation() };
@ -2376,21 +2509,38 @@
}
}
// update static html elements with language overrides
UI.lang = function() {
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});
}
}
};
// if a language needs to load, the script is injected and loaded
// first. once this loads, or doesn't, the initialization begins
let lang_load = false;
let lang_set = undefined;
let lang = SETUP.ln ? SETUP.ln[0] : SDB.getItem('kiri-lang') || KIRI.lang.get();
// inject language script if not english
if (lang && lang !== 'en' && lang !== 'en-us') {
lang_set = lang;
let map = KIRI.lang.map(lang);
let scr = DOC.createElement('script');
// scr.setAttribute('defer',true);
scr.setAttribute('src',`/kiri/lang/${lang}.js`);
scr.setAttribute('src',`/kiri/lang/${map}.js?${KIRI.version}`);
(DOC.body || DOC.head).appendChild(scr);
STATS.set('ll',lang);
scr.onload = function() {
KIRI.lang.set(lang);
lang_load = true;
KIRI.lang.set(map);
UI.lang();
init_one();
};
scr.onerror = function(err) {
@ -2401,7 +2551,10 @@
// set to browser default which will be overridden
// by any future script loads (above)
KIRI.lang.set();
if (!lang_load) {
KIRI.lang.set();
UI.lang();
}
// schedule init_one to run after all page content is loaded
// unless a languge script is loading first, in which case it

View file

@ -7,9 +7,25 @@
if (self.kiri.lang) return;
const KIRI = self.kiri, LANG = self.kiri.lang = { current: {} };
const KDFL = 'en-us';
let lset = navigator.language.toLocaleLowerCase();
LANG.map = function(key) {
if (!key) {
return KDFL;
}
let tok = key.split('-');
switch (tok[0]) {
case 'da': return 'da-dk';
case 'de': return 'de-de';
case 'en': return 'en-us';
case 'fr': return 'fr-fr';
case 'pl': return 'pl-pl';
}
return KDFL;
};
LANG.get = function() {
return lset;
};
@ -39,7 +55,12 @@
// update current map from chosen map
Object.keys(map).forEach(key => {
if (LANG.en[key]) {
LANG.current[key] = map[key];
let val = map[key];
// turn arrays into newline separated strings
if (Array.isArray(val)) {
val = val.join('\n');
}
LANG.current[key] = val;
} else {
invalid.push(key);
}

File diff suppressed because it is too large Load diff

View file

@ -133,33 +133,102 @@
maxf = 0,
seq = [],
autolayer = true,
newlayer = false;
newlayer = false,
arcdivs = Math.PI / 12,
hasmoved = false;
const output = scope.output = [ seq ];
const beltaxis = { X: "X", Y: "Z", Z: "Y", E: "E", F: "F" };
const beltfact = Math.cos(Math.PI/4);
function LOG() {
console.log(...[...arguments].map(o => Object.clone(o)));
}
function G2G3(g2, line) {
const rec = {};
line.forEach(function(tok) {
rec[tok.charAt(0)] = parseFloat(tok.substring(1));
});
let center = { x:0, y:0, r:0 };
if (rec.I !== undefined && rec.J !== undefined) {
center.x = pos.X + rec.I;
center.y = pos.Y + rec.J;
//center.r = TODO
console.log("G[2,3] IJ not supported");
} else if (rec.R !== undefined) {
let pd = { x: rec.X - pos.X, y: rec.Y - pos.Y };
let dst = Math.sqrt(pd.x * pd.x + pd.y * pd.y) / 2;
let pr2;
if (Math.abs(dst - rec.R) < 0.001) {
// center point radius
pr2 = { x: (rec.X + pos.X) / 2, y: (rec.Y + pos.Y) / 2};
} else {
// triangulate
pr2 = BASE.util.center2pr({
x: pos.X,
y: pos.Y
}, {
x: rec.X,
y: rec.Y
}, rec.R)[g2 ? 1 : 0];
}
center.x = pr2.x;
center.y = pr2.y;
center.r = rec.R;
} else {
console.log({malfomed_arc: line});
}
// line angles
let a1 = Math.atan2(center.y - pos.Y, center.x - pos.X) + Math.PI;
let a2 = Math.atan2(center.y - rec.Y, center.x - rec.X) + Math.PI;
let ad = BASE.util.thetaDiff(a1, a2, true);
let steps = Math.floor(Math.abs(ad) / arcdivs);
let step = (Math.abs(ad) > 0.001 ? ad : Math.PI * 2) / steps;
let rot = a1 + step;
// LOG({first: pos, last: rec, center, a1, a2, ad, step, rot, line});
// G0G1(false, [`X${center.x}`, `Y${center.y}`, `E1`]);
let pc = { X: pos.X, Y: pos.Y };
for (let i=0; i<=steps-2; i++) {
let np = {
X: center.x + Math.cos(rot) * center.r,
Y: center.y + Math.sin(rot) * center.r
};
rot += step;
G0G1(false, [`X${np.X}`, `Y${np.Y}`, `E1`]);
}
G0G1(false, [`X${rec.X}`, `Y${rec.Y}`, `E1`]);
pos.X = rec.X;
pos.Y = rec.Y;
}
function G0G1(g0, line) {
const mov = {};
const axes = {};
line.forEach(function(tok) {
let axis = tok.charAt(0);
if (morph && belt) {
axis = beltaxis[axis];
}
let val = parseFloat(tok.substring(1));
axes[axis] = val;
if (abs) {
pos[axis] = parseFloat(tok.substring(1));
pos[axis] = val;
} else {
mov[axis] = parseFloat(tok.substring(1));
mov[axis] = val;
pos[axis] += val;
}
});
if (!abs) {
for (let [k,v] of Object.entries(mov)) {
pos[k] += v;
}
}
const point = newPoint(
factor * pos.X + off.X + xoff.X,
factor * pos.Y + off.Y + xoff.Y,
@ -172,7 +241,7 @@
}
const retract = (fdm && pos.E < 0) || undefined;
const moving = g0 || (fdm && pos.E <= 0);
const moving = g0 || (fdm && (pos.E <= 0 || !(axes.X || axes.Y || axes.Z)));
if (!moving && point.x) bounds.min.x = Math.min(bounds.min.x, point.x);
if (!moving && point.x) bounds.max.x = Math.max(bounds.max.x, point.x);
@ -210,6 +279,11 @@
output.push(seq);
}
if (!hasmoved && !moving) {
seq.height = seq.Z = pos.Z;
hasmoved = true;
}
// debug extrusion rate
if (false && fdm && lastPos) {
let dE = (absE ? pos.E - lastPos.E : pos.E);
@ -278,12 +352,25 @@
pos[tok.charAt(0)] = parseFloat(tok.substring(1));
});
break;
case 'G10':
seq.last().retract = true;
break;
case 'G11':
break;
case 'G0':
G0G1(true, line);
break;
case 'G1':
G0G1(false, line);
break;
case 'G2':
// turn arc into a series of points
G2G3(true, line)
break;
case 'G3':
// turn arc into a series of points
G2G3(false, line);
break;
case 'M6':
tool = parseInt(line[0].substring(1));
break;
@ -371,7 +458,6 @@
first = true,
close = !options.open,
last = startPoint,
wipeDist = options.wipe || 0,
coastDist = options.coast || 0,
tool = options.tool;
@ -385,7 +471,7 @@
if (options.onfirst) {
options.onfirst(point);
}
// move from startPoint to point
// move to first output point on poly
addOutput(output, point, 0, moveSpeed, tool);
first = false;
} else {
@ -418,19 +504,18 @@
*/
PRO.slicePrintPath = function(slice, startPoint, offset, output, opt = {}) {
// console.log({slicePrintPath: slice.index, ext:slice.extruder});
let i,
preout = [],
scope = this,
settings = this.settings,
device = settings.device,
process = settings.process,
process = opt.params || settings.process,
extruder = slice.extruder || 0,
nozzleSize = device.extruders[extruder].extNozzle,
firstLayer = opt.first || false,
minSeek = nozzleSize * (opt.minSeek || 1.5),
thinWall = nozzleSize * (opt.thinWall || 1.75),
retractDist = opt.retractOver || 2,
solidWidth = process.sliceFillWidth || 1,
fillMult = opt.mult || process.outputFillMult,
shellMult = opt.mult || process.outputShellMult || (process.laserSliceHeight >= 0 ? 1 : 0),
shellOrder = {"out-in":-1,"in-out":1}[process.sliceShellOrder] || -1,
@ -442,14 +527,17 @@
firstPrintMult = process.firstLayerPrintMult,
printSpeed = opt.speed || (firstLayer ? firstShellSpeed : process.outputFeedrate),
fillSpeed = opt.speed || opt.fillSpeed || (firstLayer ? firstFillSpeed || firstShellSpeed : process.outputFeedrate),
infillSpeed = process.sliceFillRate || opt.infillSpeed || fillSpeed || printSpeed,
moveSpeed = process.outputSeekrate,
origin = startPoint.add(offset),
zhop = process.zHopDistance || 0,
antiBacklash = process.antiBacklash,
doSupport = opt.support,
wipeDist = process.outputRetractWipe || 0,
isBelt = device.bedBelt,
startClone = startPoint.clone(),
z = slice.z;
seedPoint = opt.seedPoint || startPoint,
z = slice.z,
lastPoly;
// apply first layer extrusion multipliers
if (firstLayer) {
@ -459,28 +547,202 @@
}
function retract() {
if (preout.length) preout.last().retract = true;
let array = preout.length ? preout : output;
if (array.length) {
let last = array.last();
last.retract = true;
if (wipeDist && lastPoly && last.point) {
let endpoint = last.point.followTo(lastPoly.center(true), wipeDist);
if (endpoint.inPolygon(lastPoly)) {
addOutput(array, endpoint);
}
}
} else if (opt.pretract) {
opt.pretract(wipeDist);
} else {
console.log('unable to retract. no preout or output');
}
}
function intersectsTop(p1, p2) {
if (opt.danger) {
return retractRequired(p1, p2);
}
let int = false;
POLY.flatten(slice.topPolys().clone(true)).forEach(function(poly) {
if (!int) poly.forEachSegment(function(s1, s2) {
if (UTIL.intersect(p1,p2,s1,s2,BASE.key.SEGINT)) {
int = true;
return int;
return int = true;
}
});
});
return int;
}
function outputTraces(poly, extrude) {
// returns true if no path around and retract required
// returns false if routed around or no retract
function retractRequired(p1, p2) {
const dbug = false;
let ints = [];
let tops = POLY.flatten(slice.topPolys().clone(true));
for (let poly of tops) {
poly.forEachSegment(function(s1, s2) {
let ip = UTIL.intersect(p1,p2,s1,s2,BASE.key.SEGINT);
if (ip) {
ints.push({ip, poly});
}
});
}
// no intersections
if (ints.length === 0) {
return false;
}
// odd # of intersections ?!? do retraction
if (ints.length && ints.length % 2 !== 0) {
if (dbug === slice.index) console.log(slice.index, {odd_intersects: ints});
return true;
}
// sort by distance
ints.sort((a, b) => {
return a.ip.dist - b.ip.dist;
});
let valid = ints.length;
// check pairs. eliminate too close points.
// pairs must intersect same poly or retract.
for (let i=0; i<ints.length; i += 2) {
let i1 = ints[0];
let i2 = ints[1];
// different poly. force retract
if (i1.poly !== i2.poly) {
if (dbug === slice.index) console.log(slice.index, {int_diff_poly: ints, i});
return true;
}
if (i1.ip.distTo2D(i2.ip) < retractDist) {
if (dbug === slice.index) console.log(slice.index, {int_dist_too_small: i1.ip.distTo2D(i2.ip), retractDist});
valid -= 2;
}
}
if (valid > 2) {
if (dbug === slice.index) console.log(slice.index, {complex_route: valid});
return true;
}
if (valid) {
// can route around intersected top polys
for (let i=0; i<ints.length; i += 2) {
let i1 = ints[0];
let i2 = ints[1];
// output first point
addOutput(preout, i1.ip, 0, moveSpeed, extruder);
// create two loops around poly
// find shortest of two paths and emit poly points
let poly = i1.poly;
let isCW = poly.isClockwise();
let points = poly.points;
let p1p = isCW ? points : points.slice().reverse(); // CW
let p2p = isCW ? points.slice().reverse() : points; // CCW
let r1s = p1p.indexOf(isCW ? i1.ip.p2 : i1.ip.p1);
let r1e = p1p.indexOf(isCW ? i2.ip.p1 : i2.ip.p2);
let r1 = r1s === r1e ?
[ p1p[r1s] ] : r1s < r1e ?
[ ...p1p.slice(r1s,r1e+1) ] :
[ ...p1p.slice(r1s), ...p1p.slice(0,r1e+1) ];
let r1d = 0;
for (let i=1; i<r1.length; i++) {
r1d += r1[i-1].distTo2D(r1[i]);
}
let r2s = p2p.indexOf(isCW ? i1.ip.p1 : i1.ip.p2);
let r2e = p2p.indexOf(isCW ? i2.ip.p2 : i2.ip.p1);
let r2 = r2s === r2e ?
[ p2p[r2s] ] : r2s < r2e ?
[ ...p2p.slice(r2s,r2e+1) ] :
[ ...p2p.slice(r2s), ...p2p.slice(0,r2e+1) ];
let r2d = 0;
for (let i=1; i<r2.length; i++) {
r2d += r2[i-1].distTo2D(r2[i]);
}
let route = r1d <= r2d ? r1 : r2;
if (dbug === slice.index) console.log(slice.index, {
ints: ints.map(i=>i.ip.dist),
i1, i2, same: i1.poly === i2.poly,
route,
r1, r1d, r1s, r1e,
r2, r2d, r2s, r2e,
isCW});
for (let p of route) {
addOutput(preout, p, 0, moveSpeed, extruder);
}
// output last point
addOutput(preout, i2.ip, 0, moveSpeed, extruder);
}
}
return false;
}
function outputTraces(poly, opt = {}) {
if (!poly) return;
if (Array.isArray(poly)) {
outputOrderClosest(poly, function(next) {
outputTraces(next, extrude);
}, null);
if (opt.sort) {
let polys = poly.slice().sort(function(a,b) {
return (a.perimeter() - b.perimeter()) * opt.sort;
});
let debug = polys.length > 3;
let last;
while (polys.length) {
let next;
for (let p of polys) {
if (!last) {
next = p;
break;
}
if (opt.sort > 0) {
// in-out
if (last.isInside(p)) {
next = p;
break;
}
} else {
// out-in
if (p.isInside(last)) {
next = p;
break;
}
}
}
if (next) {
last = next;
polys.remove(next);
outputTraces(next, opt);
} else {
last = null;
}
}
} else {
outputOrderClosest(poly, function(next) {
outputTraces(next, opt);
}, null);
}
} else {
let finishShell = poly.depth === 0 && !firstLayer;
startPoint = scope.polyPrintPath(poly, startPoint, preout, {
@ -489,13 +751,18 @@
accel: finishShell,
wipe: process.outputWipeDistance || 0,
coast: firstLayer ? 0 : coastDist,
extrude: pref(extrude, shellMult),
extrude: pref(opt.extrude, shellMult),
onfirst: function(firstPoint) {
if (startPoint.distTo2D(firstPoint) > retractDist) {
retract();
let from = seedPoint || startPoint;
if (from.distTo2D(firstPoint) > retractDist) {
if (intersectsTop(from, firstPoint)) {
retract();
}
}
seedPoint = null;
}
});
lastPoly = slice.lastPoly = poly;
}
}
@ -530,12 +797,12 @@
// let emit = i === 0 ? 0 : extrude;
addOutput(preout, p, emit, speed || printSpeed, extruder);
lp = p;
});
}, !poly.open);
return lp;
});
}
function outputFills(lines, options) {
function outputFills(lines, opt = {}) {
if (!lines || lines.length === 0) {
return;
}
@ -544,10 +811,10 @@
start = 0,
skip = false,
lastIndex = -1,
opt = options || {},
flow = opt.flow || 1,
near = opt.near || false,
fast = opt.fast || false,
fill = opt.fill >= 0 ? opt.fill : fillMult,
fill = (opt.fill >= 0 ? opt.fill : fillMult) * flow,
thinDist = near ? thinWall : thinWall;
while (lines && marked < lines.length) {
@ -684,11 +951,11 @@
* the special exception that depth is considered into distance
* so that inner polygons are emitted first.
*
* @param {Array} array of Polygon or Polygon wrappers
* @param {Function} fn
* @param {Function} fnp convert 'next' object into a Polygon
* @param {Array} array of Polygons or Polygon wrappers (tops)
* @param {Function} fn call to emit next candidate
* @param {Function} fnp convert 'next' object into a Polygon for closeness
*/
function outputOrderClosest(array, fn, fnp, newTop) {
function outputOrderClosest(array, fn, fnp) {
if (array.length === 1) {
return fn(array[0]);
}
@ -708,7 +975,6 @@
d: find.distance - (poly.depth * thinWall),
});
}
newTop = false;
if (order.length === 0) {
return;
}
@ -753,37 +1019,31 @@
// and enforce optimal shell order (outer first)
if (isBelt && opt.onBelt) {
startPoint = startClone;
shellOrder = 1;
shellOrder = -1;
}
// top object
let bounds = POLY.flatten(next.shellsAtDepth(0).clone(true));
// innermost shells
let inner = next.innerShells() || [];
// output inner polygons
if (shellOrder === 1)
outputTraces([].appendAll(next.innerShells() || []));
if (shellOrder === 1) outputTraces(inner, { sort: shellOrder });
// sort perimeter polygon by length to go out-to-in or in-to-out
(next.shells || []).sort(function(a,b) {
return a.perimeter() > b.perimeter() ? shellOrder : -shellOrder;
}).forEach(function(poly, index) {
outputTraces(poly);
});
outputTraces(next.shells, { sort: shellOrder });
// output outer polygons
if (shellOrder === -1)
outputTraces([].appendAll(next.innerShells() || []));
if (shellOrder === -1) outputTraces(inner, { sort: shellOrder });
// output thin fill
outputFills(next.thin_fill, {near: true});
// then output solid and sparse fill
outputFills(next.fill_lines);
outputSparse(next.fill_sparse, sparseMult);
outputFills(next.fill_lines, {flow: solidWidth});
outputSparse(next.fill_sparse, sparseMult, infillSpeed);
lastTop = next;
}
}, function(obj) {
// for tops
return obj instanceof Polygon ? obj : obj.poly;
});
@ -971,15 +1231,15 @@
function constOp(tok, consts, opch, op) {
let pos, v1, v2;
if ((pos = tok.indexOf(opch)) > 0) {
v1 = consts[tok.substring(0,pos)] || 0;
v2 = parseInt(tok.substring(pos+1)) || 0;
return op(v1,v2);
v1 = parseFloat(consts[tok.substring(0,pos)] || 0);
v2 = parseFloat(tok.substring(pos+1)) || 0;
return op(v1,v2).round(4);
} else {
return null;
}
}
function constReplace(str, consts, start) {
function constReplace(str, consts, start, pad, short) {
let cs = str.indexOf("{", start || 0),
ce = str.indexOf("}", cs),
tok, nutok, nustr;
@ -991,9 +1251,24 @@
constOp(tok, consts, "/", function(v1,v2) { return v1/v2 }) ||
constOp(tok, consts, "*", function(v1,v2) { return v1*v2 }) ||
consts[tok] || 0;
if (pad) {
nutok = nutok.toString();
let oldln = ce-cs+1;
let tokln = nutok.length;
if (tokln < oldln) {
short = (short || 1) + (oldln - tokln);
}
}
nustr = str.replace("{"+tok+"}",nutok);
return constReplace(nustr, consts, ce+1+(nustr.length-str.length));
return constReplace(nustr, consts, ce+1+(nustr.length-str.length), pad, short);
} else {
// insert compensating spaces for accumulated replace string shortages
if (short) {
let si = str.indexOf(';');
if (si > 0) {
str = str.replace(';', ';'.padStart(short,' '));
}
}
return str;
}
}

View file

@ -221,6 +221,7 @@
zM = Math.max(p1.z, p2.z, p3.z),
bm = Math.floor(zm * zScale),
bM = Math.ceil(zM * zScale);
if (bm < 0) bm = 0;
for (j = bm; j < bM; j++) {
buckets[j].push(p1);
buckets[j].push(p2);
@ -529,6 +530,9 @@
// for topo slices, we just need the raw lines
if (!topoMode && !simpleMode) {
slice.groups = connectLines(slice.lines, slice.z);
if (options.union) {
slice.groups = POLY.flatten(POLY.union(POLY.nest(slice.groups), null, true), null, true);
}
if ((debug && slice.lines.excessive) || xray) {
const dash = xray || 3;
slice.lines.forEach((line, i) => {

View file

@ -722,7 +722,11 @@
}
} else {
// no more closest polys (open set)
emit(BASE.newPolygon().addPoints(array));
if (opt.openok) {
emit(BASE.newPolygon().addPoints(array).setOpen());
} else {
emit(BASE.newPolygon().addPoints(array));
}
break loop;
}
}

View file

@ -44,6 +44,17 @@
this.show();
for (let i=0, s=this.slices, len=s.length; i<len; i++) {
s[i].visible = i >= newMin && i <= newMax;
if (s[i].visible) this.lastVis = s[i];
}
}
setLastFraction(frac = 1) {
if (this.lastVis)
for (let mesh of this.lastVis.children) {
let geo = mesh.geometry;
let pos = geo.attributes.position;
let len = frac === 1 ? Infinity : Math.round(pos.count * frac);
geo.setDrawRange(0, len);
}
}

View file

@ -52,6 +52,24 @@
return stacks[name];
}
function getStacks() {
return Object.entries(stacks);
}
function getLabels() {
return Object.keys(DYN);
}
function setVisible(label, bool) {
let ctrl = DYN[label];
if (ctrl) {
ctrl.toggle.checked = bool;
ctrl.group.forEach(mat => {
mat.visible = bool;
});
}
}
function create(name, view) {
if (stacks[name]) {
return stacks[name];
@ -70,6 +88,11 @@
ctrl.group.forEach(mat => {
mat.visible = ctrl.toggle.checked;
});
if (ctrl.toggle.checked) {
KIRI.api.event.emit("stack.show", label);
} else {
KIRI.api.event.emit("stack.hide", label)
}
})
};
let color = lmap[label].color.check;
@ -120,14 +143,24 @@
max = newMax;
}
function setFraction(frac) {
Object.values(stacks).forEach(stack => {
stack.obj.setLastFraction(frac);
});
}
KIRI.stacks = {
clear,
create,
rotate,
remove,
getStack,
getStacks,
getRange,
setRange,
getLabels,
setVisible,
setFraction,
setFreeMem
};

View file

@ -30,6 +30,7 @@
prefix = "tab",
state = {},
units = 1,
lastChange = null,
lastBtn = null,
lastTxt = null,
lastPop = null,
@ -40,6 +41,7 @@
KIRI.ui = {
prefix: function(pre) { prefix = pre; return kiri.ui },
inputAction: function(fn) { inputAction = fn; return kiri.ui },
lastChange: function() { return lastChange },
checkpoint,
restore,
refresh,
@ -47,6 +49,7 @@
setExpert,
setHoverPop,
hidePoppers,
hoverPop,
bound,
toInt,
toFloat,
@ -683,6 +686,7 @@
}
});
ip.addEventListener('blur', function(event) {
lastChange = ip;
action(event);
if (opt.trigger) {
refresh();
@ -761,6 +765,7 @@
}
ip.setVisible = row.setVisible;
ip.onchange = function(ev) {
lastChange = ip;
action();
};
ip.onclick = (ev) => {
@ -882,4 +887,54 @@
return array;
}
function hoverPop(el, opt = {}) {
if (Array.isArray(el)) {
opt.group = [];
return el.forEach(ev => hoverPop(ev, opt));
}
let group = opt.group || [];
let closes = opt.closes || group;
let target = opt.target || el;
let popped = false;
group.push(target);
if (closes !== group) {
closes.push(target);
}
let openit = () => {
clearTimeout(closes.timer);
for (let close of closes) {
close.classList.remove("hoverpop");
}
target.classList.add("hoverpop");
popped = true;
};
let closeit = () => {
target.classList.remove("hoverpop");
clearTimeout(closes.timer);
popped = false;
};
if (opt.auto !== false) {
el.addEventListener("mouseenter", openit);
}
el.addEventListener("mouseleave", (ev) => {
closes.timer = setTimeout(() => {
target.classList.remove("hoverpop");
}, 750);
});
if (!opt.sticky) {
el.addEventListener("mouseup", (ev) => {
if (popped) {
closeit();
} else {
openit();
}
});
}
document.addEventListener("keydown", (ev) => {
if (ev.keyCode === 27 || ev.code === 'Escape') {
closeit();
}
});
}
})();

View file

@ -32,6 +32,39 @@
let Group = Widget.Groups = {
list: () => {
return groups.slice()
},
merge: (widgets) => {
let grps = widgets.map(w => w.group).uniq();
if (grps.length > 1) {
let root = grps.shift();
let rpos = root[0].track.pos;
for (let grp of grps) {
for (let w of grp) {
let wpos = w.track.pos;
w.group = root;
w.moveMesh(rpos.x - wpos.x, rpos.y - wpos.y, rpos.z - wpos.z);
w._move(rpos.x, rpos.y, rpos.z, true);
root.push(w);
}
groups.splice(groups.indexOf(grp),1);
}
}
},
split: (widgets) => {
for (let group of widgets.map(w => w.group).uniq()) {
groups.splice(groups.indexOf(group),1);
for (let widget of group) {
let nugroup = Group.forid(widget.id);
nugroup.push(widget);
widget.group = nugroup;
}
}
},
forid: function(id) {
for (let i=0; i<groups.length; i++) {
if (groups[i].id === id) return groups[i];
@ -101,7 +134,7 @@
* @params {String} [id]
* @constructor
*/
function Widget(id,group) {
function Widget(id, group) {
this.id = id || new Date().getTime().toString(36)+(nextId++);
this.group = group || [];
this.group.push(this);
@ -254,6 +287,10 @@
}
};
PRO.getVertices = function() {
return this.mesh.geometry.attributes.position;
};
/**
* @param {THREE.Geometry} geometry
* @returns {Widget}
@ -461,11 +498,11 @@
this.modified = true;
};
PRO.rotate = function(x, y, z) {
PRO.rotate = function(x, y, z, temp) {
this.group.forEach(w => {
w._rotate(x, y, z, false);
w._rotate(x, y, z, temp);
});
this.center();
this.center(false);
};
PRO._rotate = function(x, y, z, temp) {
@ -509,20 +546,55 @@
};
PRO._mirror = function() {
this.setWireframe(false);
this.clearSlices();
this.mesh.applyMatrix4(new THREE.Matrix4().makeScale(-1, 1, 1));
let geo = this.mesh.geometry,
o = this.track;
this.setWireframe(false);
let geo = this.mesh.geometry, ot = this.track;
let pos = geo.attributes.position;
let arr = pos.array;
let count = pos.count;
// invert x
for (let i=0; i<count; i++) {
arr[i*3] = -arr[i*3];
}
// invert face vertices
for (let i=0; i<count; i+=3) {
let x = arr[i*3+0];
let y = arr[i*3+1];
let z = arr[i*3+2];
arr[i*3+0] = arr[i*3+6];
arr[i*3+1] = arr[i*3+7];
arr[i*3+2] = arr[i*3+8];
arr[i*3+6] = x;
arr[i*3+7] = y;
arr[i*3+8] = z;
}
pos.needsUpdate = true;
geo.computeFaceNormals();
geo.computeVertexNormals();
this.mesh.position.x = -this.mesh.position.x;
o.mirror = !o.mirror;
ot.mirror = !ot.mirror;
this.modified = true;
this.points = null;
};
PRO.getGeoVertices = function() {
return this.mesh.geometry.getAttribute('position').array;
let geo = this.mesh.geometry;
let pos = geo.getAttribute('position').array;
if (geo.index) {
let idx = geo.index.array;
let len = idx.length;
let p2 = new Float32Array(len * 3);
let inc = 0;
for (let i=0; i<len; i++) {
let iv = idx[i];
let ip = iv * 3;
p2[inc++] = pos[ip++];
p2[inc++] = pos[ip++];
p2[inc++] = pos[ip];
}
return p2;
} else {
return pos;
}
};
PRO.getPoints = function() {

View file

@ -10,7 +10,8 @@ let BASE = self.base,
print: null,
snap: null
},
cache = {};
wgroup = {},
wcache = {};
// catch clipper alerts and convert to console messages
self.alert = function(o) {
@ -24,7 +25,9 @@ BASE.debug.disable();
const dispatch =
KIRI.server =
KIRI.worker = {
cache: cache,
group: wgroup,
cache: wcache,
decimate: function(data, send) {
let { vertices, options } = data;
@ -38,65 +41,128 @@ KIRI.worker = {
send.done();
},
clear: function(data, send) {
current.snap = null;
current.print = null;
dispatch.group = wgroup = {};
dispatch.cache = wcache = {};
send.done({ clear: true });
},
// widget sync
sync: function(data, send) {
if (data.valid) {
// remove widgets not present in valid list
for (let key in wcache) {
if (data.valid.indexOf(key) < 0) {
delete wcache[key];
for (let [id,group] of Object.entries(wgroup)) {
wgroup[id] = group = group.filter(v => v.id !== key);
group.id = id;
}
}
}
send.done(data.id);
return;
}
let group = wgroup[data.group];
if (!group) {
group = [];
group.id = data.group;
wgroup[data.group] = group;
}
let vertices = new Float32Array(data.vertices),
points = BASE.verticesToPoints(vertices, { maxpass: 0 }),
widget = KIRI.newWidget(data.id).setPoints(points);
widget = KIRI.newWidget(data.id, group).loadVertices(vertices);
// do it here so cancel can work
cache[data.id] = widget;
wcache[data.id] = widget;
// stored for possible future rotations
widget.vertices = vertices;
// fake mesh object to satisfy printing
widget.track = data.tracking;
widget.mesh = {
widget: widget,
position: data.position
};
// restore tracking object
widget.track = data.track;
send.done(data.id);
},
slice: function(data, send) {
let { id, settings } = data;
rotate: function(data, send) {
let { settings } = data;
if (!settings.device.bedBelt) {
return send.done({});
}
send.data({update:0.05, updateStatus:"slicing"});
let widget = cache[data.id],
last = time(),
xpos,
ypos,
now;
try {
let rotation = (Math.PI/180) * (settings.device.bedBelt ? 45 : 0);
if (rotation) {
// need min y for post-rotation offset in prepare
let vert = widget.vertices;
let miny = Infinity;
function mins(vert, last = {}) {
let miny = last.miny || Infinity,
maxy = last.maxy || -Infinity;
for (let i=0, l=vert.length; i<l; ) {
let x = vert[i++];
let y = vert[i++];
let z = vert[i++];
if (z < 0.01) miny = Math.min(miny, y);
if (z < 0.01) {
miny = Math.min(miny, y);
maxy = Math.max(maxy, y);
}
}
return { miny, maxy };
}
function gmin(group) {
let minv = {};
for (let w of group) {
minv = mins(w.vertices, minv);
}
return minv;
}
for (let group of Object.values(wgroup)) {
let { miny, maxy } = gmin(group);
let widget = group[0];
let track = widget.track;
let xpos = track.pos.x;
let ypos = settings.device.bedDepth / 2 + track.pos.y + miny;
let rotation = (Math.PI / 180) * 45;
widget.rotate(rotation,0,0,true);
let minr = gmin(group);
widget.belt = { xpos, ypos, yadd: minr.maxy - minr.miny };
for (let others of group.slice(1)) {
others.belt = widget.belt;
}
widget.getBoundingBox(true);
let track = widget.track;
xpos = track.pos.x;
ypos = settings.device.bedDepth / 2 + track.pos.y + miny;
widget.mesh = null;
widget.points = null;
widget.loadVertices(widget.vertices);
widget._rotate(rotation,0,0,true);
widget.center(false, true);
widget.getBoundingBox(true);
widget.belt = { xpos, ypos };
send.data({group: group.id, belt: widget.belt});
}
send.done({});
},
unrotate: function(data, send) {
let { settings } = data;
if (!settings.device.bedBelt) {
return send.done({});
}
let rotation = (Math.PI / 180) * 45;
for (let group of Object.values(wgroup)) {
let widget = group[0];
widget.groupBounds();
widget.rotate(-rotation,0,0,true);
let { dy, dz } = widget.track.center;
widget.groupBounds();
let { xpos, ypos } = widget.belt;
widget.rotinfo = { angle: 45, dy, dz, xpos, ypos };
for (let others of group.slice(1)) {
others.rotinfo = widget.rotinfo;
}
send.data({group: group.id, rotinfo: widget.rotinfo});
}
send.done({});
},
slice: function(data, send) {
send.data({update:0.001, updateStatus:"slicing"});
let settings = data.settings,
widget = wcache[data.id],
last = time(),
now;
widget.slice(settings, function(error) {
if (error) {
@ -113,19 +179,7 @@ KIRI.worker = {
send.data({index: index, slice: slice.encode(state)}, state.zeros);
})
send.data({send_end: time()});
// unrotate and send delta coordinates
if (rotation) {
widget.setPoints(null);
widget._rotate(-rotation,0,0,true);
let wbb = widget.getBoundingBox(true);
widget.center(false, true);
let dy = (wbb.max.y + wbb.min.y)/2;
let dz = wbb.min.z;
widget.rotinfo = { angle: 45, dy, dz, xpos, ypos };
send.data({ rotinfo: widget.rotinfo });
}
}
send.done({done: true});
}, function(update, msg) {
now = time();
@ -134,16 +188,11 @@ KIRI.worker = {
send.data({update: (0.05 + update * 0.95), updateStatus: msg});
last = now;
});
} catch (error) {
send.data({error: error.toString()});
console.log(error);
}
},
prepare: function(data, send) {
// create widget array from id:widget cache
const widgets = Object.values(cache);
const widgets = Object.values(wcache);
// let client know we've started
send.data({update:0.05, updateStatus:"preview"});
@ -222,7 +271,7 @@ KIRI.worker = {
z: origin.z
};
const device = settings.device;
const print = current.print = KIRI.newPrint(settings, Object.values(cache));
const print = current.print = KIRI.newPrint(settings, Object.values(wcache));
const tools = device.extruders;
const mode = settings.mode;
const thin = settings.controller.lineType === 'line' || mode !== 'FDM';
@ -248,30 +297,13 @@ KIRI.worker = {
out.point = BASE.newPoint(x,y,z || 0);
});
});
const print = current.print = KIRI.newPrint(null, Object.values(cache));
const print = current.print = KIRI.newPrint(null, Object.values(wcache));
const layers = KIRI.driver.FDM.prepareRender(parsed, progress => {
send.data({ progress });
}, { thin: true });
send.done({parsed: KIRI.codec.encode(layers)});
},
clear: function(data, send) {
current.snap = null;
current.print = null;
if (!data.id) {
cache = {};
send.done({ clear: true });
return;
}
let had = cache[data.id] !== undefined;
delete cache[data.id];
send.done({
id: data.id,
had: had,
has: cache[data.id] !== undefined
});
},
config: function(data, send) {
const update = {};
if (data.base) {
@ -383,11 +415,11 @@ KIRI.worker = {
let p3 = (x - 0) * height + (y - 0);
let p4 = (x - 1) * height + (y - 1);
faces[ii++] = p1;
faces[ii++] = p2;
faces[ii++] = p3;
faces[ii++] = p1;
faces[ii++] = p4;
faces[ii++] = p2;
faces[ii++] = p1;
faces[ii++] = p2;
faces[ii++] = p4;
}
}
send.data({progress: x / width});
@ -504,6 +536,24 @@ KIRI.worker = {
// send.done({done: {verts, faces, bigv, vi, ii}}, [ bigv.buffer ]);
send.done({done: {bigv}}, [ bigv.buffer ]);
});
},
zip: function(data, send) {
let { files } = data;
let zip = new JSZip();
for (let file of files) {
zip.file(file.name, file.data);
}
zip.generateAsync({
type: "uint8array",
compression: "DEFLATE",
compressionOptions: { level: 3 },
streamFiles: true
}, progress => {
send.data(progress);
}).then(output => {
send.done(output);
});
}
};
@ -543,20 +593,26 @@ self.onmessage = function(e) {
};
if (run) {
let time_xfer = (time_recv - msg.time),
output = run(msg.data, send),
time_send = time(),
time_proc = time_send - time_recv;
try {
let time_xfer = (time_recv - msg.time),
output = run(msg.data, send),
time_send = time(),
time_proc = time_send - time_recv;
if (output) self.postMessage({
seq: msg.seq,
task: msg.task,
time_send: time_xfer,
time_proc: time_proc,
// replaced on reply side
time_recv: time(),
data: output
});
if (output) self.postMessage({
seq: msg.seq,
task: msg.task,
time_send: time_xfer,
time_proc: time_proc,
// replaced on reply side
time_recv: time(),
data: output
});
} catch (wrkerr) {
// console.log(wrkerr);
console.trace(wrkerr.stack);
send.done({error: wrkerr.toString()});
}
} else {
console.log({kiri_msg:e});
}

View file

@ -3,7 +3,7 @@
let terms = {
COPYRIGHT: "Copyright (C) Stewart Allen <sa@grid.space> - All Rights Reserved",
LICENSE: "See the license.md file included with the source distribution",
VERSION: "2.6.D8"
VERSION: "2.8.D1"
};
if (typeof(module) === 'object') {

View file

@ -41,7 +41,10 @@
API = api;
// wire up animate button in ui
api.function.animate = () => {
api.event.on("function.animate", (mode) => {
if (isAnimate) {
return;
}
if (isCamMode && !camStock) {
return api.show.alert("animation requires stock to be enabled");
}
@ -50,7 +53,7 @@
animate();
}
});
};
});
api.event.on("mode.set", (mode) => {
isCamMode = mode === 'CAM';
@ -61,11 +64,11 @@
if (!isCamMode) {
func.tabClear();
func.traceDone();
UI.label.slice.innerText = 'slice';
UI.label.preview.innerText = 'preview';
UI.label.export.innerText = 'export';
UI.label.slice.innerText = LANG.slice;
UI.label.preview.innerText = LANG.preview;
UI.label.export.innerText = LANG.export;
} else {
UI.label.slice.innerText = 'start';
UI.label.slice.innerText = LANG.start;
}
// do not persist traces across page reloads
func.traceClear();
@ -534,7 +537,7 @@
KIRI.api.widgets.opacity(0.8);
KIRI.api.widgets.for(widget => {
if (ids.indexOf(widget.id) >= 0) {
unselectTraces(widget);
unselectTraces(widget, true);
widget.trace_stack = null;
}
if (widget.trace_stack) {
@ -578,7 +581,7 @@
}
});
});
});
}, poppedRec.single);
api.feature.hover = true;
api.feature.hoverAdds = true;
func.hover = func.traceHover;
@ -831,7 +834,7 @@
dogbones: UC.newBoolean(LANG.co_dogb_s, undefined, {title:LANG.co_dogb_l, show:(op) => { return !op.inputs.wide.checked }}),
inside: UC.newBoolean(LANG.co_olin_s, undefined, {title:LANG.co_olin_l, show:(op) => { return !op.inputs.outside.checked }}),
outside: UC.newBoolean(LANG.co_olot_s, undefined, {title:LANG.co_olot_l, show:(op) => { return !op.inputs.inside.checked }}),
omitthru: UC.newBoolean(LANG.co_omit_s, undefined, {title:LANG.co_omit_l, show:(op) => { return op.inputs.outside.checked }}),
omitthru: UC.newBoolean(LANG.co_omit_s, undefined, {title:LANG.co_omit_l, xshow:(op) => { return op.inputs.outside.checked }}),
wide: UC.newBoolean(LANG.co_wide_s, undefined, {title:LANG.co_wide_l, show:(op) => { return !op.inputs.inside.checked }})
};
@ -867,7 +870,8 @@
step: 'camTraceOver',
down: 'camTraceDown',
rate: 'camTraceSpeed',
plunge: 'camTracePlunge'
plunge: 'camTracePlunge',
single: 'camTraceLines',
}).inputs = {
tool: UC.newSelect(LANG.cc_tool, {}, "tools"),
sep: UC.newBlank({class:"pop-sep"}),
@ -879,6 +883,7 @@
rate: UC.newInput(LANG.cc_feed_s, {title:LANG.cc_feed_l, convert:UC.toInt, units:true}),
plunge: UC.newInput(LANG.cc_plng_s, {title:LANG.cc_plng_l, convert:UC.toInt, units:true}),
sep: UC.newBlank({class:"pop-sep"}),
single: UC.newBoolean(LANG.cc_sngl_s, undefined, {title:LANG.cc_sngl_l}),
select: UC.newRow([
UC.newButton(undefined, func.traceAdd, {icon:'<i class="fas fa-plus"></i>'}),
UC.newButton(undefined, func.traceDone, {icon:'<i class="fas fa-check"></i>'}),
@ -1044,11 +1049,11 @@
}
}
function unselectTraces(widget) {
function unselectTraces(widget, skip) {
if (widget.trace_stack) {
widget.trace_stack.meshes.forEach(mesh => {
if (mesh.selected) {
func.traceToggle(mesh);
func.traceToggle(mesh, skip);
}
});
}

View file

@ -25,11 +25,11 @@
KIRI.loader.push(function(API) {
if (KIRI.client)
CAM.traces = function(ondone) {
CAM.traces = function(ondone, single) {
KIRI.client.sync();
let settings = API.conf.get();
let widgets = API.widgets.map();
KIRI.client.send("cam_traces", { settings }, output => {
KIRI.client.send("cam_traces", { settings, single }, output => {
let ids = [];
KIRI.codec.decode(output).forEach(rec => {
ids.push(rec.id);
@ -41,9 +41,9 @@
if (KIRI.worker)
KIRI.worker.cam_traces = function(data, send) {
const { settings } = data;
const widgets = Object.values(cache);
const fresh = widgets.filter(widget => CAM.traces(settings, widget));
const { settings, single } = data;
const widgets = Object.values(wcache);
const fresh = widgets.filter(widget => CAM.traces(settings, widget, single));
send.done(KIRI.codec.encode(fresh.map(widget => { return {
id: widget.id,
traces: widget.traces,

View file

@ -31,6 +31,7 @@
gcodes = settings.device || {},
tools = settings.tools,
space = gcodes.gcodeSpace ? ' ' : '',
isRML = device.gcodeFExt.toLowerCase() === 'rml',
stripComments = gcodes.gcodeStrip || false,
cmdToolChange = gcodes.gcodeChange || [ "M6 T{tool}" ],
cmdSpindle = gcodes.gcodeSpindle || [ "M3 S{speed}" ],
@ -88,7 +89,7 @@
if (!array) return;
for (i=0; i<array.length; i++) {
line = print.constReplace(array[i], consts);
if (stripComments && (cidx = line.indexOf(";")) >= 0) {
if (!isRML && stripComments && (cidx = line.indexOf(";")) >= 0) {
line = line.substring(0, cidx).trim();
if (line.length === 0) continue;
}
@ -179,7 +180,8 @@
dz = newpos.z - pos.z,
maxf = dz ? maxZd : maxXYd,
feed = Math.min(speed || maxf, maxf),
dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
dist = Math.sqrt(dx * dx + dy * dy + dz * dz),
newFeed = feed && feed !== pos.f;
// drop dup points (all deltas are 0)
if (!(dx || dy || dz)) {
@ -204,11 +206,23 @@
runbox.max.z = Math.max(runbox.max.z, pos.z);
nl.append(space).append("Z").append(add0(pos.z * factor));
}
if (feed && feed !== pos.f) {
if (newFeed) {
pos.f = feed;
nl.append(space).append("F").append(add0(feed * factor, true));
}
// temp hack to support RML1 dialect from a file extensions trigger
if (isRML) {
if (speed) {
if (newFeed) {
append(`VS${feed};`);
}
nl = [ "Z", add0(pos.x * factor), ",", add0(pos.y * factor), ",", add0(pos.z * factor), ";" ];
} else {
nl = [ "PU", add0(pos.x * factor), ",", add0(pos.y * factor), ";" ];
}
}
// update time calculation
time += (dist / (pos.f || 1000)) * 60;
@ -279,7 +293,10 @@
if (layerout.spindle && layerout.spindle !== spindle) {
spindle = layerout.spindle;
if (spindle > 0) {
filterEmit(cmdSpindle, {speed: Math.abs(spindle)});
let speed = Math.abs(spindle);
filterEmit(cmdSpindle, {
speed, spindle: speed, rpm: speed
});
} else {
append("M4");
}

View file

@ -109,6 +109,10 @@
let { tshadow, shadowTop, ztOff, zBottom, zMax } = state;
let { process, stock } = settings;
if (op.down <= 0) {
throw `invalid step down "${op.down}"`;
}
let roughIn = op.inside;
let roughTop = op.top;
let roughDown = op.down;
@ -219,7 +223,7 @@
});
shadow = POLY.nest(shadow);
// expand shadow by half tool diameter + stock to leave
// shell = shadow expanded by half tool diameter + leave stock
const sadd = roughIn ? toolDiam / 2 : toolDiam / 2;
const shell = POLY.offset(shadow, sadd + roughLeave);
@ -262,7 +266,7 @@
if (!offset) return;
if (process.camStockClipTo) {
if (process.camStockClipTo && stock.x && stock.y && stock.center) {
let rect = newPolygon().centerRectangle(stock.center, stock.x, stock.y);
offset = cutPolys([rect], offset, slice.z, true);
}
@ -270,10 +274,12 @@
// elimate double inset on inners
offset.forEach(op => {
if (op.inner) {
let operim = op.perimeter();
let pv1 = op.perimeter();
let newinner = [];
op.inner.forEach(oi => {
if (Math.abs(oi.perimeter() - operim) > 0.01) {
let pv2 = oi.perimeter();
let pct = pv1 > pv2 ? pv2/pv1 : pv1/pv2;
if (pct < 0.98) {
newinner.push(oi);
}
});
@ -307,8 +313,10 @@
let { polyEmit, poly2polyEmit, depthRoughPath } = ops;
let { camOut, newLayer, printPoint } = ops;
let { settings, widget } = state;
let { process } = settings;
let { process, controller } = settings;
let danger = controller.danger;
let easeDown = process.camEaseDown;
let cutdir = process.camConventional;
let depthFirst = process.camDepthFirst;
let depthData = [];
@ -368,7 +376,9 @@
// return POLY.nest(level.filter(poly => poly.depth === 0));
return POLY.nest(level.filter(poly => poly.depth === 0).clone());
});
printPoint = depthRoughPath(printPoint, 0, depthData, tops, polyEmit);
// experimental start of ease down
let ease = danger && op.down && easeDown ? op.down : 0;
printPoint = depthRoughPath(printPoint, 0, depthData, tops, polyEmit, false, ease);
// printPoint = depthRoughPath(printPoint, 0, depthData, tops, (poly, index, count, start) => {
// console.log({z: poly.getZ(), i: poly.id, index, poly});
// return polyEmit(poly, index, count, start);
@ -390,6 +400,10 @@
let { updateToolDiams, zThru, zBottom, shadowTop, tabs, cutTabs, cutPolys } = state;
let { process, stock } = settings;
if (op.down <= 0) {
throw `invalid step down "${op.down}"`;
}
let toolDiam = this.toolDiam = new CAM.Tool(settings, op.tool).fluteDiameter();
updateToolDiams(toolDiam);
@ -405,7 +419,7 @@
indices = indices.appendAll(flats).sort((a,b) => b-a);
// console.log('indices', ...indices, {zBottom, slicer});
if (op.outside && !op.inside) {
console.log({outline_bypass: indices, down: op.down});
// console.log({outline_bypass: indices, down: op.down});
indices.forEach((ind,i) => {
if (flats.indexOf(ind) >= 0) {
// exclude flats
@ -509,7 +523,7 @@
CAM.addDogbones(offset, toolDiam / 5);
}
if (process.camStockClipTo) {
if (process.camStockClipTo && stock.x && stock.y && stock.center) {
let rect = newPolygon().centerRectangle(stock.center, stock.x, stock.y);
offset = cutPolys([rect], offset, slice.z, true);
}
@ -534,8 +548,10 @@
let { polyEmit, poly2polyEmit, depthOutlinePath } = ops;
let { camOut, newLayer, printPoint } = ops;
let { settings, widget } = state;
let { process } = settings;
let { process, controller } = settings;
let danger = controller.danger;
let easeDown = process.camEaseDown;
let toolDiam = this.toolDiam;
let cutdir = process.camConventional;
let depthFirst = process.camDepthFirst;
@ -584,8 +600,10 @@
printPoint = flatLevels[0]
.sort((a,b) => { return a.area() - b.area() })[0]
.average();
printPoint = depthOutlinePath(printPoint, 0, flatLevels, toolDiam, polyEmit, false);
printPoint = depthOutlinePath(printPoint, 0, flatLevels, toolDiam, polyEmit, true);
// experimental start of ease down
let ease = danger && op.down && easeDown ? op.down : 0;
printPoint = depthOutlinePath(printPoint, 0, flatLevels, toolDiam, polyEmit, false, ease);
printPoint = depthOutlinePath(printPoint, 0, flatLevels, toolDiam, polyEmit, true, ease);
}
}
@ -629,7 +647,8 @@
let { settings, widget } = state;
let { process } = settings;
let toolDiam = this.toolDiam;
let toolDiam = this.toolDiam = new CAM.Tool(settings, op.tool).fluteDiameter();
let stepover = toolDiam * op.step * 2;
let cutdir = process.camConventional;
let depthFirst = process.camDepthFirst;
let depthData = [];
@ -656,7 +675,7 @@
poly.reverse();
}
poly.forEachPoint(function(point, pidx) {
camOut(point.clone(), pidx > 0);
camOut(point.clone(), pidx > 0, stepover);
}, false);
});
newLayer();
@ -670,7 +689,7 @@
poly.reverse();
}
poly.forEachPoint(function(point, pidx) {
camOut(point.clone(), pidx > 0);
camOut(point.clone(), pidx > 0, stepover);
}, false);
newLayer();
return lastPoint();
@ -700,7 +719,8 @@
let toolOver = toolDiam * op.step;
let cutdir = process.camConventional;
let polys = [];
let stockRect = newPolygon().centerRectangle(stock.center, stock.x, stock.y);
let stockRect = stock.center && stock.x && stock.y ?
newPolygon().centerRectangle(stock.center, stock.x, stock.y) : undefined;
updateToolDiams(toolDiam);
if (tabs) {
tabs.forEach(tab => {
@ -727,7 +747,7 @@
} else {
slice.camLines = [ poly ];
}
if (process.camStockClipTo) {
if (process.camStockClipTo && stockRect) {
slice.camLines = cutPolys([stockRect], slice.camLines, z, true);
}
slice.output()
@ -771,19 +791,22 @@
case "inside": offdist = -toolDiam / 2; break;
}
if (offdist) {
let pnew = POLY.offset([poly], offdist)[0];
let pnew = POLY.offset([poly], offdist);
if (pnew) {
poly = pnew.setZ(poly.getZ());
poly = POLY.setZ(pnew, poly.getZ());
} else {
continue;
}
} else {
poly = [ poly ];
}
for (let pi of poly)
if (down) {
for (let z of BASE.util.lerp(zMax, poly.getZ(), down)) {
followZ(poly.clone().setZ(z));
for (let z of BASE.util.lerp(zMax, pi.getZ(), down)) {
followZ(pi.clone().setZ(z));
}
} else {
followZ(poly);
followZ(pi);
}
}
break;
@ -904,6 +927,7 @@
updateToolDiams(tool.fluteDiameter());
let { stock } = settings,
tz = widget.track.pos.z,
lx = bounds.min.x,
hx = bounds.max.x,
ly = bounds.min.y,
@ -915,7 +939,10 @@
dx = (stock.x - (hx - lx)) / 4,
dy = (stock.y - (hy - ly)) / 4,
dz = stock.z,
points = [];
points = [],
wo = stock.z - bounds.max.z,
z1 = bounds.max.z + wo + tz,
z2 = tz - mz;
if (!(stock.x && stock.y && stock.z)) {
return;
@ -969,7 +996,7 @@
tv = -tv;
}
}
for (let z of BASE.util.lerp(zMax, -zThru, op.down)) {
for (let z of BASE.util.lerp(z1, z2, op.down)) {
let slice = newSlice(z);
sliceAll.push(slice);
sliceOut.push(slice);
@ -1005,8 +1032,8 @@
let slice = newSlice(0,null), polys = [];
points.forEach(point => {
polys.push(newPolygon()
.append(point.clone().setZ(bounds.max.z))
.append(point.clone().setZ(bounds.max.z - stock.z - mz)));
.append(point.clone().setZ(z1))
.append(point.clone().setZ(z2)));
});
slice.camLines = polys;
slice.output()
@ -1096,7 +1123,7 @@
let terrain = slicer.slice(tzindex, { each: (data, index, total) => {
tshadow = POLY.union(tshadow.slice().appendAll(data.tops), 0.01, true);
tslices.push(data.slice);
if (true) {
if (false) {
const slice = data.slice;
sliceAll.push(slice);
slice.output()

View file

@ -69,7 +69,7 @@
boundsz = bounds.max.z + ztOff,
zadd = hasStock ? stock.z - boundsz : alignTop ? outerz - boundsz : 0,
zmax = outerz + zclear,
wmpos = widget.mesh.position,
wmpos = widget.track.pos,
wmx = wmpos.x,
wmy = wmpos.y,
originx = startCenter ? 0 : hasStock ? -stock.x / 2 : bounds.min.x,
@ -231,7 +231,7 @@
);
}
function camOut(point, cut) {
function camOut(point, cut, moveLen = toolDiamMove) {
point = point.clone();
point.x += wmx;
point.y += wmy;
@ -264,7 +264,7 @@
}
// convert short planar moves to cuts in some cases
if (isMove && deltaXY <= toolDiamMove) {
if (isMove && deltaXY <= moveLen) {
let iscontour = tolerance > 0;
let isflat = absDeltaZ < 0.001;
// restrict this to contouring
@ -279,7 +279,7 @@
}
} else if (isMove) {
// for longer moves, check the terrain to see if we need to go up and over
const bigXY = (deltaXY > toolDiamMove);
const bigXY = (deltaXY > moveLen);
const bigZ = (deltaZ > toolDiam/2 && deltaXY > tolerance);
const midZ = (absDeltaZ >= tolerance);
if ((bigXY || bigZ) && (isMove || midZ)) {
@ -296,11 +296,8 @@
) + ztOff,
mustGoUp = Math.max(maxz - point.z, maxz - lastPoint.z) >= tolerance,
clearz = maxz;
// up if any point between higher than start/outline, go up
// up if any point between higher than start/outline, go up first
if (mustGoUp) {
if (bigXY) {
clearz += zclear;
}
layerPush(lastPoint.clone().setZ(clearz), 0, 0, tool.getNumber());
}
// move to point above target point
@ -414,7 +411,7 @@
return last;
}
function depthRoughPath(start, depth, levels, tops, emitter, fit) {
function depthRoughPath(start, depth, levels, tops, emitter, fit, ease) {
let level = levels[depth];
if (!level) {
return start;
@ -424,13 +421,19 @@
fitted.filter(top => !top.level_emit).forEach(top => {
top.level_emit = true;
let inside = level.filter(poly => poly.isInside(top));
if (ease) {
start.z += ease;
}
start = poly2polyEmit(inside, start, emitter, { mark: "emark", perm: true });
start = depthRoughPath(start, depth + 1, levels, tops, emitter, top);
if (ease) {
start.z += ease;
}
start = depthRoughPath(start, depth + 1, levels, tops, emitter, top, ease);
});
return start;
}
function depthOutlinePath(start, depth, levels, radius, emitter, clr) {
function depthOutlinePath(start, depth, levels, radius, emitter, clr, ease) {
let bottm = depth < levels.length - 1 ? levels[levels.length - 1] : null;
let above = levels[depth-1];
let level = levels[depth];
@ -458,8 +461,14 @@
// omit polys that match bottom level polys unless level above is cleared
start = poly2polyEmit(level, start, (poly, index, count, fromPoint) => {
poly.level_emit = true;
if (ease) {
fromPoint.z += ease;
}
fromPoint = polyEmit(poly, index, count, fromPoint);
fromPoint = depthOutlinePath(fromPoint, depth + 1, levels, radius, emitter, clr);
if (ease) {
fromPoint.z += ease;
}
fromPoint = depthOutlinePath(fromPoint, depth + 1, levels, radius, emitter, clr, ease);
return fromPoint;
}, {weight: true});
return start;
@ -479,8 +488,9 @@
* return tool Z clearance height for a line segment movement path
*/
function getZClearPath(terrain, x1, y1, x2, y2, z, zadd, off, over) {
// when terrain skipped, top + pass used
if (terrain > 0) {
return terrain + zadd + over;
return terrain;
}
let maxz = z;
let check = [];

View file

@ -221,8 +221,8 @@
}
};
CAM.traces = function(settings, widget) {
if (widget.traces) {
CAM.traces = function(settings, widget, single) {
if (widget.traces && widget.trace_single === single) {
// do no work if cached
return false;
}
@ -242,6 +242,13 @@
let traces = [];
// find and trim polys (including open) to shadow
let oneach = (data, index, total) => {
if (single) {
for (let line of data.lines) {
if (line.p1.distTo2D(line.p2) > 1) {
traces.push(newPolygon().append(line.p1).append(line.p2).setOpen());
}
}
} else
BASE.polygons.flatten(data.tops,null,true).forEach(poly => {
poly.inner = null;
poly.parent = null;
@ -265,6 +272,7 @@
opts.over = true;
slicer.slice(indices, opts);
widget.traces = traces;
widget.trace_single = single;
return true;
};

View file

@ -154,8 +154,8 @@
if (curvesOnly && lastP) {
// maxangle
const dz = Math.abs(lastP.z - z);
const dxy = Math.max(Math.abs(lastP.x - x), Math.abs(lastP.y - y));
const angle = Math.atan2(dz, dxy) * RAD2DEG;
const dv = contourX ? Math.abs(lastP.x - x) : Math.abs(lastP.y - y);
const angle = Math.atan2(dz, dv) * RAD2DEG;
// if (lastP.z < 0.1) console.log('pp', {dz, dxy, angle});
if (angle > maxangle) {
lastP = newP;

View file

@ -8,9 +8,12 @@
BASE = self.base,
UTIL = BASE.util,
FDM = KIRI.driver.FDM,
SPACE, API,
SPACE, API, VIEWS, LANG, PROC, UI, UC,
nextID = Date.now(),
p1, p2, iw,
lastMode, lastView, lastPillar,
lastBox,
lastMode, lastView,
lastPillar, fromPillar,
isFdmMode = false,
addingSupports = false,
alert = [],
@ -18,8 +21,133 @@
func = {};
FDM.init = function(kiri, api) {
UI = api.ui;
UC = api.uc;
API = api;
SPACE = api.const.SPACE;
VIEWS = api.const.VIEWS;
LANG = KIRI.lang.current;
PROC = Object.keys(kiri.conf.defaults.fdm.p);
let rangeVars = {
// slice
"sliceShells": LANG.sl_shel_s,
"sliceFillType": LANG.fi_type,
"sliceFillWidth": LANG.fi_wdth_s,
"sliceFillSparse": LANG.fi_pcnt_s,
"sliceSolidMinArea": LANG.ad_msol_s,
// prepare
"sliceShellOrder": LANG.sl_ordr_s,
"sliceFillOverlap": LANG.fi_over_s,
"outputFeedrate": LANG.ou_feed_s,
"outputFinishrate": LANG.ou_fini_s,
"outputShellMult": LANG.ou_shml_s,
"outputFillMult": LANG.ou_flml_s,
"outputSparseMult": LANG.ou_spml_s,
"outputRetractWipe": LANG.ad_wpln_s,
"outputShortPoly": LANG.ad_spol_s,
"outputMinSpeed": LANG.ad_mins_s,
"outputCoastDist": LANG.ad_scst_s,
"sliceSupportDensity": LANG.sp_dens_s,
"sliceSupportOffset": LANG.sp_offs_s,
"sliceLayerStart": LANG.sl_strt_s,
// export
"zHopDistance": LANG.ad_zhop_s,
"arcTolerance": LANG.ad_zhop_s,
"antiBacklash": LANG.ad_abkl_s,
"outputTemp": LANG.ou_nozl_s,
"outputBedTemp": LANG.ou_bedd_s,
"outputFanSpeed": LANG.ou_fans_s,
"outputRetractDist": LANG.ad_rdst_s,
"outputRetractSpeed": LANG.outputRetractSpeed,
"outputRetractDwell": LANG.ad_rdwl_s,
};
for (let key of Object.keys(rangeVars)) {
if (UI[key]) {
UI[key].range = true;
}
}
function filterSynth() {
api.widgets.filter((widget) => {
if (widget.track.synth) {
api.const.SPACE.platform.remove(widget.mesh);
kiri.Widget.Groups.remove(widget);
}
return !widget.track.synth
});
}
function updateRanges(ranges = []) {
UI.rangeGroup.style.display = isFdmMode && ranges && ranges.length ? 'flex' : 'none';
let html = [];
let bind = [];
let now = Date.now();
let sorted = ranges.sort((a,b) => b.lo - a.lo);
for (let range of sorted) {
let id = (now++).toString(36);
let rows = Object.entries(range.fields).map(a => `<div><label class="pad">${rangeVars[a[0]]}</label><span></span><label class="val">${a[1]}</label></div>`).join('');
let hover = `<div id="hov_${id}" class="range-detail">${rows}</div>`;
let info = `<button id="sel_${id}" class="j-center grow">${range.lo} - ${range.hi}</button><button id="del_${id}"><i class="far fa-trash-alt"></i></button>`;
html.appendAll([
`<div id="rng_${id}" class="range-info">${hover}${info}</div>`
]);
bind.push({id, range});
}
UI.rangeGroup.firstElementChild.innerHTML = html.join('');
for (let rec of bind) {
$(`sel_${rec.id}`).onclick = () => {
api.show.layer(rec.range.hi, rec.range.lo);
};
$(`del_${rec.id}`).onclick = () => {
let io = ranges.indexOf(rec.range);
ranges.splice(io,1);
updateRanges(ranges);
};
}
}
api.event.on("boolean.click", func.updateSupportButtons = () => {
if (!isFdmMode) {
return;
}
for (let btn of [
UI.ssaGen,
UI.ssmAdd,
UI.ssmDun,
UI.ssmClr
]) {
btn.disabled = UI.sliceSupportEnable.checked;
}
if (UI.sliceSupportEnable.checked) {
func.sclear();
}
});
api.event.on("function.animate", (mode) => {
if (!isFdmMode) {
return;
}
if (!API.conf.get().controller.danger) {
return;
}
let pct = 0;
let int = setInterval(function() {
if (pct <= 1) {
api.const.STACKS.setFraction(pct);
pct += 0.05;
} else {
api.const.STACKS.setFraction(1);
clearTimeout(int);
}
}, 50);
// let slider = $('top-slider');
// slider.style.display = 'flex';
// slider.oninput = slider.onchange = (ev) => {
// api.const.STACKS.setFraction(parseInt(ev.target.value)/100);
// };
});
api.event.on("mode.set", mode => {
isFdmMode = mode === 'FDM';
lastMode = mode;
@ -28,14 +156,36 @@
api.event.on("view.set", view => {
lastView = view;
updateVisiblity();
filterSynth();
func.sdone();
// let ranges = API.conf.get().process.ranges;
if (isFdmMode) {
if (lastView === VIEWS.SLICE) {
for (let key of PROC) {
if (UI[key] && !UI[key].range) {
UI[key].disabled = true;
}
}
} else {
for (let key of PROC) {
if (UI[key]) UI[key].disabled = false;
}
}
}
// UI.rangeGroup.style.display = ranges && ranges.length ? 'flex' : 'none';
});
api.event.on("range.updates", updateRanges);
api.event.on("settings.load", (settings) => {
if (settings.mode !== 'FDM') return;
settings.process.outputOriginCenter = (settings.device.originCenter || false);
restoreSupports(api.widgets.all());
updateRanges(settings.process.ranges);
});
api.event.on("settings.saved", (settings) => {
api.ui.fdmSupport.style.display = lastMode !== 'FDM' || settings.device.bedBelt ? 'none' : 'flex';
updateRanges(settings.process.ranges);
func.updateSupportButtons();
// let ranges = settings.process.ranges;
// UI.rangeGroup.style.display = isFdmMode && ranges && ranges.length ? 'flex' : 'none';
});
api.event.on("button.click", target => {
switch (target) {
@ -43,9 +193,10 @@
case api.ui.ssmAdd: return func.sadd();
case api.ui.ssmDun: return func.sdone();
case api.ui.ssmClr:
return api.uc.confirm("clear supports?").then(ok => {
if (ok) func.sclear();
});
// return api.uc.confirm("clear supports?").then(ok => {
// if (ok) func.sclear();
// });
func.sclear();
}
});
api.event.on("fdm.supports.detect", func.sgen = () => {
@ -86,11 +237,13 @@
delbox('supp');
api.hide.alert(alert);
api.feature.hover = addingSupports = false;
fromPillar = undefined;
});
api.event.on("fdm.supports.clear", func.sclear = () => {
func.sdone();
clearAllWidgetSupports();
API.conf.save();
if (clearAllWidgetSupports()) {
API.conf.save();
}
});
api.event.on("slice.begin", () => {
if (!isFdmMode) {
@ -98,6 +251,37 @@
}
func.sdone();
updateVisiblity();
// synth support widget for each widget group
let synth = [];
for (let group of kiri.Widget.Groups.list()) {
let merge = group.filter(w => w.sups).map(w => Object.values(w.sups)).flat();
if (!merge.length) {
continue;
}
let boxen = merge.map(m => {
let geo = m.box.geometry.clone();
if (geo.index) geo = geo.toNonIndexed();
return geo.translate(m.x, m.y, m.z);
});
for (let box of boxen) {
// eliminate / normalize uv to allow other widget merge
box.setAttribute('uv',new THREE.BufferAttribute(new Float32Array(0), 3));
}
let bbg = THREE.BufferGeometryUtils.mergeBufferGeometries(boxen);
let sw = kiri.newWidget(null, group);
let fwp = group[0].track.pos;
sw.loadGeometry(bbg);
sw._move(fwp.x, fwp.y, fwp.z);
api.widgets.add(sw);
sw.track.synth = true;
api.const.SPACE.platform.add(sw.mesh);
}
});
api.event.on("slice.end", () => {
if (!isFdmMode) {
return;
}
});
api.event.on("key.esc", () => {
if (!isFdmMode) {
@ -132,8 +316,8 @@
});
}
});
api.event.on("mouse.hover.up", on => {
let { object, event } = on;
api.event.on("mouse.hover.up", func.hoverup = (on = {}) => {
let { event } = on;
if (!isFdmMode) {
return;
}
@ -142,34 +326,77 @@
}
delbox('supp');
if (lastPillar) {
const {widget, box, id} = lastPillar;
widget.adds.remove(box);
widget.mesh.remove(box);
delete widget.sups[id];
let sa = API.widgets.annotate(widget.id).support;
let ix = 0;
sa.forEach((rec,i) => {
if (rec.id === id) {
ix = i;
}
});
sa.splice(ix,1);
API.conf.save();
removeWidgetSupport(lastPillar.widget, lastPillar);
return;
}
if (!iw) return;
let hy = (p1.y + p2.y) / 2;
let dh = Math.abs(p1.y - p2.y);
let { point, dim } = lastBox;
p1.y = Math.max(0, p1.y);
p2.y = Math.max(0, p2.y);
let rz = dim.rz;
let dh = dim.z;
let dw = api.conf.get().process.sliceSupportSize / 2;
let ip = iw.track.pos;
let wa = api.widgets.annotate(iw.id);
let ws = (wa.support = wa.support || []);
let x = p1.x - ip.x, y = -p1.z - ip.y, z = hy, id = Date.now();
let rec = {x, y, z, dw, dh, id};
let id = ++nextID;
let rec = {x: point.x - ip.x, y: -point.z - ip.y, z: point.y, rz, dw, dh, id};
if (fromPillar && event && event.shiftKey) {
let targets = api.widgets.meshes().append(SPACE.internals().platform);
let from = fromPillar,
d = {
x: rec.x - from.x,
y: rec.y - from.y,
z: rec.z - from.z
};
let dist = Math.sqrt(d.x*d.x + d.y*d.y + d.z*d.z);
let lerp = UTIL.lerp(0,dist,dw).map(v => v/dist);
let angle = rec.rz = Math.atan2(d.y,d.x);
let fromrec = ws.filter(r => r.id === from.id)[0];
if (fromrec) {
from.rz = fromrec.rz = angle;
removeWidgetSupport(iw, fromPillar);
ws.push(Object.clone(fromrec));
addWidgetSupport(iw, fromrec);
}
lerp.pop();
let seed = Math.random();
for (let pct of lerp) {
let point = new THREE.Vector3(from.x + d.x * pct, from.z + d.z * pct, from.y + d.y * pct);
addSupportAtPoint(targets, point, ip, angle, seed++);
}
}
ws.push(Object.clone(rec));
addWidgetSupport(iw, rec);
fromPillar = addWidgetSupport(iw, rec);
API.conf.save();
});
function addSupportAtPoint(targets, point, ip, angle, id) {
let up = new THREE.Vector3(0,1,0)
let dn = new THREE.Vector3(0,-1,0)
let rp = new THREE.Vector3(point.x + ip.x, point.y, -point.z - ip.y);
let iup = new THREE.Raycaster(rp, up)
.intersectObjects(targets, false)
.filter(t => !t.object.pillar);
if (iup.length && iup.length % 2 === 0) {
let idn = new THREE.Raycaster(rp, dn)
.intersectObjects(targets, false)
.filter(t => !t.object.pillar);
if (idn.length && idn.length % 2 === 0) {
iw = iup[0].object.widget || iw;
let wa = api.widgets.annotate(iw.id);
let ws = (wa.support = wa.support || []);
let phi = iup[0].point;
let plo = idn[0].point;
let ply = Math.max(0, plo.y);
let mp = (phi.y + ply) / 2;
let dy = Math.abs(phi.y - ply);
let dw = api.conf.get().process.sliceSupportSize / 2;
let rec = { rz:angle, x:point.x, y:point.z, z:mp, dw, dh:dy, id };
ws.push(Object.clone(rec));
fromPillar = addWidgetSupport(iw, rec);
}
}
}
api.event.on("mouse.hover", data => {
if (!isFdmMode) {
return;
@ -177,11 +404,8 @@
if (!addingSupports) {
return;
}
// delbox('intZ');
// delbox('intW');
// addbox(point, 0xff0000, 'intZ');
delbox('supp');
const { int, type, point } = data;
const { int, type, point, event } = data;
const pillar = int ? int.object.pillar : undefined;
if (lastPillar) {
lastPillar.box.material.color.r = 0;
@ -190,6 +414,9 @@
if (pillar) {
pillar.box.material.color.r = 0.5;
lastPillar = pillar;
if (event && (event.metaKey || event.ctrlKey)) {
return func.hoverup();
}
return;
}
if (int && type === 'widget') {
@ -200,30 +427,29 @@
p1 = point;
let dir = new THREE.Vector3(0,1,0)
let ray = new THREE.Raycaster(point, dir);
let rz = int && int.face ? Math.atan2(int.face.normal.y, int.face.normal.x) : 0;
// when on object, project down on downward faces
if (int && int.face && int.face.normal.z < -0.1) {
dir.y = -1;
}
let targets = api.widgets.meshes()
.append(SPACE.internals().platform)
.appendAll(activeSupports())
;
let i2 = ray.intersectObjects(targets, false);
if (i2 && i2.length > 0) {
// prevent false matches close to origin of ray
i2 = i2.filter(i => i.distance > 0.01);
// prevent single point base to top matches
if (i2.length > 1) {
p2 = i2[0].point;
iw = i2[0].object.widget || iw;
let hy = (p1.y + p2.y) / 2;
let dy = Math.abs(p1.y - p2.y);
let dw = api.conf.get().process.sliceSupportSize / 2;
// addbox(p2, 0x00ff00, 'intW');
addbox({x:p1.x, y:hy, z:p1.z}, 0x0000dd, 'supp', {
x:dw, y:dw, z:dy
});
}
let targets = api.widgets.meshes().append(SPACE.internals().platform);
let i2 = ray.intersectObjects(targets, false)
.filter(t => !t.object.pillar) // eliminate other pillars
.filter(i => i.distance > 0.1); // false matches close to origin of ray
if (i2.length && i2.length % 2 === 0) {
p2 = i2[0].point;
iw = i2[0].object.widget || iw;
let p1y = Math.max(0, p1.y);
let p2y = Math.max(0, p2.y);
let hy = (p1y + p2y) / 2;
let dy = Math.abs(p1y - p2y);
let dw = api.conf.get().process.sliceSupportSize / 2;
addbox({x:p1.x, y:hy, z:p1.z}, 0x0000dd, 'supp', {
x:dw, y:dw, z:dy, rz
});
}
if (event && event.altKey) {
return func.hoverup();
}
});
}
@ -234,7 +460,6 @@
Object.values(widget.sups || {}).forEach(support => {
active.push(support.box);
support.box.support = true;
// console.log({support});
});
});
return active;
@ -250,23 +475,41 @@
}
function addWidgetSupport(widget, pos) {
const { x, y, z, dw, dh, id } = pos;
const { x, y, z, rz, dw, dh, id } = pos;
const sups = widget.sups = (widget.sups || {});
// prevent duplicate restore from repeated settings load calls
if (!sups[id]) {
pos.box = addbox(
{ x, y, z }, 0x0000dd, id,
{ x:dw, y:dw, z:dh }, { group: widget.mesh }
{ x:dw, y:dw, z:dh, rz }, { group: widget.mesh }
);
pos.box.pillar = Object.assign({widget}, pos);
sups[id] = pos;
widget.adds.push(pos.box);
}
return sups[id];
}
function removeWidgetSupport(widget, rec) {
const {box, id} = rec;
widget.adds.remove(box);
widget.mesh.remove(box);
delete widget.sups[id];
let sa = API.widgets.annotate(widget.id).support;
let ix = 0;
sa.forEach((rec,i) => {
if (rec.id === id) {
ix = i;
}
});
sa.splice(ix,1);
API.conf.save();
fromPillar = undefined;
}
function updateVisiblity() {
API.widgets.all().forEach(w => {
setSupportVisiblity(w, lastMode === 'FDM' && lastView === API.const.VIEWS.ARRANGE);
setSupportVisiblity(w, lastMode === 'FDM' && lastView === VIEWS.ARRANGE);
});
}
@ -277,18 +520,23 @@
}
function clearAllWidgetSupports() {
let cleared = 0;
API.widgets.all().forEach(widget => {
clearWidgetSupports(widget);
cleared += clearWidgetSupports(widget);
});
return cleared;
}
function clearWidgetSupports(widget) {
let cleared = 0;
Object.values(widget.sups || {}).forEach(support => {
widget.adds.remove(support.box);
widget.mesh.remove(support.box);
cleared++;
});
widget.sups = {};
delete API.widgets.annotate(widget.id).support;
return cleared;
}
function delbox(name) {
@ -298,7 +546,7 @@
}
}
function addbox(point, color, name, dim = {x:1,y:1,z:1}, opt = {}) {
function addbox(point, color, name, dim = {x:1,y:1,z:1,rz:0}, opt = {}) {
delbox(name);
const box = boxes[name] = new THREE.Mesh(
new THREE.BoxGeometry(dim.x, dim.y, dim.z),
@ -312,10 +560,15 @@
box.position.y = point.y;
box.position.z = point.z;
lastBox = {point, dim};
const group = opt.group || SPACE.scene
group.add(box);
box.groupTo = group;
if (dim.rz) {
opt.rotate = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,0,1), dim.rz);
}
if (opt.rotate) {
opt.matrix = new THREE.Matrix4().makeRotationFromQuaternion(opt.rotate);
}

View file

@ -14,6 +14,7 @@
// slice, // src/mode/fdm/slice.js
// prepare, // src/mode/fdm/prepare.js
// export, // src/mode/fdm/export.js
getRangeParameters,
fixExtruders
};
@ -28,10 +29,25 @@
return settings;
}
// customer gcode post function for XYZ daVinci Mini W
self.kiri_fdm_xyz_mini_w = function(gcode, options) {
return btoa("; filename = kirimoto.gcode\n; machine = dv1MW0A000\n" + gcode);
};
function getRangeParameters(settings, index) {
if (index === undefined || index === null || index < 0) {
return settings.process;
}
let ranges = settings.process.ranges;
if (!(ranges && ranges.length)) {
return settings.process;
}
let params = Object.clone(settings.process);
for (let range of ranges) {
if (index >= range.lo && index <= range.hi) {
for (let [key,value] of Object.entries(range.fields)) {
params[key] = value;
params._range = true;
}
}
}
return params;
}
// defer loading until KIRI.client and KIRI.worker exist
KIRI.loader.push(function(API) {
@ -39,6 +55,7 @@
if (KIRI.client)
// FDM.support_generate = KIRI.client.fdm_support_generate = function(ondone) {
FDM.support_generate = function(ondone) {
KIRI.client.clear();
KIRI.client.sync();
let settings = API.conf.get();
let widgets = API.widgets.map();
@ -51,7 +68,7 @@
if (KIRI.worker)
KIRI.worker.fdm_support_generate = function(data, send) {
const { settings } = data;
const widgets = Object.values(cache);
const widgets = Object.values(wcache);
const fresh = widgets.filter(widget => FDM.supports(settings, widget));
send.done(KIRI.codec.encode(fresh.map(widget => { return {
id: widget.id,

View file

@ -15,25 +15,28 @@
FDM.export = function(print, online) {
let layers = print.output,
settings = FDM.fixExtruders(print.settings),
getRangeParameters = FDM.getRangeParameters,
device = settings.device,
extruders = device.extruders,
process = settings.process,
gcodeFan = device.gcodeFan,
gcodeLayer = device.gcodeLayer,
gcodeTrack = device.gcodeTrack,
tool = 0,
fwRetract = device.fwRetract,
isDanger = settings.controller.danger,
isBelt = device.bedBelt,
bedType = isBelt ? "belt" : "fixed",
extruder = extruders[tool],
offset_x = extruder.extOffsetX,
offset_y = extruder.extOffsetY,
extrudeAbs = device.extrudeAbs,
extrudeAbs = device.extrudeAbs || false,
extrudeSet = false,
time = 0,
layer = 0,
pause = [],
pauseCmd = device.gcodePause,
output = [],
outputLength = 0,
outputLength = 0, // absolute extruder position
lastProgress = 0,
decimals = BASE.config.gcode_decimals || 4,
progress = 0,
@ -44,26 +47,39 @@
lout = null,
last = null,
zpos = 0,
zhop = process.zHopDistance || 0,
bmax = 0,
blast = 0,
blastz = 0,
process = settings.process,
loops = process.outputLoopLayers,
zhop = process.zHopDistance || 0, // range
seekMMM = process.outputSeekrate * 60,
retDist = process.outputRetractDist,
retSpeed = process.outputRetractSpeed * 60,
retDwell = process.outputRetractDwell || 0,
retDist = process.outputRetractDist || 0, // range
retSpeed = process.outputRetractSpeed * 60, // range
retDwell = process.outputRetractDwell || 0, // range
timeDwell = retDwell / 1000,
peelGuard = process.outputPeelGuard || 0,
arcDist = isBelt || !isDanger ? 0 : (process.arcTolerance || 0),
originCenter = process.outputOriginCenter,
offset = originCenter ? null : {
x: device.bedWidth/2,
y: isBelt ? 0 : device.bedDepth/2
},
nozzleTemp = process.firstLayerNozzleTemp || process.outputTemp,
bedTemp = process.firstLayerBedTemp || process.outputBedTemp,
fanSpeed = undefined,
lastNozzleTemp = nozzleTemp,
lastBedTemp = bedTemp,
lastFanSpeed = fanSpeed,
subst = {
travel_speed: seekMMM,
retract_speed: retSpeed,
retract_distance: retDist,
temp: process.firstLayerNozzleTemp || process.outputTemp,
temp_bed: process.firstLayerBedTemp || process.outputBedTemp,
bed_temp: process.firstLayerBedTemp || process.outputBedTemp,
fan_speed: process.outputFanMax,
speed: process.outputFanMax, // legacy
temp: nozzleTemp,
temp_bed: bedTemp,
bed_temp: bedTemp,
fan_speed: fanSpeed,
speed: fanSpeed, // legacy
top: offset ? device.bedDepth : device.bedDepth/2,
left: offset ? 0 : -device.bedWidth/2,
right: offset ? device.bedWidth : device.bedWidth/2,
@ -79,8 +95,38 @@
bytes = 0,
bcos = Math.cos(Math.PI/4),
icos = 1 / bcos,
loops = process.outputLoopLayers,
inLoop;
inLoop,
arcQ = [];
// smallish band-aid. refactor above to remove redundancy
function updateParams(layer) {
let process = getRangeParameters(settings, layer);
zhop = process.zHopDistance || 0; // range
retDist = process.outputRetractDist || 0; // range
retSpeed = process.outputRetractSpeed * 60; // range
retDwell = process.outputRetractDwell || 0; // range
timeDwell = retDwell / 1000;
nozzleTemp = layer === 0 ?
process.firstLayerNozzleTemp || process.outputTemp :
process.outputTemp || process.firstLayerNozzleTemp;
bedTemp = layer === 0 ?
process.firstLayerBedTemp || process.outputBedTemp :
process.outputBedTemp || process.firstLayerBedTemp;
fanSpeed = layer === 0 ?
process.firstLayerFanSpeed || 0 :
process.outputFanSpeed || 0;
Object.assign(subst, {
temp_bed: bedTemp,
bed_temp: bedTemp,
fan_speed: fanSpeed,
speed: fanSpeed, // legacy
retract_speed: retSpeed,
retract_distance: retDist,
temp: process.outputTemp, // range
temp_bed: process.outputBedTemp, // range
bed_temp: process.outputBedTemp, // range
});
}
if (isBelt && loops) {
loops = loops.split(',').map(range => {
@ -116,8 +162,12 @@
}
};
function appendSub(line) {
append(print.constReplace(line, subst));
function appendSubPad(line, pad) {
appendSub(line, true);
}
function appendSub(line, pad) {
append(print.constReplace(line, subst, 0, pad));
}
function appendAll(arr) {
@ -126,10 +176,10 @@
arr.forEach(function(line) { append(line) });
}
function appendAllSub(arr) {
if (!arr) return;
function appendAllSub(arr, pad) {
if (!arr || arr.length === 0) return;
if (!Array.isArray(arr)) arr = [ arr ];
arr.forEach(function(line) { appendSub(line) });
arr.forEach(function(line) { appendSub(line, pad) });
}
append(`; Generated by Kiri:Moto ${KIRI.version}`);
@ -146,12 +196,18 @@
let t1 = false;
for (let i=0; i<device.gcodePre.length; i++) {
let line = device.gcodePre[i];
if (line.indexOf('T0') >= 0) t0 = true; else
if (line.indexOf('T1') >= 0) t1 = true; else
if (line.indexOf('M82') >= 0) extrudeAbs = true; else
if (line.indexOf('M83') >= 0) extrudeAbs = false; else
if (line.indexOf('G90') >= 0) extrudeAbs = true; else
if (line.indexOf('G91') >= 0) extrudeAbs = false; else
if (line.indexOf('T0') === 0) t0 = true; else
if (line.indexOf('T1') === 0) t1 = true; else
if (line.indexOf('M82') === 0) {
extrudeAbs = true;
extrudeSet = true;
} else
if (line.indexOf('M83') === 0) {
extrudeAbs = false;
extrudeSet = true;
} else
if (line.indexOf('G90') === 0 && !extrudeSet) extrudeAbs = true; else
if (line.indexOf('G91') === 0 && !extrudeSet) extrudeAbs = false; else
if (line.indexOf('G92') === 0) {
line.split(";")[0].split(' ').forEach(function (tok) {
let val = parseFloat(tok.substring(1) || 0) || 0;
@ -174,11 +230,11 @@
if (line.indexOf("{tool}") > 0 && extruders.length > 1) {
for (let i=0; i<extruders.length; i++) {
subst.tool = i;
appendSub(line);
appendSubPad(line);
}
subst.tool = 0;
} else {
appendSub(line);
appendSubPad(line);
}
}
@ -188,8 +244,16 @@
}
function retract(zhop) {
if (retracted) {
// console.log({double_retract: zhop});
return;
}
retracted = retDist;
moveTo({e:-retracted}, retSpeed, `e-retract ${retDist}`);
if (fwRetract) {
append('G10');
} else {
moveTo({e:-retracted}, retSpeed, `e-retract ${retDist}`);
}
if (zhop) moveTo({z:zpos + zhop}, seekMMM, "z-hop start");
time += (retDist / retSpeed) * 60 * 2; // retraction time
}
@ -199,9 +263,6 @@
let angle = -Math.PI / 4;
function moveTo(newpos, rate, comment) {
if (comment) {
append(";; " + comment);
}
let o = [!rate && !newpos.e ? 'G0' : 'G1'];
let emit = { x: false, y: false, z: false };
if (typeof newpos.x === 'number' && newpos.x !== pos.x) {
@ -221,7 +282,7 @@
let epos = isBelt ? { x: pos.x, y: pos.y, z: pos.z } : pos;
if (isBelt) {
epos.x = originCenter ? -pos.x : device.bedWidth - pos.x;
epos.z = pos.z * icos;
epos.z = blastz = pos.z * icos;
epos.y = -pos.y + epos.z * bcos;
lout = epos;
}
@ -241,6 +302,13 @@
o.append(" F").append(Math.round(rate));
pos.f = rate
}
if (comment) {
o.append(` ; ${comment}`);
}
if (o.length === 1) {
// console.trace({no_move: o, out, newpos, pos, lastp, emit});
return;
}
let line = o.join('');
if (last == line) {
// console.log({dup:line});
@ -264,20 +332,33 @@
while (layer < layers.length) {
path = layers[layer];
// range overrides
if (path.layer >= 0) {
updateParams(path.layer);
}
emitPerMM = print.extrudePerMM(
extruder.extNozzle,
extruder.extFilament,
path.layer === 0 ?
(process.firstSliceHeight || process.sliceHeight) : path.height);
subst.z = zpos = path.z;
subst.Z = subst.z;
zpos = path.z || zpos;
bmax = Math.max(bmax, pos.z * icos);
subst.z = subst.Z = zpos.round(3);
subst.e = subst.E = outputLength;
subst.layer = layer;
subst.height = path.height.toFixed(3);
if (isBelt) {
pos.z = zpos;
if (peelGuard && bmax > peelGuard && blast < peelGuard) {
peelGuard += 50;
append(`G0 Z${(blast + 55).round(decimals)} F200 ; peel guard`);
append(`G0 Z${blastz.round(decimals)} F200 ; unpeel`);
}
blast = bmax;
}
if (pauseCmd && pause.indexOf(layer) >= 0) {
@ -309,31 +390,23 @@
if (gcodeLayer && gcodeLayer.length) {
appendAllSub(gcodeLayer);
} else {
append(`;; --- layer ${layer} (${subst.height} @ ${subst.z}) ---`);
append(`;; --- layer ${layer} (${subst.height} @ ${subst.z.round(3)}) ---`);
}
if (layer > 0 && process.outputLayerRetract) {
retract();
}
// enable fan at fan layer
if (gcodeFan && layer === process.outputFanLayer) {
// layer temp and fan overrides at layer changes
if (fanSpeed !== lastFanSpeed) {
appendAllSub(gcodeFan);
lastFanSpeed = fanSpeed;
}
// second layer transitions
if (layer === 1) {
// update temps when first layer overrides are present
if (process.firstLayerNozzleTemp) {
subst.temp = process.outputTemp;
if (t0) appendSub("M104 S{temp} T0");
if (t1) appendSub("M104 S{temp} T1");
if (!(t0 || t1)) appendSub("M104 S{temp} T{tool}");
}
if (process.firstLayerBedTemp) {
subst.bed_temp = subst.temp_bed = process.outputBedTemp;
appendSub("M140 S{temp_bed} T0");
}
if (bedTemp !== lastBedTemp) {
append(`M140 S${bedTemp} T0`);
lastBedTemp = bedTemp;
}
if (nozzleTemp !== lastNozzleTemp) {
if (t0) append(`M104 S${nozzleTemp} T0`);
if (t1) append(`M104 S${nozzleTemp} T1`);
if (!(t0 || t1)) append(`M104 S${nozzleTemp} T${tool}`);
lastNozzleTemp = nozzleTemp;
}
// move Z to layer height
@ -344,10 +417,11 @@
// iterate through layer outputs
for (pidx=0; pidx<path.length; pidx++) {
out = path[pidx];
speedMMM = (out.speed || process.outputFeedrate) * 60;
speedMMM = (out.speed || process.outputFeedrate) * 60; // range
// look for extruder change and recalc emit factor
// look for extruder change, run scripts, recalc emit factor
if (out.tool !== undefined && out.tool !== tool) {
appendAllSub(extruder.extDeselect);
tool = out.tool;
subst.nozzle = subst.tool = tool;
extruder = extruders[tool];
@ -383,10 +457,16 @@
// re-engage post-retraction before new extrusion
if (out.emit && retracted) {
drainQ();
// console.log({engage:zhop});
// when enabled, resume previous Z
if (zhop && pos.z != zpos) moveTo({z:zpos}, seekMMM, "z-hop end");
// re-engage retracted filament
moveTo({e:retracted}, retSpeed, `e-engage ${retracted}`);
if (fwRetract) {
append('G11');
} else {
moveTo({e:retracted}, retSpeed, `e-engage ${retracted}`);
}
retracted = 0;
// optional dwell after re-engaging filament to allow pressure to build
if (retDwell) dwell(retDwell);
@ -394,10 +474,58 @@
}
if (lastp && out.emit) {
emitMM = emitPerMM * out.emit * dist;
moveTo({x:x, y:y, e:emitMM}, speedMMM);
emitted += emitMM;
if (arcDist) {
let rec = {e:out.emit, x, y, z, dist, emitPerMM, speedMMM};
arcQ.push(rec);
let deem = false;
let depm = false;
let desp = false;
if (arcQ.length > 1) {
deem = arcQ[0].e !== rec.e;
depm = arcQ[0].emitPerMM !== rec.emitPerMM;
desp = arcQ[0].speedMMM !== rec.speedMMM;
}
if (arcQ.length > 2) {
let el = arcQ.length;
let e1 = arcQ[el-3];
let e2 = arcQ[el-2];
let e3 = arcQ[el-1];
let cc = BASE.util.center2d(e1, e2, e3, 1);
let dc = 0;
if (arcQ.length === 3) {
arcQ.center = [ cc ];
} else {
// check center point delta
let dx = cc.x - arcQ.center[0].x;
let dy = cc.y - arcQ.center[0].y;
dc = Math.sqrt(dx * dx + dy * dy);
}
// if new point is off the arc
if (deem || depm || desp || dc > arcDist) {
// console.log({dc, depm, desp});
if (arcQ.length === 4) {
// not enough points for an arc, drop first point and recalc center
emitQrec(arcQ.shift());
arcQ.center = [ BASE.util.center2d(arcQ[0], arcQ[1], arcQ[2], 1) ];
} else {
// enough to consider an arc, emit and start new arc
let defer = arcQ.pop();
drainQ();
// re-add point that was off the last arc
arcQ.push(defer);
}
} else {
// new point is on the arc
arcQ.center.push(cc);
}
}
} else {
emitMM = emitPerMM * out.emit * dist;
moveTo({x:x, y:y, e:emitMM}, speedMMM);
emitted += emitMM;
}
} else {
drainQ();
moveTo({x:x, y:y}, seekMMM);
// TODO disabling out of plane z moves until a better mechanism
// can be built that doesn't rely on computed zpos from layer heights...
@ -407,7 +535,8 @@
}
// retract filament if point retract flag set
if (!retracted && out.retract) {
if (out.retract) {
drainQ();
retract(zhop);
}
@ -427,13 +556,72 @@
}
layer++;
}
drainQ();
function emitQrec(rec) {
let {e, x, y, dist, emitPerMM, speedMMM} = rec;
emitMM = emitPerMM * e * dist;
moveTo({x:x, y:y, e:emitMM}, speedMMM);
emitted += emitMM;
}
function drainQ() {
if (!arcDist) {
return;
}
if (arcQ.length > 4) {
let area = BASE.newPolygon().addObj(arcQ).area();
let from = arcQ[0];
let to = arcQ.peek();
let cc = {x:0, y:0, z:0, r:0};
let cl = 0;
for (let center of arcQ.center.filter(rec => !isNaN(rec.r))) {
cc.x += center.x;
cc.y += center.y;
cc.z += center.z;
cc.r += center.r;
cl++;
}
cc.x /= cl;
cc.y /= cl;
cc.z /= cl;
cc.r /= cl;
// first arc point
emitQrec(from);
// console.log(arcQ.slice(), arcQ.center);
// console.log({first: from, last: arcQ.peek(), center: cc});
// rest of arc to final point
let dist = arcQ.slice(1).map(v => v.dist).reduce((a,v) => a+v);
let emit = from.e;//arcQ.slice(1).map(v => v.e).reduce((a,v) => a+v);
emit = (from.emitPerMM * emit * dist);
outputLength += emit;
emitted += emit;
if (extrudeAbs) {
emit = outputLength;
}
let gc = area > 0 ? 'G2' : 'G3';
let pre = `${gc} X${to.x.toFixed(decimals)} Y${to.y.toFixed(decimals)} R${cc.r.toFixed(decimals)} E${emit.toFixed(decimals)}`;
let add = pos.f !== from.speedMMM ? ` E${from.speedMMM}` : '';
append(`${pre}${add} ; merged=${cl-1} len=${dist.toFixed(decimals)} cp=${cc.x.round(2)},${cc.y.round(2)}`);
pos.x = to.x;
pos.y = to.y;
pos.z = to.z;
} else {
for (let rec of arcQ) {
emitQrec(rec);
}
}
arcQ.length = 0;
arcQ.center = undefined;
}
if (inLoop) {
append(`M808`);
}
subst.time = UTIL.round(time,2);
subst.material = UTIL.round(emitted,2);
subst.time = UTIL.round(time,2);
subst['print-time'] = subst.time;
append("; --- shutdown ---");
appendAllSub(device.gcodePost);

View file

@ -155,25 +155,28 @@
function fillLinear(target) {
let bounds = target.bounds();
let height = target.zHeight();
let density = target.density();
let line = target.lineWidth();
let span_x = bounds.max.x - bounds.min.x;
let span_y = bounds.max.y - bounds.min.y;
let density = target.density();
let offset = target.offset() / 2;
let tile = 1 + (1 - density);
let tile_xc = Math.ceil(span_x / tile);
let tile_yc = Math.ceil(span_y / tile);
let steps_x = (span_x / line) * density;
let steps_y = (span_y / line) * density;
let step_x = span_x / steps_x;
let step_y = span_x / steps_x;
if (target.zIndex() % 2 === 1) {
for (let tx=0; tx<=tile_xc; tx++) {
for (let tx=bounds.min.x; tx<=bounds.max.x; tx += step_x) {
target.newline();
target.emit(tx * tile + bounds.min.x, bounds.min.y);
target.emit(tx * tile + bounds.min.x, bounds.max.y);
target.emit(tx, bounds.min.y);
target.emit(tx, bounds.max.y);
}
} else {
for (let ty=0; ty<=tile_yc; ty++) {
for (let ty=bounds.min.y; ty<=bounds.max.y; ty += step_y) {
target.newline();
target.emit(bounds.min.x, ty * tile + bounds.min.y);
target.emit(bounds.max.x, ty * tile + bounds.min.y);
target.emit(bounds.min.x, ty);
target.emit(bounds.max.x, ty);
}
}
}

View file

@ -10,7 +10,8 @@
POLY = BASE.polygons,
FDM = KIRI.driver.FDM,
newPoint = BASE.newPoint,
newPolygon = BASE.newPolygon;
newPolygon = BASE.newPolygon,
getRangeParameters = FDM.getRangeParameters;
/**
* DRIVER PRINT CONTRACT
@ -19,34 +20,31 @@
* @returns {Object[]} returns array of render objects
*/
FDM.prepare = function(widgets, settings, update) {
settings = FDM.fixExtruders(settings);
// filter ignored widgets
widgets = widgets.filter(w => !w.track.ignore);
settings = FDM.fixExtruders(settings);
let render = settings.render !== false,
{ device, process, controller, bounds, mode } = settings,
{ bedWidth, bedDepth } = device,
output = [],
printPoint = newPoint(0,0,0),
nozzle = device.extruders[0].extNozzle,
isBelt = device.bedBelt,
isThin = controller.lineType === "line",
isFlat = controller.lineType === "flat",
isDanger = controller.danger || false,
firstLayerHeight = isBelt ? process.sliceHeight : process.firstSliceHeight || process.sliceHeight,
firstLayerSeek = process.outputSeekrate,
firstLayerRate = process.firstLayerRate,
firstLayerMult = process.firstLayerPrintMult,
firstLayerBrim = process.firstLayerBrim,
firstLayerBrimTrig = process.firstLayerBrimTrig,
maxLayers = 0,
layer = 0,
layerRetract = process.outputLayerRetract,
layerno = 0,
zoff = 0,
meshIndex,
lastIndex,
closest,
mindist,
minidx,
find,
layerout = [],
slices = [],
sliceEntry,
print = self.worker.print = KIRI.newPrint(settings, widgets),
beltYoff = device.bedDepth / 2,
beltfact = Math.cos(Math.PI/4),
@ -75,7 +73,7 @@
offset = process.outputBrimOffset || (process.outputRaft ? 4 : 0);
// compute first brim
widgets.forEach(function(widget) {
widgets.filter(w => w.slices.length).forEach(function(widget) {
let tops = [];
// collect top outer polygons
widget.slices[0].tops.forEach(function(top) {
@ -88,15 +86,16 @@
});
// nest and offset tops
POLY.nest(tops).forEach(function(poly) {
poly.offset(-offset + nozzle / 2).forEach(function(brim) {
brim.move(widget.mesh.position);
let off = poly.offset(-offset + nozzle / 2);
if (off) off.forEach(function(brim) {
brim.move(widget.track.pos);
brims.push(brim);
});
});
});
// merge brims
brims = POLY.union(brims);
brims = POLY.union(brims, undefined, true);
// if brim is offset, over-expand then shrink to induce brims to merge
if (offset && brims.length) {
@ -132,6 +131,7 @@
speed: speed,
mult: extrude,
});
layerout.z = zoff + height;
layerout.height = height;
output.append(layerout);
@ -139,14 +139,15 @@
zoff += height;
};
raft(nozzle/1, process.sliceFillAngle + 0 , nozzle * 6.0, firstLayerRate / 3, 4);
raft(nozzle/1, process.sliceFillAngle + 0 , nozzle * 6.0, firstLayerRate / 2, 4);
raft(nozzle/1, process.sliceFillAngle + 0 , nozzle * 5.0, firstLayerRate / 3, 4);
raft(nozzle/1, process.sliceFillAngle + 0 , nozzle * 5.0, firstLayerRate / 2, 4);
raft(nozzle/2, process.sliceFillAngle + 90, nozzle * 3.0, process.outputFeedrate, 2.5);
raft(nozzle/2, process.sliceFillAngle + 0 , nozzle * 1.0, process.outputFeedrate, 1.5);
raft(nozzle/2, process.sliceFillAngle + 0 , nozzle * 1.0, process.outputFeedrate, 1.0);
raft(nozzle/2, process.sliceFillAngle + 90 , nozzle * 0.7, process.outputFeedrate, 0.75);
// raise first layer off raft slightly to lessen adhesion
firstLayerHeight += process.outputRaftSpacing || 0;
zoff += process.outputRaftSpacing || 0;
// retract after last raft layer
output.last().last().retract = true;
@ -169,7 +170,8 @@
});
// output brim points
printPoint = print.poly2polyEmit(polys, printPoint, function(poly, index, count, startPoint) {
let brimStart = offset < nozzle * 2 ? newPoint(-bedWidth, -bedDepth, 0) : printPoint;
printPoint = print.poly2polyEmit(polys, brimStart, (poly, index, count, startPoint) => {
return print.polyPrintPath(poly, startPoint, preout, {
rate: firstLayerRate,
onfirst: function(point) {
@ -203,6 +205,10 @@
// so that they can use a separate extruder
for (let widget of widgets.slice()) {
let sslices = [];
if (!widget.slices) {
console.log('invalid widget', widget);
continue;
}
for (let slice of widget.slices) {
if (!slice.supports) {
continue;
@ -210,25 +216,30 @@
let sslice = KIRI.newSlice(slice.z);
sslice.extruder = process.sliceSupportNozzle;
sslice.supports = slice.supports.slice();
sslice.height = slice.height;
sslices.push(sslice);
}
if (sslices.length) {
let swidget = KIRI.newWidget();
let swidget = KIRI.newWidget(null,widget.group);
swidget.slices = sslices;
swidget.support = true;
swidget.mesh = { widget: swidget, position: widget.mesh.position };
swidget.rotinfo = widget.rotinfo;
swidget.belt = widget.belt;
swidget.track = Object.clone(widget.track);
swidget.mesh = { widget: swidget, position: swidget.track.pos };
settings.widget[swidget.id] = { extruder: process.sliceSupportNozzle };
widgets.push(swidget);
}
}
let lastPoly;
let lastLayer;
let extruders = [];
let extcount = 0;
// find max layers (for updates)
// generate list of used extruders for purge blocks
for (let widget of widgets) {
maxLayers = Math.max(maxLayers, widget.slices.length);
let extruder = (settings.widget[widget.id] || {}).extruder || 0;
if (!extruders[extruder]) {
extruders[extruder] = {};
@ -265,13 +276,15 @@
// generate purge block for given nozzle
function purge(nozzle, track, layer, start, z, using) {
if (extcount < 2) {
if (extcount < 2 || isBelt) {
return start;
}
let rec = track[nozzle];
if (rec) {
track[nozzle] = null;
if (layer.last()) layer.last().retract = true;
if (layer.last()) {
layer.last().retract = true;
}
start = print.polyPrintPath(rec.poly.clone().setZ(z), start, layer, {
tool: using || nozzle,
open: true,
@ -288,13 +301,15 @@
// establish offsets
for (let widget of widgets) {
let { rotinfo, belt } = widget;
// console.log({rotinfo, belt});
let offset = widget.mesh ? Object.clone(widget.mesh.position) : {x:0, y:0, z:0};
let offset = Object.clone(widget.track.pos);
if (isBelt) {
offset.x = rotinfo.xpos;
offset.y = -belt.midy;
offset.y += rotinfo.ypos * beltfact;
offset.z = rotinfo.ypos * beltfact;
let o = rotinfo.ypos * beltfact;
offset = {
x: rotinfo.xpos,
y: o - belt.midy - belt.yadd/2,
z: o,
};
// offset = { x:0, y:0, z:0 };
// locate the lowest point in slices and widget overall
let minby = Infinity;
for (let slice of widget.slices) {
@ -308,10 +323,11 @@
}
}
}
// todo remove b/c this is now calculated at slice time
// flag slices as being on or off the bed / belt
for (let slice of widget.slices) {
slice.onbelt = Math.abs(minby - slice.minby) < 0.01;
}
// for (let slice of widget.slices) {
// slice.onbelt = Math.abs(minby - slice.minby) < 0.01;
// }
} else {
// when rafts used this is non-zero
offset.z = zoff;
@ -341,6 +357,8 @@
return a.z - b.z;
});
let lastWidget;
// walk cake layers bottom up
for (let layer of cake) {
// track purge blocks generated for each layer
@ -376,41 +394,63 @@
return a.dst - b.dst;
});
let { z, slice, offset } = order[0];
let params = getRangeParameters(settings, slice.index);
slice.prep = true;
// retract between widgets
if (layerout.length && minidx !== lastIndex) {
// retract between widgets or layers (when set)
if (layerout.length && slice.widget !== lastWidget) {
layerout.last().retract = true;
}
layerout.z = z;
lastWidget = slice.widget;
layerout.z = z + slice.height / 2;
layerout.height = layerout.height || slice.height;
layerout.slice = slice;
// mark layer as anchor if slice is belt and flag set
layerout.anchor = slice.belt && slice.belt.anchor;
// detect extruder change and print purge block
if (!lastOut || lastOut.extruder !== slice.extruder) {
printPoint = purge(slice.extruder, track, layerout, printPoint, slice.z);
}
let tmpout = [];
let wtb = slice.widget.track.box;
// output seek to start point between mesh slices if previous data
printPoint = print.slicePrintPath(
slice,
isBelt ? newPoint(-wtb.w, wtb.d * 2, 0) : printPoint.sub(offset),
slice.belt && slice.belt.touch ? newPoint(-wtb.w, wtb.d * 2, 0) : printPoint.sub(offset),
offset,
tmpout, //layerout,
layerout,
{
seedPoint: printPoint.sub(offset),
danger: isDanger,
params, // range parameters
first: slice.index === 0,
support: slice.widget.support,
onBelt: slice.onbelt
onBelt: slice.belt && slice.belt.touch,
pretract: (wipeDist) => {
if (lastLayer && lastLayer.length) {
let lastOut = lastLayer.last();
lastOut.retract = true;
if (wipeDist && lastPoly && lastOut.point) {
let endpoint = lastOut.point.followTo(lastPoly.center(true).add(offset), wipeDist);
if (endpoint.inPolygon(lastPoly)) {
print.addOutput(lastLayer, endpoint);
}
}
}
}
}
);
layerout.appendAll(tmpout);
lastOut = slice;
lastExt = lastOut.ext
lastIndex = minidx;
lastPoly = slice.lastPoly;
lastLayer = layerout;
if (layerRetract && layerout.length) {
layerout.last().retract = true;
}
}
// if a declared extruder isn't used in a layer, use selected
// extruder to fill the relevant purge blocks for later support
track.forEach(ext => {
if (ext) {
if (ext && lastOut) {
printPoint = purge(ext.extruder, track, layerout, printPoint, lastOut.z, lastExt);
}
});
@ -421,13 +461,13 @@
}
// retract after last layer
if (layer === maxLayers && layerout.length) {
if (layerno === cake.length - 1 && layerout.length) {
layerout.last().retract = true;
}
// notify progress
layerout.layer = layer++;
update((layer / maxLayers) * 0.5, "prepare");
layerout.layer = layerno++;
update((layerno / cake.length) * 0.5, "prepare");
slices = [];
layerout = [];
@ -454,6 +494,13 @@
rec.point.y += poff;
}
}
// add lead in, when specified
// if (process.firstLayerBeltLead) {
// // add belt lead in
// console.log({process, output});
// }
let thresh = firstLayerHeight * 1.05;
// iterate over layers, find extrusion on belt and
// apply corrections and add brim when specified
@ -479,13 +526,17 @@
}
lastout = rec;
}
// skip if brig trigger not met
if (firstLayerBrimTrig && mins > firstLayerBrimTrig) {
// skip if brim trigger not met
if (firstLayerBrimTrig > 0 && mins > firstLayerBrimTrig) {
continue;
}
// do not add brims to anchor layers
if (layer.anchor) {
continue;
}
// add brim, if specified
if (first && firstLayerBrim) {
let {emit, tool } = first;
let { emit, tool } = first;
let y = maxy;
let z = minz;
let b = Math.max(firstLayerBrim, 1);
@ -541,8 +592,11 @@
const flat = opts.flat;
const thin = opts.thin && !flat;
const ckspeed = opts.speed !== false;
const headColor = 0x888888;
const moveColor = opts.move >= 0 ? opts.move : 0xaaaaaa;
const printColor = opts.print >= 0 ? opts.print : 0x777700;
const arrowAll = false;
const arrowSize = arrowAll ? 0.2 : 0.4;
const layers = [];
const moveOpt = {
@ -569,6 +623,7 @@
let lastOut = null;
let current = null;
let retracted = false;
let retractz = 0;
function color(point) {
return FDM.rateToColor(point.speed, maxspd);
@ -604,6 +659,7 @@
if (out.retract) {
retracts.push(out.point);
retracted = true;
retractz++;
}
if (!out.point) {
// in cam mode, these are drilling or dwell ops
@ -611,7 +667,7 @@
}
if (lastOut) {
if (lastOut.emit !== out.emit) {
if (arrowAll || lastOut.emit !== out.emit) {
heads.push({p1: lastOut.point, p2: out.point});
}
const op = out.point, lp = lastOut.point;
@ -670,14 +726,14 @@
}
if (heads.length) {
output
.setLayer('arrows', { face: moveColor, line: 0x555555, opacity: 0.5 }, true)
.setLayer('arrows', { face: headColor, line: 0x112233, opacity: 0.5 }, true)
.addAreas(heads.map(points => {
const {p1, p2} = points;
const slope = p2.slopeTo(p1);
const s1 = BASE.newSlopeFromAngle(slope.angle + 20);
const s2 = BASE.newSlopeFromAngle(slope.angle - 20);
const p3 = points.p2.projectOnSlope(s1, 0.4);
const p4 = points.p2.projectOnSlope(s2, 0.4);
const p3 = points.p2.projectOnSlope(s1, arrowSize);
const p4 = points.p2.projectOnSlope(s2, arrowSize);
return newPolygon().addPoints([p2,p3,p4]).setZ(p2.z + 0.01);
}), { thin: true, outline: true });
}
@ -685,9 +741,7 @@
.setLayer(opts.other || 'move', moveOpt, opts.moves !== true)
.addPolys(moves, { thin: true, z: opts.z });
// force level when present
if (level.z) {
opts.z = level.z;
}
let pz = level.z ? level.z - height : opts.z;
Object.values(prints).forEach(array => {
array.forEach(poly => {
if (flat && poly.appearsClosed()) {
@ -700,10 +754,10 @@
.addPolys([ poly ],
thin ? { thin, z: opts.z, color: poly.color } :
flat ? {
flat, z: opts.z, color: poly.color,
flat, z: pz, color: poly.color,
outline: true, offset: array.width, open: poly.open } :
{
offset: array.width, height, z: opts.z,
offset: array.width, height, z: pz,
color: { face: poly.color, line: poly.color }
})
});
@ -711,7 +765,7 @@
update(index / levels.length, output);
});
// console.log({retractz});
return layers;
}

View file

@ -21,9 +21,13 @@
shell: { check: 0x0077bb, face: 0x0077bb, line: 0x0077bb, opacity: 1 },
fill: { check: 0x00bb77, face: 0x00bb77, line: 0x00bb77, opacity: 1 },
infill: { check: 0x3322bb, face: 0x3322bb, line: 0x3322bb, opacity: 1 },
support: { check: 0xaa5533, face: 0xaa5533, line: 0xaa5533, opacity: 1 }
support: { check: 0xaa5533, face: 0xaa5533, line: 0xaa5533, opacity: 1 },
gaps: { check: 0xaa3366, face: 0xaa3366, line: 0xaa3366, opacity: 1 }
},
PROTO = Object.clone(COLOR);
PROTO = Object.clone(COLOR),
bwcomp = (1 / Math.cos(Math.PI/4)),
getRangeParameters = FDM.getRangeParameters,
debug = false;
let isThin = false; // force line rendering
let isFlat = false; // force flat rendering
@ -57,14 +61,16 @@
FDM.slice = function(settings, widget, onupdate, ondone) {
FDM.fixExtruders(settings);
let render = settings.render !== false,
ctrl = settings.controller,
spro = settings.process,
sdev = settings.device,
isBelt = sdev.bedBelt,
isSynth = widget.track.synth,
update_start = Date.now(),
minSolid = spro.sliceSolidMinArea,
solidLayers = spro.sliceSolidLayers,
vaseMode = spro.sliceFillType === 'vase',
doSolidLayers = solidLayers && !vaseMode,
vaseMode = spro.sliceFillType === 'vase' && !isSynth,
doSolidLayers = solidLayers && !vaseMode && !isSynth,
metadata = settings.widget[widget.id] || {},
extruder = metadata.extruder || 0,
sliceHeight = spro.sliceHeight,
@ -77,10 +83,13 @@
fillSpacing = lineWidth,
fillOffset = lineWidth * fillOffsetMult,
sliceFillAngle = spro.sliceFillAngle,
view = widget.mesh && widget.mesh.newGroup ? widget.mesh.newGroup() : null;
supportDensity = spro.sliceSupportDensity,
view = widget.mesh && widget.mesh.newGroup ? widget.mesh.newGroup() : null,
beltfact = Math.cos(Math.PI/4),
invbfact = 1 / beltfact;
isFlat = settings.controller.lineType === "flat";
isThin = !isFlat && settings.controller.lineType === "line";
isFlat = ctrl.lineType === "flat";
isThin = !isFlat && ctrl.lineType === "line";
offset = lineWidth / 2;
if (isFlat) {
@ -145,6 +154,7 @@
height: sliceHeight,
minHeight: sliceMinHeight,
firstHeight: firstSliceHeight,
union: ctrl.healMesh,
// debug: true,
// xray: 3,
// view: view
@ -155,9 +165,9 @@
}
function onSliceDone(slices) {
// slices = slices.filter(slice => slice.tops.length);
// remove all empty slices above part but leave below
// for multi-part (multi-extruder) setups where the void is ok
// also reverse because slicing occurs bottom-up
let found = false;
slices = slices.reverse().filter(slice => {
if (slice.tops.length) {
@ -173,6 +183,61 @@
return;
}
// for synth support widgets, merge tops
if (isSynth) {
for (let slice of slices) {
// union top support polys
let tops = slice.topPolys();
let union = POLY.union(tops, null, true);
if (union.length < tops.length) {
slice.tops = [];
for (let u of union) {
slice.addTop(u);
}
}
let gap = sliceHeight * (isBelt ? 0 : spro.sliceSupportGap);
// clip tops to other widgets in group
tops = slice.topPolys();
for (let peer of widget.group) {
// skip self
if (peer === widget) {
continue;
}
for (let pslice of peer.slices) {
if (Math.abs(Math.abs(pslice.z - slice.z) - gap) > 0.1) {
continue;
}
// offset pslice tops by spro.sliceSupportOffset
if (!pslice.synth_off) {
pslice.synth_off = POLY.offset(pslice.topPolys(), spro.sliceSupportOffset);
}
let ptops = pslice.synth_off;
let ntops = [];
POLY.subtract(tops, ptops, ntops, null, slice.z, 0);
tops = ntops;
}
// trim to group's shadow if not in belt mode
if (!isBelt) {
let group = widget.group[0];
if (!group.shadow) {
let gs = [];
for (let w of group) {
if (w.shadow) {
gs = POLY.union([w.shadow,...gs],null,0.1);
}
}
group.shadow = gs;
}
tops = POLY.setZ(POLY.trimTo(tops, group.shadow), slice.z);
}
}
slice.tops = [];
for (let t of tops) {
slice.addTop(t);
}
}
}
// calculate % complete and call onupdate()
function doupdate(index, from, to, msg) {
onupdate(0.5 + (from + ((index/slices.length) * (to-from))) * 0.5, msg);
@ -197,16 +262,15 @@
slice.solids = [];
});
// fixed supports (generated ahead of time)
const fixed = Object.values(settings.widget[widget.id].support || {});
// create shadow for clipping supports
let shadow = null;
if (fixed.length || spro.sliceSupportEnable) {
// create shadow. TODO test if any supports set
if (true || spro.sliceSupportEnable) {
let alltops = slices.map(slice => slice.topPolys()).flat();
shadow = POLY.union(alltops,null,0.1);
if (spro.sliceSupportExtra) {
shadow = POLY.offset(shadow, spro.sliceSupportExtra);
}
widget.shadow = shadow;
// slices[0].output()
// .setLayer('shadow', { line: 0xff0000, check: 0xff0000 })
// .addPolys(shadow);
@ -214,28 +278,100 @@
// create shells and diff inner fillable areas
forSlices(0.0, 0.2, slice => {
let params = getRangeParameters(settings, slice.index);
let shellFrac = (params.sliceShells - (params.sliceShells | 0));
let sliceShells = params.sliceShells | 0;
if (ctrl.danger && shellFrac) {
let v1 = shellFrac > 0.5 ? 1 - shellFrac : shellFrac;
let v2 = 1 - v1;
let parts = Math.round(v2/v1) + 1;
let rem = slice.index % parts;
let trg = shellFrac > 0.5 ? 1 : parts - 1;
sliceShells += rem >= trg ? 1 : 0;
}
let first = slice.index === 0;
let solid = (
slice.index < spro.sliceBottomLayers ||
slice.index > slices.length - spro.sliceTopLayers-1 ||
spro.sliceFillSparse > 0.95
) && !vaseMode;
let isBottom = slice.index < spro.sliceBottomLayers;
let isTop = slice.index > slices.length - spro.sliceTopLayers-1;
let isDense = params.sliceFillSparse > 0.98;
let solid = (isBottom || ((isTop || isDense) && !vaseMode)) && !isSynth;
let solidWidth = params.sliceFillWidth || 1;
let spaceMult = first ? spro.firstLayerLineMult || 1 : 1;
let offset = shellOffset * spaceMult;
let fillOff = fillOffset * spaceMult;
doShells(slice, spro.sliceShells, offset, fillOff, {
let count = isSynth ? 1 : sliceShells;
doShells(slice, count, offset, fillOff, {
vase: vaseMode,
thin: spro.detectThinWalls,
belt0: isBelt && first,
widget: widget
thin: spro.detectThinWalls && !isSynth,
widget: widget,
danger: ctrl.danger
});
if (solid) {
let fillSpace = fillSpacing * spaceMult;
let fillSpace = fillSpacing * spaceMult * solidWidth;
doSolidLayerFill(slice, fillSpace, sliceFillAngle);
}
sliceFillAngle += 90.0;
}, "offsets");
// add lead in when specified in belt mode
if (!isSynth && isBelt) {
let wb = widget.bounds;
// find adjusted zero point from slices
let smin = Infinity;
for (let slice of slices) {
let miny = Infinity;
for (let poly of slice.topPolys()) {
let y = poly.bounds.maxy;
let z = slice.z;
let by = -y + z;
if (by < miny) miny = by;
if (by < smin) smin = by;
}
slice.belt = { miny, touch: false };
}
// mark slices with tops touching belt
// also find max width of first 5 layers
let start;
let minx = Infinity, maxx = -Infinity;
for (let slice of slices) {
if (slice.index < 5) {
for (let poly of slice.topPolys()) {
minx = Math.min(minx, poly.bounds.minx);
maxx = Math.max(maxx, poly.bounds.maxx);
}
}
if (Math.abs(slice.belt.miny - smin) < 0.001) {
slice.belt.touch = true;
if (!start) start = slice;
}
}
// console.log({smin: smin.round(4)});
let offset = spro.firstLayerBeltLead * beltfact;
// ensure we start against a layer with shells
while (start.up && start.topShells().length === 0) {
start = start.up;
}
while (offset && start && offset >= sliceHeight) {
let addto = start.down;
if (!addto) {
addto = newSlice(start.z - sliceHeight);
addto.belt = { };
addto.height = start.height;
addto.up = start;
start.down = addto;
slices.splice(0,0,addto);
}
addto.index = -1;
addto.belt.anchor = true;
let z = addto.z;
let y = z - smin - (nozzleSize / 2);
// let splat = BASE.newPolygon().add(wb.min.x, y, z).add(wb.max.x, y, z).setOpen();
let splat = BASE.newPolygon().add(minx, y, z).add(maxx, y, z).setOpen();
addto.addTop(splat).fill_sparse = [ splat ];
start = addto;
offset -= sliceHeight;
}
}
// calculations only relevant when solid layers are used
if (doSolidLayers) {
forSlices(0.2, 0.34, slice => {
@ -246,48 +382,68 @@
projectBridges(slice, solidLayers);
}, "solids");
forSlices(0.35, 0.5, slice => {
let params = getRangeParameters(settings, slice.index);
let first = slice.index === 0;
let spaceMult = first ? spro.firstLayerLineMult || 1 : 1;
let fillSpace = fillSpacing * spaceMult;
let solidWidth = params.sliceFillWidth || 1;
let spaceMult = first ? params.firstLayerLineMult || 1 : 1;
let fillSpace = fillSpacing * spaceMult * solidWidth;
doSolidsFill(slice, fillSpace, sliceFillAngle, minSolid);
sliceFillAngle += 90.0;
}, "solids");
}
// sparse layers only present when non-vase mose and sparse % > 0
if (!vaseMode && spro.sliceFillSparse > 0.0) {
if (!isSynth) {
let lastType;
forSlices(0.5, 0.7, slice => {
let params = getRangeParameters(settings, slice.index);
if (vaseMode || !params.sliceFillSparse) {
return;
}
let newType = params.sliceFillType;
doSparseLayerFill(slice, {
settings: settings,
process: spro,
device: sdev,
lineWidth: lineWidth,
spacing: fillOffset,
density: spro.sliceFillSparse,
density: params.sliceFillSparse,
bounds: widget.getBoundingBox(),
height: sliceHeight,
type: spro.sliceFillType
type: newType,
cache: params._range !== true && lastType === newType
});
lastType = newType;
}, "infill");
} else if (isSynth) {
forSlices(0.5, 0.7, slice => {
let params = getRangeParameters(settings, slice.index);
let density = params.sliceSupportDensity;
if (density)
for (let top of slice.tops) {
let offset = [];
POLY.expand(top.shells, -nozzleSize/4, slice.z, offset);
fillSupportPolys(offset, lineWidth, density, slice.z);
top.fill_lines = offset.map(o => o.fill).flat().filter(v => v);
}
}, "infill");
}
// calculations only relevant when supports are enabled
if (!isBelt) {
let auto = spro.sliceSupportEnable && spro.sliceSupportDensity > 0.0,
minArea = spro.sliceSupportArea;
// auto support generation
if (!isBelt && !isSynth && supportDensity && spro.sliceSupportEnable) {
forSlices(0.7, 0.8, slice => {
doSupport(slice, spro, auto, fixed, shadow);
doSupport(slice, spro, shadow);
}, "support");
forSlices(0.8, 0.9, slice => {
doSupportFill(slice, lineWidth, spro.sliceSupportDensity, minArea);
doSupportFill(slice, lineWidth, supportDensity, spro.sliceSupportArea);
}, "support");
}
// render if not explicitly disabled
if (render) {
forSlices(0.9, 1.0, slice => {
doRender(slice);
let params = getRangeParameters(settings, slice.index);
doRender(slice, isSynth, params);
}, "render");
}
@ -310,32 +466,35 @@
return Math.max(min,Math.min(max,v));
}
function doRender(slice) {
function doRender(slice, isSynth, params) {
const output = slice.output();
const height = slice.height / 2;
const solidWidth = params.sliceFillWidth || 1;
slice.tops.forEach(top => {
if (isThin) {
output
.setLayer('slice', { line: 0x000066, check: 0x000066 })
.addPolys(top.poly);
}
if (isThin) output
.setLayer('slice', { line: 0x000066, check: 0x000066 })
.addPolys(top.poly);
output
.setLayer("shells", COLOR.shell)
if (top.shells) output
.setLayer("shells", isSynth ? COLOR.support : COLOR.shell)
.addPolys(top.shells, vopt({ offset, height }));
// if (isThin && debug) {
// slice.output()
// .setLayer('offset', { face: 0, line: 0x888888 })
// .addPolys(top.fill_off)
// .setLayer('last', { face: 0, line: 0x008888 })
// .addPolys(top.last);
// }
if (top.gaps) output
.setLayer("gaps", COLOR.gaps)
.addPolys(top.gaps, vopt({ offset, height, thin: true }));
if (top.fill_lines) output
.setLayer("fill", COLOR.fill)
.addLines(top.fill_lines, vopt({ offset, height }));
if (isThin && debug) {
slice.output()
.setLayer('offset', { face: 0, line: 0x888888 })
.addPolys(top.fill_off)
.setLayer('last', { face: 0, line: 0x008888 })
.addPolys(top.last);
}
if (top.fill_lines && top.fill_lines.length) output
.setLayer("fill", isSynth ? COLOR.support : COLOR.fill)
.addLines(top.fill_lines, vopt({ offset: offset * solidWidth, height }));
if (top.fill_sparse) output
.setLayer("infill", COLOR.infill)
@ -344,15 +503,22 @@
if (top.thin_fill) output
.setLayer("fill", COLOR.fill)
.addLines(top.thin_fill, vopt({ offset, height }));
// emit solid areas
// if (isThin && debug) {
// output
// .setLayer("solids", { face: 0x00dd00 })
// .addAreas(slice.solids);
// }
});
if (isThin && debug) {
if (slice.solids) output
.setLayer("solids", { face: 0x00dd00 })
.addAreas(slice.solids);
if (slice.bridges) output
.setLayer("bridges", { face: 0x00aaaa, line: 0x00aaaa })
.addAreas(slice.bridges);
if (slice.flats) output
.setLayer("flats", { face: 0xaa00aa, line: 0xaa00aa })
.addAreas(slice.flats);
}
if (slice.supports) output
.setLayer("support", COLOR.support)
.addPolys(slice.supports, vopt({ offset, height }));
@ -363,16 +529,6 @@
.addLines(poly.fill, vopt({ offset, height }));
});
// if (isThin && debug) {
// output
// .setLayer("bridges", { face: 0x00aaaa, line: 0x00aaaa })
// .addAreas(top.bridges);
//
// output
// .setLayer("flats", { face: 0xaa00aa, line: 0xaa00aa })
// .addAreas(top.flats);
// }
// console.log(slice.index, slice.render.stats);
}
@ -433,7 +589,22 @@
let dist = p.first().distTo2D(p.last());
if (dist < 1) p.open = false;
} });
if (opt.thin) {
if (opt.danger && opt.thin) {
top.thin_fill = [];
top.fill_sparse = [];
let layers = POLY.inset(top_poly, offsetN, count, z);
last = layers.last().mid;
top.shells = layers.map(r => r.mid).flat();
top.gaps = layers.map(r => r.gap).flat();
let off = offsetN;
let min = off * 0.75;
let max = off * 4;
for (let poly of layers.map(r => r.gap).flat()) {
let centers = poly.centers(off/2, z, min, max, {lines:false});
top.fill_sparse.appendAll(centers);
// top.fill_lines.appendAll(centers);
}
} else if (opt.thin) {
top.thin_fill = [];
let oso = {z, count, gaps: [], outs: [], minArea: 0.05};
POLY.offset(top_poly, [-offset1, -offsetN], oso);
@ -444,6 +615,11 @@
if (p.fill_off) {
p.fill_off.forEach(pi => pi.depth = i);
}
if (p.inner) {
for (let pi of p.inner) {
pi.depth = p.depth;
}
}
top.shells.push(p);
});
last = polys;
@ -453,12 +629,9 @@
oso.gaps.forEach((polys, i) => {
let off = (i == 0 ? offset1 : offsetN);
polys = POLY.offset(polys, -off * 0.8, {z, minArea: 0});
// polys.forEach(p => { slice.solids.trimmed.push(p); });
top.thin_fill.appendAll(cullIntersections(
fillArea(polys, 45, off/2, [], 0.01, off*2),
fillArea(polys, 135, off/2, [], 0.01, off*2),
// fillArea(polys, 90, off, [], 0.05, off*4),
// fillArea(polys, 180, off, [], 0.05, off*4),
));
gaps = polys;
});
@ -481,6 +654,11 @@
// use negative offset for inners
pi.depth = -(count - countNow);
});
if (p.inner) {
for (let pi of p.inner) {
pi.depth = p.depth;
}
}
});
});
}
@ -507,23 +685,6 @@
top.last = last;
shellout += top.shells.length;
// add anchor extrusion if missing on the first belt layer
// todo: fix. disabled because throws off layer min y calculations in some cases
if (false && opt.belt0 && top.shells.length === 0 && slice.up && slice.up.tops.length) {
for (let up of slice.up.tops) {
let bounds = up.poly.bounds,
midy = (bounds.miny + bounds.maxy) / 2;
if (top.poly.isInside(up.poly, 1)) {
slice.widget.belt.ymid = midy;
slice.widget.belt.zmid = slice.z;
top.shells.push(BASE.newPolygon()
.setOpen()
.add(bounds.minx,midy,slice.z)
.add(bounds.maxx,midy,slice.z));
}
}
}
});
};
@ -559,6 +720,7 @@
density = options.density, // density of infill 0.0 - 1.0
bounds = options.bounds, // bounding box of widget
height = options.height, // z layer height
cache = !(options.cache === false),
type = options.type || 'hex';
if (slice.tops.length === 0 || density === 0.0 || slice.isSolidLayer) {
@ -622,7 +784,7 @@
// prepare top infill structure
tops.forEach(function(top) {
top.fill_sparse = [];
top.fill_sparse = top.fill_sparse || [];
polys.appendAll(top.fill_off);
polys.appendAll(top.solids);
});
@ -630,7 +792,7 @@
// update fill fingerprint for this slice
slice._fill_finger = POLY.fingerprint(polys);
let skippable = FILLFIXED[type] ? true : false;
let skippable = cache && FILLFIXED[type] ? true : false;
let miss = false;
// if the layer below has the same fingerprint,
// we may be able to clone the infill instead of regenerating it
@ -779,25 +941,27 @@
let minarea = minArea || 1,
tops = slice.tops,
solids = slice.solids,
unioned = POLY.union(solids, undefined, true).flat(), // TODO verify
solids = slice.solids;
if (!(tops && solids)) {
return;
}
let unioned = POLY.union(solids, undefined, true).flat(), // TODO verify
isSLA = (spacing === undefined && angle === undefined);
if (solids.length === 0) return false;
if (unioned.length === 0) return false;
let masks,
trims = [],
let trims = [],
inner = isSLA ? slice.topPolys() : slice.topFillOff();
// trim each solid to the inner bounds
unioned.forEach(function(p) {
p.setZ(slice.z);
inner.forEach(function(i) {
if (p.del) return;
masks = p.mask(i);
let masks = p.mask(i);
if (masks && masks.length > 0) {
p.del = true;
trims.appendAll(masks);
}
});
@ -832,6 +996,10 @@
// create empty filled line array for each top
tops.forEach(function(top) {
// synth belt anchor tops don't want fill
if (!top.fill_lines) {
return;
}
const tofill = [];
const angfill = [];
const newfill = [];
@ -857,7 +1025,6 @@
top.fill_lines_ang.poly.push(af.clone());
});
}
top.fill_lines.appendAll(newfill);
});
@ -865,12 +1032,10 @@
};
/**
* calculate external overhangs requiring support.
* this is done bottom-up except for fixed supports (manual)
* calculate external overhangs requiring support
*/
function doSupport(slice, proc, auto, fixed, shadow) {
let minOffset = proc.sliceSupportOffset,
maxBridge = proc.sliceSupportSpan || 5,
function doSupport(slice, proc, shadow) {
let maxBridge = proc.sliceSupportSpan || 5,
minArea = proc.supportMinArea,
pillarSize = proc.sliceSupportSize,
offset = proc.sliceSupportOffset,
@ -900,7 +1065,7 @@
let supported = point.isInPolygonOnly(down_tops);
if (!supported) down_traces.forEach(function(trace) {
trace.forEachSegment(function(p1, p2) {
if (point.distToLine(p1, p2) <= minOffset) {
if (point.distToLine(p1, p2) <= offset) {
return supported = true;
}
});
@ -927,7 +1092,7 @@
let supports = [];
// generate support polys from unsupported points
if (auto && slice.down) (function() {
if (slice.down) (function() {
// check trace line support needs
traces.forEach(function(trace) {
trace.forEachSegment(function(p1, p2) { checkLineSupport(p1, p2, true) });
@ -952,21 +1117,10 @@
});
})();
if (supports.length === 0 && !(fixed && fixed.length)) {
if (supports.length === 0) {
return;
}
if (fixed && fixed.length) {
for (let sup of fixed) {
let zmin = sup.z - sup.dh / 2;
let zmax = sup.z + sup.dh / 2;
if (slice.z >= zmin && slice.z <= zmax) {
let center = BASE.newPoint(sup.x, sup.y, slice.z);
supports.push(BASE.newPolygon().centerRectangle(center, sup.dw, sup.dw));
}
}
}
// then union supports
supports = POLY.union(supports, null, true);
@ -984,7 +1138,7 @@
// set depth hint on support polys for infill density
trimmed.forEach(function(trim) {
if (trim.area() < 0.1) return;
// if (trim.area() < 0.1) return;
culled.push(trim.setZ(down.z));
});
@ -1031,23 +1185,29 @@
supports = nsC;
}
if (supports) supports.forEach(function (poly) {
if (supports) {
fillSupportPolys(supports, linewidth, density, slice.z);
}
// re-assign new supports back to slice
slice.supports = supports;
};
function fillSupportPolys(polys, linewidth, density, z) {
// calculate fill density
let spacing = linewidth * (1 / density);
polys.forEach(function (poly) {
// angle based on width/height ratio
let angle = (poly.bounds.width() / poly.bounds.height() > 1) ? 90 : 0;
// calculate fill density
let spacing = linewidth * (1 / density);
// inset support poly for fill lines 33% of nozzle width
let inset = POLY.offset([poly], -linewidth/3, {flat: true, z: slice.z});
let inset = POLY.offset([poly], -linewidth/3, {flat: true, z});
// do the fill
if (inset && inset.length > 0) {
fillArea(inset, angle, spacing, poly.fill = []);
}
return true;
});
// re-assign new supports back to slice
slice.supports = supports;
};
}
/**
*
@ -1126,14 +1286,16 @@
}
FDM.supports = function(settings, widget) {
let isBelt = settings.device.bedBelt;
let process = settings.process;
let size = process.sliceSupportSize;
let min = process.sliceSupportArea || 0.1;
let buf = new THREE.BufferGeometry();
buf.setAttribute('position', new THREE.BufferAttribute(widget.vertices, 3));
let min = process.sliceSupportArea || 1;
let geo = new THREE.BufferGeometry();
geo.setAttribute('position', new THREE.BufferAttribute(widget.vertices, 3));
let mat = new THREE.MeshBasicMaterial();
let geo = new THREE.Geometry().fromBufferGeometry(buf);
let angle = (Math.PI / 180) * settings.process.sliceSupportAngle;
let rad = (Math.PI / 180);
let deg = (180 / Math.PI);
let angle = rad * settings.process.sliceSupportAngle;
let thresh = -Math.sin(angle);
let dir = new THREE.Vector3(0,0,-1)
let add = [];
@ -1167,6 +1329,13 @@
if (point.added) {
return;
}
for (let added of add) {
let p2 = new THREE.Vector2(point.x, point.y);
let pm = new THREE.Vector2(added.mid.x, added.mid.y);
if (p2.distanceTo(pm) < 1) {
return;
}
}
let ray = new THREE.Raycaster(point, dir);
let int = ray.intersectObjects([ mesh, platform ], false);
if (int && int.length && int[0].distance > 0.01) {
@ -1175,20 +1344,33 @@
point.added = true;
}
}
geo.faces.filter(f => f.normal.z < thresh).forEach(face => {
let a = geo.vertices[face.a];
let b = geo.vertices[face.b];
let c = geo.vertices[face.c];
let filter = isBelt ? (norm) => {
return norm.z < thresh && norm.y < -0.001;
} : (norm) => {
return norm.z < thresh;
};
let { position } = geo.attributes;
let { itemSize, count, array } = position;
for (let i = 0; i<count; i += 3) {
let ip = i * itemSize;
let a = new THREE.Vector3(array[ip++], array[ip++], array[ip++]);
let b = new THREE.Vector3(array[ip++], array[ip++], array[ip++]);
let c = new THREE.Vector3(array[ip++], array[ip++], array[ip++]);
let norm = THREE.computeFaceNormal(a,b,c);
// limit to downward faces
if (!filter(norm)) {
continue;
}
// skip tiny faces
let area = BASE.newPolygon().addPoints([a,b,c]).area();
if (BASE.newPolygon().addPoints([a,b,c]).area() < min) {
skip++;
return;
continue;
}
tp(new THREE.Vector3().add(a).add(b).add(c).divideScalar(3));
tl(a,b);
tl(b,c);
tl(a,c);
});
}
widget.supports = add;
return add.length > 0;
};

View file

@ -301,7 +301,7 @@
e = o.point;
e.x += p.max.w / 2 - m.fit.x;
e.y += p.max.h / 2 - m.fit.y;
// e.z = 0;
e.z = 0;
});
}

View file

@ -476,6 +476,8 @@
// };
// } else
// load renderer code in worker context only
let wasm;
fetch('/wasm/kiri-sla.wasm')
.then(response => response.arrayBuffer())
.then(bytes => WebAssembly.instantiate(bytes, {
@ -488,7 +490,7 @@
let {module, instance} = results;
let {exports} = instance;
let heap = new Uint8Array(exports.memory.buffer);
self.wasm = {
wasm = {
heap,
memory: exports.memory,
render: exports.render,
@ -502,7 +504,6 @@
let width2 = width / 2, height2 = height / 2;
let array = [];
let count = 0;
let wasm = self.wasm;
function scaleMovePoly(poly) {
let points = poly.points;

View file

@ -181,6 +181,15 @@ MOTO.CTRL = function (object, domElement, notify, slider) {
}
};
this.getTarget = function() {
return this.target;
};
this.setTarget = function(t) {
this.target = t;
this.target0 = t.clone();
}
function isValue(v) {
return v !== null && v !== undefined;
}

View file

@ -16,8 +16,13 @@
PI2 = PI / 2,
PI4 = PI / 4,
ROUND = Math.round,
panX = 0,
panY = 0,
panZ = 0,
home = 0,
gridZOff = 0,
tweenTime = 500,
tweenDelay = 20,
platformZOff = 0,
perspective = 35,
refreshTimeout = null,
@ -92,6 +97,7 @@
camera,
renderer,
container,
raycaster,
freezeTo,
freeze,
isRound = false,
@ -139,7 +145,7 @@
function tweenit() {
TWEEN.update();
setTimeout(tweenit, 20);
setTimeout(tweenit, tweenDelay);
}
tweenit();
@ -170,12 +176,13 @@
}
}
new TWEEN.Tween(from).
to(to, 500).
to(to, tweenTime).
onUpdate(tf).
onComplete(() => {
viewControl.setPosition(pos);
updateLastAction();
refresh();
if (pos.then) pos.then();
}).
start();
}
@ -195,7 +202,7 @@
setGrid();
};
new TWEEN.Tween(from).
to(to, 500).
to(to, tweenTime).
onStart(start).
onUpdate(update).
onComplete(complete).
@ -676,14 +683,15 @@
if (points.length % 2 != 0) {
throw "invalid line : "+points.length;
}
const geo = new THREE.Geometry();
for (let i=0; i < points.length; ) {
const p1 = points[i++];
const p2 = points[i++];
geo.vertices.push(new THREE.Vector3(p1.x, p1.y, p1.z));
geo.vertices.push(new THREE.Vector3(p2.x, p2.y, p2.z));
const geo = new THREE.BufferGeometry();
const vrt = new Float32Array(points.length * 3);
let vi = 0;
for (let p of points) {
vrt[vi++] = p.x;
vrt[vi++] = p.y;
vrt[vi++] = p.z;
}
geo.verticesNeedUpdate = true;
geo.setAttribute('position', new THREE.BufferAttribute(vrt, 3));
return new THREE.LineSegments(geo, new THREE.LineBasicMaterial({
color: color,
opacity: opacity || 1,
@ -692,9 +700,7 @@
}
function intersect(objects, recurse) {
let lookAt = new THREE.Vector3(mouse.x, mouse.y, 0.0).unproject(camera);
let ray = new THREE.Raycaster(camera.position, lookAt.sub(camera.position).normalize());
return ray.intersectObjects(objects, recurse);
return raycaster.intersectObjects(objects, recurse);
}
/** ******************************************************************
@ -802,6 +808,11 @@
updateLastAction();
let int, vis;
const mv = new THREE.Vector2();
mv.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mv.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
raycaster.setFromCamera( mv, camera );
if (viewControl.enabled) {
event.preventDefault();
let selection = mouseHover ? mouseHover() : null;
@ -847,6 +858,7 @@
onEnterKey: onEnterKey,
onResize: onResize,
update: requestRefresh,
raycast: intersect,
refresh: refresh,
showSkyGrid: function(b) {
@ -884,6 +896,7 @@
},
platform: {
update: updateDraws,
tweenTo: tweenPlatform,
setSize: setPlatformSizeUpdateGrid,
setColor: setPlatformColor,
@ -896,6 +909,7 @@
add: function(o) { WORLD.add(o) },
remove: function(o) { WORLD.remove(o) },
setMaxZ: function(z) { panY = z / 2 },
setCenter: function(x,y,z) { panX = x; panY = z, panZ = y },
isHidden: function() { return !showPlatform },
setHidden: function(b) { showPlatform = !b; platform.visible = !b },
setHiding: function(b) { hidePlatformBelow = b },
@ -935,12 +949,12 @@
},
view: {
top: function() { tweenCam({left: 0, up: 0, panX: 0, panY: panY, panZ: 0}) },
back: function() { tweenCam({left: PI, up: PI2, panX: 0, panY: panY, panZ: 0}) },
home: function() { tweenCam({left: 0, up: PI4, panX: 0, panY: panY, panZ: 0}) },
front: function() { tweenCam({left: 0, up: PI2, panX: 0, panY: panY, panZ: 0}) },
right: function() { tweenCam({left: PI2, up: PI2, panX: 0, panY: panY, panZ: 0}) },
left: function() { tweenCam({left: -PI2, up: PI2, panX: 0, panY: panY, panZ: 0}) },
top: function(then) { tweenCam({left: home, up: 0, panX, panY, panZ, then}) },
back: function(then) { tweenCam({left: PI, up: PI2, panX, panY, panZ, then}) },
home: function(then) { tweenCam({left: home, up: PI4, panX, panY, panZ, then}) },
front: function(then) { tweenCam({left: 0, up: PI2, panX, panY, panZ, then}) },
right: function(then) { tweenCam({left: PI2, up: PI2, panX, panY, panZ, then}) },
left: function(then) { tweenCam({left: -PI2, up: PI2, panX, panY, panZ, then}) },
reset: function() { viewControl.reset(); requestRefresh() },
load: function(cam) { viewControl.setPosition(cam) },
save: function() { return viewControl.getPosition(true) },
@ -953,7 +967,30 @@
viewControl.setMouse(viewControl.mouseDefault);
}
},
getFPS: function() { return fps }
getFPS: function() { return fps },
getFocus: function() { return viewControl.getTarget() },
setFocus: function(v) {
viewControl.setTarget(v);
refresh();
},
setHome: function(r) {
home = r || 0;
},
spin: function(then, count) {
Space.view.front(() => {
Space.view.right(() => {
Space.view.back(() => {
Space.view.left(() => {
if (--count > 0) {
Space.view.spin(then, count);
} else {
Space.view.front(then);
}
});
});
});
});
}
},
mouse: {
@ -973,6 +1010,14 @@
selectRecurse = b;
},
setTweenTime: function(t) {
tweenTime = t || 500;
},
setTweenDelay: function(d) {
tweenDelay = d || 20;
},
objects: function() {
return WC;
},
@ -985,7 +1030,7 @@
return { renderer, camera, platform };
},
init: function(domelement, slider) {
init: function(domelement, slider, ortho) {
container = domelement;
WORLD.rotation.x = -PI2;
@ -998,14 +1043,16 @@
antialias: true,
preserveDrawingBuffer: true
});
camera = perspective ?
new THREE.PerspectiveCamera(perspective, aspect(), 5, 100000) :
new THREE.OrthographicCamera(-100 * aspect(), 100 * aspect(), 100, -100, 0.1, 100000);
camera = ortho ?
new THREE.OrthographicCamera(-100 * aspect(), 100 * aspect(), 100, -100, 0.1, 100000) :
new THREE.PerspectiveCamera(perspective, aspect(), 5, 100000);
camera.position.set(0, 200, 340);
renderer.setSize(width(), height());
domelement.appendChild(renderer.domElement);
raycaster = new THREE.Raycaster();
viewControl = new MOTO.CTRL(camera, domelement, function (position, moved) {
if (platform) {
platform.visible = hidePlatformBelow ?

View file

@ -43,12 +43,12 @@
#jbody, #gbody {
flex-grow: 1;
}
#jfoot {
#jfoot, #dfoot {
display: flex;
flex-direction: row;
justify-content: center;
}
#jfoot button {
#jfoot button, #dfoot button {
width: 100%;
}
#gbody {
@ -58,6 +58,7 @@
overflow: auto;
}
#gfoot {
display: none;
background-color: rgba(61,133,198,0.25);
border-top: 1px solid #aaa;
padding: 3px;
@ -66,6 +67,9 @@
#api {
width: 812px;
}
#download {
display: none;
}
li {
list-style: circle;
}
@ -77,7 +81,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ace.js"></script>
<script src="/code/engine.js"></script>
<script>
let edit;
let edit, saveit = "";
function $(id) { return document.getElementById(id) }
function onload() {
let fnstr = demo.toString().split("\n");
@ -97,10 +101,14 @@
$('gfoot').innerText = msg ? Object.keys(msg).join(' - ') : '';
}
function display_gcode(gcode) {
$('gbody').innerText = gcode;
$('gbody').innerText = saveit = gcode;
display_message();
$('dfoot').style.display = 'block';
$('gfoot').style.display = '';
}
function run_code() {
$('dfoot').style.display = 'none';
$('gfoot').style.display = 'block';
let code = edit.session.getValue();
eval(`(function(){${code}})()`);
}
@ -108,11 +116,28 @@
kiri.newEngine()
.setListener(display_message)
.load("/obj/cube.stl")
.then(eng => eng.setProcess({
sliceShells: 1,
sliceFillSparse: 0.25,
sliceTopLayers: 2,
sliceSolidLayers: 2,
sliceBottomLayers: 2
}))
.then(eng => eng.setDevice({
gcodePre: [ "M82", "M104 S220" ],
gcodePost: [ "M107" ]
}))
.then(eng => eng.slice())
.then(eng => eng.prepare())
.then(eng => eng.export())
.then(display_gcode);
}
function download() {
let blob = new Blob([saveit], {type: "octet/stream"});
let url = window.URL.createObjectURL(blob);
$('download').innerHTML = `<a id="xo" href="${url}" download="sample.gcode"></a>`;
$('xo').click();
}
</script>
</head>
<body onload="onload()">
@ -127,6 +152,7 @@
<div id="ghead">gcode output</div>
<div id="gbody"></div>
<div id="gfoot"></div>
<div id="dfoot"><button onclick="download()">download</button></div>
</div>
</div>
<div id="api">
@ -145,5 +171,6 @@
<li><label>prepare()</label> - async path routing of slice data</li>
<li><label>export()</label> - async gcode generation from path routing</li>
</div>
<a id="download"></a>
</body>
</html>

View file

@ -269,7 +269,7 @@ th, tr, td, label {
#app-mode, #app-acct {
width: 125px;
}
#app-mode:hover #mode-info {
#mode-info.hoverpop {
display: flex;
}
#mode-info {
@ -295,7 +295,7 @@ th, tr, td, label {
padding-right: 5px;
}
#app-name:hover #app-info {
#app-info.hoverpop {
display: flex;
}
#app-info {
@ -317,8 +317,8 @@ th, tr, td, label {
align-items: center;
justify-content: flex-end;
}
#app-acct:hover #acct-pop {
display: flex;
#acct-pop.hoverpop {
display: flex !important;
}
#acct-pop {
display: none;
@ -441,7 +441,7 @@ th, tr, td, label {
background-color: rgba(255,255,255,0.85);
border: 1px solid rgba(0,0,0,0.1);
border-radius: 3px;
width: 60px;
min-width: 60px;
color: var(--fg-gray);
}
.pop-lcol > div:hover {
@ -471,10 +471,10 @@ th, tr, td, label {
.lt-menu label {
font-size: smaller;
}
.lt-enabled {
.lt-enabled, .lt-active {
color: var(--fg-gray);
}
.lt-enabled:hover {
.lt-enabled:hover, .lt-active:hover {
background-color: #eee;
border: 1px solid #aaa;
color: #000;
@ -482,6 +482,9 @@ th, tr, td, label {
.lt-enabled:hover .pop-lcol {
display: flex;
}
.lt-active .hoverpop {
display: flex;
}
@media only screen and (max-height : 800px) {
.lt-menu, .pop-lcol {
font-size: 16px !important;
@ -524,7 +527,7 @@ th, tr, td, label {
.txt-sel button, .btn-sel {
background-color: #ccc;
}
#pop-rotate .value, #pop-scale .value {
#pop-rotate input.value, #pop-scale input.value {
width: 90%;
}
#pop-rotate label {
@ -870,8 +873,8 @@ th, tr, td, label {
min-height: 4em !important;
}
#code-preview-textarea {
width: 100%;
height: 250px;
width: 40em;
height: 12em;
font-size: 10px;
}
.mod-end {
@ -962,6 +965,8 @@ th, tr, td, label {
}
#help .content li {
line-height: 1.75em;
padding-left: 22px ;
text-indent: -22px ;
}
#help .footer {
font-family: monospace;
@ -1239,6 +1244,17 @@ th, tr, td, label {
width: 20px;
}
#top-slider {
position: absolute;
display: none;
top: 60px;
left: 50px;
right: 50px;
}
#anim-slider {
width: 100%;
}
#alert-area {
display: none;
justify-content: center;
@ -1500,3 +1516,43 @@ th, tr, td, label {
border-top: 1px solid #ddd;
margin: 5px 0 5px 0;
}
#lab-axis, #lab-scale {
padding: 0 6px 0 6px;
border-radius: 3px;
}
#lab-axis:hover, #lab-scale:hover {
background-color: rgba(0,0,0,0.2);
}
.range-info {
position: relative;
}
.range-info:hover .range-detail {
display: flex;
}
.range-detail {
position: absolute;
display: none;
flex-direction: column;
bottom: 50%;
right: 100%;
padding: 5px;
margin: 0 10px 0 10px;
font-size: smaller;
font-family: monospace;
transform: translateY(50%);
background-color: rgba(255,255,255,0.8);
border: 1px solid #aaa;
border-radius: 5px;
}
.range-detail label.pad {
padding-right: 8px;
border-bottom: 1px solid rgba(0,0,0,0.1);
}
.range-detail label.val {
font-weight: bold;
}
.range-detail span {
flex-grow: 1;
}

View file

@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="author" content="Stewart Allen">
<meta name="keywords" content="3d slicer,slicer,3d slicing,cnc,cam,toolpaths,toolpath generation,kiri,kirimoto,kiri:moto,gcode,cura,simplify 3d,prusaslicer,fdm,sla" />
<meta name="keywords" content="3d slicer,chromebook,slicer,3d slicing,cnc,cam,toolpaths,toolpath generation,kiri,kirimoto,kiri:moto,gcode,cura,simplify 3d,prusaslicer,fdm,sla" />
<meta name="copyright" content="stewart allen [sa@grid.space]">
<meta name="description" content="Browser-based 3D slicer, gcode and CNC toolpath generator">
<meta property="og:description" content="Kiri:Moto is a unique 3D slicer that runs entirely in browser and creates output for your favorite maker tools: 3D Printers, CNC Mills and Laser Cutters.">
@ -45,17 +45,20 @@
<div id="acct-pop" class="f-col">
<div id="acct-menu" class="f-col">
<div id="acct-lang">
<label><i class="fas fa-caret-left"></i>&nbsp;language</label>
<label><i class="fas fa-caret-left"></i>&nbsp;<span lk="language">language</span></label>
<div id="lang-pop" class="f-col">
<div id="lset-en">english</div>
<div id="lset-da">danish</div>
<div id="lset-en">english</div>
<div id="lset-fr">french</div>
<div id="lset-de">german</div>
<div id="lset-pl">polish</div>
</div>
</div>
<div id="acct-export" class="f-col a-end">
<label>export</label>
<label lk="export">export</label>
</div>
<div id="acct-help" class="f-col a-end">
<label>help</label>
<label lk="help">help</label>
</div>
</div>
</div>
@ -72,104 +75,104 @@
<i class="fas fa-times-circle"></i>
</div>
<div id="lt-top"></div>
<div id="lt-setup" class="lt-menu lt-enabled">
<div id="lt-setup" class="lt-menu lt-active">
<i class="fas fa-cogs"></i>
<label>setup</label>
<label lk="setup">setup</label>
<div id="set-pop" class="pop-lcol f-row">
<div id="set-device">
<i class="fas fa-cube"></i>
<label>machine</label>
<label lk="machine">machine</label>
</div>
<div id="set-tools">
<i class="fas fa-tools"></i>
<label>tools<label>
<label lk="tools">tools<label>
</div>
<div id="set-prefs">
<i class="fas fa-sliders-h"></i>
<label>prefs<label>
<label lk="prefs">prefs<label>
</div>
</div>
</div>
<div id="lt-file" class="lt-menu lt-enabled">
<div id="lt-file" class="lt-menu lt-active">
<i class="fas fa-folder"></i>
<label>files</label>
<label lk="files">files</label>
<div id="file-pop" class="pop-lcol f-row">
<div id="file-recent">
<i class="fas fa-list"></i>
<label>recent</label>
<label lk="recent">recent</label>
</div>
<div id="file-import">
<i class="fas fa-file-upload"></i>
<label>import<label>
<label lk="import">import<label>
</div>
</div>
</div>
<div id="act-sep"></div>
<div id="lt-view" class="lt-menu lt-enabled">
<div id="lt-view" class="lt-menu lt-active">
<i class="fas fa-eye"></i>
<label>view</label>
<label lk="view">view</label>
<div id="pop-view" class="pop-lcol f-row">
<div id="view-arrange">
<i class="fas fa-th"></i>
<label>arrange</label>
<label lk="arrange">arrange</label>
</div>
<div id="view-top">
<i class="fas fa-square"></i>
<label>top</label>
<label lk="top">top</label>
</div>
<div id="view-home">
<i class="fas fa-home"></i>
<label>home</label>
<label lk="home">home</label>
</div>
<div id="view-clear">
<i class="far fa-square"></i>
<label>clear</label>
<label lk="clear">clear</label>
</div>
</div>
</div>
<div id="act-slice" class="lt-menu lt-enabled">
<div id="act-slice" class="lt-menu lt-active">
<i class="fas fa-bars"></i>
<label id="label-slice" title="analyze parts">slice</label>
<label id="label-slice" title="analyze parts" lk="slice">slice</label>
<div id="pop-slice" class="pop-lcol f-row">
<div id="act-preview">
<i class="fas fa-cubes"></i>
<label id="label-preview" title="show routing and paths">preview</label>
<label id="label-preview" title="show routing and paths" lk="preview">preview</label>
</div>
<div id="act-animate">
<i class="fas fa-running"></i>
<label id="label-animate" title="render routing on a mesh">animate</label>
<label id="label-animate" title="render routing on a mesh" lk="animate">animate</label>
</div>
<div id="act-export">
<i class="fas fa-file-download"></i>
<label id="label-export" title="generate downloadable files">export</label>
<label id="label-export" title="generate downloadable files" lk="export">export</label>
</div>
</div>
</div>
<div id="lt-sep"></div>
<div id="lt-render" class="lt-menu lt-enabled">
<div id="lt-render" class="lt-menu lt-active">
<i class="fas fa-border-style"></i>
<label>render</label>
<label lk="render">render</label>
<div id="pop-render" class="pop-lcol f-row">
<div id="render-solid" class="grow a-center">
<i class="fas fa-th-large"></i>
<label>solid</label>
<label lk="solid">solid</label>
</div>
<div id="render-wire" class="grow a-center">
<i class="fas fa-border-all"></i>
<label>wire</label>
<label lk="wire">wire</label>
</div>
<div id="render-ghost" class="grow a-center">
<i class="fas fa-border-none"></i>
<label>ghost</label>
<label lk="ghost">ghost</label>
</div>
<div id="render-hide" class="grow a-center">
<label>hide</label>
<label lk="hide">hide</label>
</div>
</div>
</div>
<div id="lt-rotate" class="lt-menu">
<i class="fas fa-sync-alt"></i>
<label>rotate</label>
<label lk="rotate">rotate</label>
<div id="pop-rotate" class="pop-lcol f-row russo">
<div>
<div class="grow a-center">
@ -196,12 +199,12 @@
<input id="rot_z" class="value center" size="5"/ value="0">
</div>
<!-- <div id="lay-flat" class="f-col j-center center">lay flat</div> -->
<div id="unrotate" class="f-col j-center">reset</div>
<div id="unrotate" class="f-col j-center" lk="reset">reset</div>
</div>
</div>
<div id="lt-scale" class="lt-menu">
<i class="fas fa-expand"></i>
<label>scale</label>
<label lk="scale">scale</label>
<div id="pop-scale" class="pop-lcol f-row russo">
<div>
<input id="lock_x" type="checkbox" checked/>
@ -222,9 +225,9 @@
<div><input id="scale_z" xsize="8" class="value" value="1"/></div>
</div>
<div class="f-col j-center">
<div><label>axis</label></div>
<div><label>size</label></div>
<div><label>scale</label></div>
<div><label id="lab-axis" lk="axis">axis</label></div>
<div><label id="lab-size" lk="size">size</label></div>
<div><label id="lab-scale" lk="scale">scale</label></div>
</div>
</div>
</div>
@ -249,7 +252,7 @@
</div>
<div id="mod-setup" class="mdialog f-row">
<div class="f-col left a-stretch">
<label class="header">select a device type</label>
<label class="header" lk="dm_sldt">select a device type</label>
<div class="f-col">
<table><tr>
<th><button id="mode-fdm" class="dev-type"><label>FDM</label></button></th>
@ -260,7 +263,7 @@
</tr></table>
</div>
<div class="header sub f-col j-center">
<label>standard devices</label>
<label lk="dm_stdd">standard devices</label>
<div id="dev-search">
<i class="fas fa-search"></i>
<input id="dev-filter" />
@ -269,20 +272,20 @@
<div class="f-col mh0 mh35 j-center a-stretch">
<div id="device-list" class="devlist f-col overy"></div>
</div>
<label class="header sub">my devices</label>
<label class="header sub" lk="dm_mydd">my devices</label>
<div class="f-col grow">
<div id="device-my" class="devlist f-col grow overy"></div>
<div class="f-row footer">
<div class="grow"></div>
<button id="device-add">copy</button>
<button id="device-del">delete</button>
<button id="device-exp">export</button>
<button id="device-add" lk="copy">copy</button>
<button id="device-del" lk="delete">delete</button>
<button id="device-exp" lk="export">export</button>
<div class="grow"></div>
</div>
</div>
</div>
<div class="f-col grow a-stretch">
<label class="header">selected device</label>
<label class="header" lk="dm_seld">selected device</label>
<div class="f-row">
<div id="device1" class="f-col t-body t-inset overy grow"></div>
<div class="t-pad"></div>
@ -296,27 +299,27 @@
</div>
<div id="device-action" class="f-row footer">
<div class="grow"></div>
<button id="device-save">save</button>
<button id="device-save" lk="save">save</button>
<div class="grow"></div>
</div>
</div>
</div>
<div id="mod-files" class="mdialog f-col">
<label class="set-header">recent files</label>
<label class="set-header" lk="dm_rcnt">recent files</label>
<div id="catalogList" class="f-col"></div>
</div>
<div id="mod-saves" class="mdialog f-col">
<label class="set-header">saved settings</label>
<label class="set-header" lk="dm_savs">saved settings</label>
<div id="settingsList" class="f-col"></div>
</div>
<div id="mod-tools" class="mdialog">
<div class="f-col">
<div class="f-row t-head">
<label class="t-33">tool</label>
<label class="t-33" lk="tool">tool</label>
<div class="t-pad"></div>
<label class="t-33">detail</label>
<label class="t-33" lk="detail">detail</label>
<div class="t-pad"></div>
<label class="t-33">view</label>
<label class="t-33" lk="view">view</label>
</div>
<div class="f-row">
<div class="f-col t-33">
@ -324,23 +327,23 @@
</div>
<div class="t-pad"></div>
<div class="f-col t-33 t-body t-inset">
<div class="f-row var-row"><label>name</label><input id="tool-name" size="15"></input></div>
<div class="f-row var-row"><label>type</label>
<div class="f-row var-row"><label lk="name">name</label><input id="tool-name" size="15"></input></div>
<div class="f-row var-row"><label lk="type">type</label>
<select id="tool-type">
<option value="endmill" selected>end</option>
<option value="ballmill">ball</option>
<option value="tapermill">taper</option>
<option value="endmill" lk="td_tyem" selected>end</option>
<option value="ballmill" lk="td_tybm">ball</option>
<option value="tapermill" lk="td_tytm">taper</option>
</select>
</div>
<div class="f-row var-row" title="tool number to use&#010;in gcode commands"><label>tool #</label><input id="tool-num" size="7"></input></div>
<div class="f-row var-row"><label>metric</label><input id="tool-metric" type="checkbox"></input></div>
<div class="set-header f-col">shaft</div>
<div class="f-row var-row" title="tool number to use&#010;in gcode commands"><label lk="td_tonm">tool #</label><input id="tool-num" size="7"></input></div>
<div class="f-row var-row"><label lk="metric">metric</label><input id="tool-metric" type="checkbox"></input></div>
<div class="set-header f-col" lk="td_shft">shaft</div>
<div class="f-row var-row" title="shaft diameter in inches&#010;unless metric is checked&#010;then in millimeters"><label>diameter</label><input id="tool-sdiam" size="7"></input></div>
<div class="f-row var-row" title="shaft length in inches&#010;unless metric is checked&#010;then in millimeters"><label>length</label><input id="tool-slen" size="7"></input></div>
<div class="set-header f-col">flute</div>
<div class="set-header f-col" lk="td_flut">flute</div>
<div class="f-row var-row" title="flute diameter in inches&#010;unless metric is checked&#010;then in millimeters"><label>diameter</label><input id="tool-fdiam" size="7"></input></div>
<div class="f-row var-row" title="flute length in inches&#010;unless metric is checked&#010;then in millimeters"><label>length</label><input id="tool-flen" size="7"></input></div>
<div class="set-header f-col">taper</div>
<div class="set-header f-col" ln="td_tapr">taper</div>
<!-- <div class="flow-row" title="taper angle"><label>angle</label><input id="tool-tangle" size=5></input></div> -->
<div class="f-row var-row" title="tip width in inches&#010;unless metric is checked&#010;then in millimeters"><label>tip</label><input id="tool-ttip" size="7"></input></div>
</div>
@ -351,13 +354,13 @@
<button id="tool-add"><i class="fas fa-plus"></i></button>
<button id="tool-del"><i class="fas fa-minus"></i></button>
<div class="t-pad2"></div>
<button id="tools-save">save</button>
<button id="tools-close">done</button>
<button id="tools-save" lk="save">save</button>
<button id="tools-close" lk="done">done</button>
</div>
</div>
</div>
<div id="mod-prefs" class="mdialog f-col a-center">
<b>Application Preferences</b>
<b lk="dm_appp">Application Preferences</b>
<hr width="80%">
<div class="f-row">
<div id="prefs-gen1" class="f-col"></div>
@ -378,7 +381,7 @@
<div id="mod-print" class="mdialog f-col"></div>
<div id="mod-any" class="mdialog f-col"></div>
<!-- hidden file input loader -->
<input id="load-file" type="file" name="loadme" style="display:none" accept=".stl,.gcode,.nc,.km,.b64,.png"/>
<input id="load-file" type="file" name="loadme" style="display:none"/>
<div class="mod-end"></div></div>
</div>
<div id="layer-slider" class="f-col a-stretch">
@ -440,13 +443,14 @@
</div>
<div id="tool-info" class="noshow"></div>
<div id="context-menu">
<button id="context-clear-workspace">clear workspace</button>
<button id="context-export-workspace">export workspace</button>
<button id="context-clear-workspace" lk="rc_clws">clear workspace</button>
<button id="context-export-workspace" lk="rc_xpws">export workspace</button>
<div class="sep"></div>
<button id="context-mirror">mirror</button>
<button id="context-duplicate">duplicate</button>
<button id="context-layflat" lk="rc_lafl">lay flat</button>
<button id="context-mirror" lk="rc_mirr">mirror</button>
<button id="context-duplicate" lk="rc_dupl">duplicate</button>
<div class="sep"></div>
<button id="context-export-stl">export as STL</button>
<button id="context-export-stl" lk="rc_xstl">export as STL</button>
</div>
<div id="fps" class="noshow"></div>
<div id="layer-animate" class="f-col a-center"></div>
@ -457,6 +461,9 @@
<div id="alert-text">alerts<br>alerts<br>alerts</div>
</div>
</div>
<div id="top-slider" class="f-col grow">
<input id="anim-slider" type="range" min="0" max="1000" value="0"></input>
</div>
</div>
</body>
</html>

View file

@ -67,8 +67,7 @@
<tr><td>mouse-wheel</td><td>Zoom</td></tr>
<tr><td>middle-drag</td><td>Zoom</td></tr>
<tr><td>2-touch-pinch</td><td>Zoom</td></tr>
</table>
<table>
<tr><td><br><br></td></tr>
<tr><th colspan=2 class="header">Konfiguration</th></tr>
<tr><td><label>?</label></td><td>Dette Hjælpe Vindue</td></tr>
<tr><td><label>e</label></td><td>Enhed Udvælger</td></tr>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
de-help.html

640
web/kiri/lang/de-de.js Normal file
View file

@ -0,0 +1,640 @@
// english. other language maps will defer to english
// map for any missing key/value pairs
kiri.lang['de'] =
kiri.lang['de-de'] = {
// common keys and menus
animate: "Animieren", // CAM animate button
arrange: "Anordnen", // layout workspace objects
axis: "Achse", // left object scale pop menu
clear: "alles entfernen", // clear workspace (remove all objects)
copy: "Kopieren",
delete: "Löschen",
detail: "Details",
done: "Fertig",
enable: "Aktivieren",
export: "Export",
files: "Dateien",
help: "Hilfe",
ghost: "Geist", // left render pop menu (wireframe)
hide: "Verbergen", // left render pop menu (invisible)
home: "home",
import: "Import",
language: "Sprache",
machine: "Maschine", // device or machine
metric: "Metrisch",
name: "Name",
prefs: "Einstellungen", // left menu "preferences"
preview: "Vorschau",
recent: "Letzte Objekte",
render: "Rendern", // left render pop menu
reset: "Zurücksetzen",
rotate: "Rotieren", // left rotate pop menu
save: "Speichern",
scale: "Skalieren", // left object scale pop menu
setup: "Einrichtung",
settings: "Einstellungen",
size: "Größe",
solid: "Undurchsichtig", // view type pop menu
tool: "Werkzeug",
tools: "Werkzeuge", // CAM tool menu button
top: "Oben",
type: "Typ", // CAM tool type
version: "Version",
view: "Ansicht", // left view pop menu
wire: "Drahtansicht", // left render pop menu
acct_xpo: ["Lege ein Backup Deiner Geräte","und Geräte-Profile an, mit der","Option, den Arbeitsbereich und","mit Objekten und Anordnung zu speichern"],
// RIGHT-CLICK CONTEXT MENU
rc_clws: "Arbeitsbereich freiräumen",
rc_xpws: "Arbeitsbereich exportieren",
rc_lafl: "Auf das Bett",
rc_mirr: "Spiegeln",
rc_dupl: "Duplizieren",
rc_xstl: "Als STL exportieren",
// DEVICE MENU and related dialogs
dm_sldt: "Wähle einen Geräte-Typ aus",
dm_stdd: "Vordefinierte Geräte",
dm_mydd: "Meine Geräte",
dm_seld: "Ausgweähltes Gerät",
dm_rcnt: "Letzte Dateien",
dm_savs: "Gespeicherte Voreinstellungen",
dm_appp: "Anwendungseinsetllungen",
// CAM Tool Dialog
td_tyem: "end", // end mill
td_tybm: "ball", // ball mill
td_tytm: "taper", // taper mill
td_tonm: "tool #",
td_shft: "shaft", // endmill shaft specs
td_flut: "flute", // endmill flute specs
td_tapr: "taper", // endmill taper specs
// DEVICE dialog groups
dv_gr_dev: "Gerät",
dv_gr_ext: "Extruder",
dv_gr_out: "Output",
dv_gr_gco: "GCode Macros",
// DEVICE dialog (_s = label, _l = hover help)
dv_name_s: "Name",
dv_name_l: "Geräte-Name",
dv_fila_s: "Filament",
dv_fila_l: "Durchmesser in mm",
dv_nozl_s: "Düse",
dv_nozl_l: "Durchmesser in mm",
dv_bedw_s: "Breite",
dv_bedw_l: "Maßeinheiten des Arbeitsbereichs",
dv_bedd_s: "Tiefe",
dv_bedd_l: "Maßeinheiten der Arbeitsfläche",
dv_bedh_s: "Höhe",
dv_bedh_l: ["Maximale Objekthöhe","in Maßeinheiten der Arbeitsfläche"],
dv_spmx_s: "Spindel-Maximum",
dv_spmx_l: ["Maximale Umdrehungen pro Minute der Spindel","0 zum deaktivieren"],
dv_xtab_s: "Absolute Positionierung",
dv_xtab_l: "Filament wird mit absoluter Positionierung extrudiert",
dv_orgc_s: "Nullpunkt in der Mitte",
dv_orgc_l: "Nullpunkt des Druckbetts in der Mitte",
// dv_orgt_s: "origin top",
// dv_orgt_l: "part z origin top",
dv_bedc_s: "Rundes Druckbett",
dv_bedc_l: "Der Druckbereich des Gerätes ist rund",
dv_belt_s: "Riemen-Druckbett",
dv_belt_l: "Für bewegliche Druckbetten",
dv_fanp_s: "Lüfter-Einstellung",
dv_fanp_l: "Wie stark der Lüfter eingestellt sein soll",
dv_prog_s: "Fortschrittsanzeige",
dv_prog_l: "Wenn für jedes % der Fertigstellung eine Nachicht angezeigt werden soll",
dv_layr_s: "Ebene",
dv_layr_l: ["Ausgabe bei jeder","neuen Ebene"],
dv_tksp_s: "Befehls-Trenner",
dv_tksp_l: ["Füge ein Leerzeichen ein bei","GCode-Achsen-Befehlen","G0X0Y0X0","oder","G0 X0 Y0 Z0"],
dv_strc_s: "Kommentare entfernen",
dv_strc_l: ["Kommentare aus GCode entfernen","Kommentare beginnen mit ;"],
dv_fext_s: "Datei-Erweiterung",
dv_fext_l: "Datei-Erweiterung",
dv_dwll_s: "dwell",
dv_dwll_l: "gcode dwell script",
dv_tool_s: "Werkzeg-Wechel",
dv_tool_l: "Skript zum Werkzegwechsel",
dv_sspd_s: "Spindelgeschwindigkeit",
dv_sspd_l: "Einstellung der Spindelgeschwindigkeit",
dv_paus_s: "Pause",
dv_paus_l: "Skript zum Ausführen bei einer Pause",
dv_head_s: "Anfang",
dv_head_l: "Skript für den Anfang einer GCode-Datei",
dv_foot_s: "Ende",
dv_foot_l: "Skript für das Ende einer GCode-Datei",
dv_lzon_s: "Laser an",
dv_lzon_l: "Skript, um den Laser einzuschalten",
dv_lzof_s: "Laser aus",
dv_lzof_l: "Skript, um den Laser auszuschalten",
dv_exts_s: "Auswhälen",
dv_exts_l: "GCode, um diesen Extruder auszuwählen",
dv_dext_s: "Abwählen",
dv_dext_l: "GCode, bevor ein anderer Extruder ausgewählt wird",
dv_extd_s: "Deaktivieren",
dv_extd_l: "GCode, um diesen Extruder zu deaktivieren",
dv_exox_s: "X-Offset",
dv_exox_l: "X-Offset der Düse",
dv_exoy_s: "Y-Offset",
dv_exoy_l: "Y-Offset der Düse",
// MODE
mo_menu: "Arbeits-Modus",
mo_fdmp: "FDM-Druck",
mo_slap: "SLA-Druck",
mo_lazr: "Laser-Cutting",
mo_cncm: "CNC-Fräsen",
// SETUP
su_menu: "setup",
su_devi: "Geräte",
su_tool: "Werkzeuge",
su_locl: "Lokal",
su_xprt: "Export",
su_help: "Hilfe",
// LOAD
fe_menu: "Datei",
fn_recn: "Letzte",
fn_impo: "Import",
// FUNCTION
fn_menu: "Modus",
fn_arra: "Anordnen",
fn_slic: "Slice",
fn_prev: "Vorschau",
fn_expo: "Export",
// VIEW
vu_menu: "Ansicht",
vu_home: "Standard",
vu_rset: "Zurücksetzen",
vu_sptp: "Oben",
vu_spfr: "Vorn",
vu_splt: "Links",
vu_sprt: "Ansicht",
// WORKSPACE
ws_menu: "Ansicht",
ws_save: "Speichern",
ws_cler: "Leeren",
// OPTIONS
op_menu: "interface",
op_disp: "Anzeige",
op_xprt_s: "Fortgeschritten",
op_xprt_l: "weitere Einstellungen",
op_decl_s: "decals",
op_decl_l: "show device decals and logos",
op_dang_s: "Experimentell",
op_dang_l: "Zeige experimentelle Parameter",
op_hopo_s: "hover pop",
op_hopo_l: ["enable menu hover","to activate"],
op_dark_s: "Dunkler Modus",
op_dark_l: "Dunkles Interface",
op_comp_s: "Kompakt",
op_comp_l: ["Kompakte Ansicht","Besser für kleine Bildschirme","und Tablets"],
op_shor_s: "Nullpunkt anzeigen",
op_shor_l: "Zeige den Nullpunkt des Gerätes oder des Prozesses",
op_shru_s: "Lineal einblenden",
op_shru_l: ["Zeige Lineale mit Maßen","auf den Hauptachsen"],
op_sped_s: "Geschwindigkeiten einblenden",
op_sped_l: ["Zeige Legende für Farben und Geschwindigkeiten","in der Vorschau"],
op_auto_s: "Automatische Anordnung",
op_auto_l: ["Ordne automatisch an","wenn neue Objekte hinzugefügt werden"],
op_free_s: "Freie Anordnung",
op_free_l: ["Erlaube Freie Anordnung von Objekten","Keine Auswirkung im Laser-Cutting Arbeitsmodus"],
op_spcr_s: "Abstand",
op_spcr_l: ["Abstand zwischen Objekten","bei automatischer Anordnung","in Maßeinheiten der Arbeitsfläche"],
op_invr_s: "Zoom Umkehren",
op_invr_l: ["Kehre die Richtung beim Mausrad-","Zoomen um"],
op_save_s: "Automatisch speichern",
op_save_l: ["Behalte Objekte im Arbeitsbereich","wenn die Anwendung neu geladen wird"],
op_line_s: "Darstellungs-Typ",
op_line_l: ["Wie Linien bei der Darstellung der Pfade gezeichnet werden","Beeinflußt 3D-Performance","Pfad: 3D (beste)","Flach: 2d (gut)","Linie = 1d (schnell)"],
op_unit_s: "Maßeinheiten",
op_unit_l: ["die Maßeinheit des Arbeitsbereich wirkt sich auf die Darstellung von ","Geschwindigkeiten und Entfernungen aus"],
op_anim_s: "Animieren",
op_anim_l: ["Dichte des Netzgitters beim Animieren","Je höher, desto dichter","benötigt mehr Speicher","und ist langsamer"],
lo_menu: "Anordnung",
pt_menu: "Objekte",
pt_deci_s: "Deizimierung",
pt_deci_l: ["Punktdezimierung","während des Imports. Für schnelleres Slicen","und weniger Speicherverbrauch"],
pt_qual_s: "Qualität",
pt_qual_l: ["Detailstufe beim","Slicen","geringer ist schneller"],
pt_heal_s: "Mesh reparieren",
pt_heal_l: ["Versuche","defekte Meshes zu reparieren","wirkt sich auf die Verarbeitungszeit us"],
xp_menu: "Export",
// SETTINGS
se_menu: "Profil",
se_load: "Laden",
se_save: "Speichern",
// FDM SLICING
sl_menu: "Schichten",
sl_lahi_s: "Höhe",
sl_lahi_l: ["Höhe jeder Schicht","Schichthöhe in mm"],
ad_minl_s: "Mindesthöhe",
ad_minl_l: ["Mindesthöhe für adaptive Schichthöhe","in mm","Darf nicht null sein"],
sl_ltop_s: "top layers",
sl_ltop_l: ["number of solid layers","to enforce at the","top of the print"],
sl_lsld_s: "solid layers",
sl_lsld_l: ["solid fill areas computed","from layer deltas. see","layer pop menu"],
sl_lbot_s: "base layers",
sl_lbot_l: ["Anzahl der soliden Schichten","um die Unterseite des","Drucks zu verstärken"],
ad_adap_s: "Adaptive Höhe",
ad_adap_l: ["Verwende adaptive Schichthöhe","mit 'Maximale Höhe' als Maximum","und 'Mindesthöhe' als Minimum"],
// FDM SHELLS
sl_shel_s: "Anzahl Außenwände",
sl_shel_l: ["Anzahl der äußeren","Wände, die erzeugt werden sollen"],
sl_ordr_s: "Druckreihenfolge der Wände",
sl_ordr_l: ["In welcher Reihenfolge die Wände gedruckt werden","innen nach aussen","oder aussen nach innen","wirkt sich auf die Oberflächenqualität aus"],
sl_strt_s: "Start der Ebene",
sl_strt_l: ["Startpunkt der Ebene","Letzte = Ende der letzten Ebene ","Mitte = Zur Mitte des Objekts","Nullpunkt = Zum Nullpunkt des Gerätes"],
ad_thin_s: "Dünne Wände",
ad_thin_l: ["Finde und fülle schmale Lücken","zwischen Außenwänden"],
// FDM FILL
fi_menu: "Füllmaterial",
fi_type: "Typ",
fi_pcnt_s: "Füll-Dichte",
fi_pcnt_l: ["Füll-Dichte ","0.0 - 1.0"],
fi_angl_s: "Startwinkel für volle Füllung",
fi_angl_l: ["Startwinkel in Grad","90 Grad werden ","bei jeder folgender Schicht dazugerechnet","Wirkt sich nur auf vollgefüllte Schichten aus."],
fi_over_s: "Schicht-Überlappung",
fi_over_l: ["Überlappung von Schichten und Füllung","als Anteil des Düsen-Durchmessers","0.0 - 1.0"],
// fi_rate_s: "print speed",
fi_rate_l: ["Druckgeschwindigkeit für Füllmaterial","Auf 0 setzen, um die Standard-","Druckgeschwindigkeit zu verwenden"],
// FDM FIRST LAYER
fl_menu: "Erste Schicht",
fl_lahi_s: "Schichthöhe",
fl_lahi_l: ["Höhe der ersten Schicht","in in mm","sollte >= der Standard-Schichthöhe sein"],
fl_rate_s: "Wand-Geschwindigkeit",
fl_rate_l: ["Maximalgeschwindigkeit beim Drucken von Wänden","in mm / min"],
fl_frat_s: "Füll-Geschwindikeit",
fl_frat_l: ["Maximalgeschwindikeit beim Drucken von Füllung","in mm / min"],
fl_mult_s: "Extrusions-Faktor",
fl_mult_l: ["Exktrusions-Faktor","0.0 - 2.0"],
fl_sfac_s: "Breiten-Faktor",
fl_sfac_l: ["Multipliziert mit dem Düsenfaktor","beeinflußt die Breite der Drucklinien"],
fl_skrt_s: "Anzahl der Skirt-Linien",
fl_skrt_l: ["Wieviele Linien um die erste Schicht","herum generiert werden sollen"],
fl_skro_s: "Skirt-Abstand",
fl_skro_l: ["Abstand des Skirts vom Objekt","in mm"],
fl_nozl_s: "Drucktemperatur",
fl_nozl_l: ["in Grad Celsius","Standard-Einstellung wird verwendet","wenn auf 0 gestellt"],
fl_bedd_s: "Betttemperatur",
fl_bedd_l: ["in Grad Celsius","Standard-Einstellung wird verwendet","when auf 0 gestellt"],
fr_spac_s: "Raft Abstand",
fr_spac_l: ["Zusätzlicher Abstand","zwischen erster Schicht und Raft","in mm"],
fr_nabl_s: "Raft verwenden",
fr_nabl_l: ["Erzeuge ein Raft unter dem","Objekt für bessere Betthaftung","verwendet Skirt-Abstand und","Deaktiviert die Erzeugung eines Skirt"],
// FDM BELT ONLY
fl_zoff_s: "belt offset",
fl_zoff_l: ["height offset from belt","of lowest extrusion","in millimeters"],
fl_brim_s: "Größe des Brim",
fl_brim_l: ["Brim am Fuß des Objekts erzeugen","Größe in mm","0 um zu deaktivieren"],
fl_brmn_s: "Brim-Auslöser",
fl_brmn_l: ["Erzeuge nur Brim, wenn die Fläche","auf dem Gurt kürzer ist als dieser Wert","Angegeben in mm","0 = Immer"],
fl_bled_s: "Objekt verankern",
fl_bled_l: ["Objekt mit dem Gurt verankern","zu Beginn des Drucks","in mm"],
// FDM SUPPORT
sp_menu: "Stützstrukturen",
sp_detect: "Analysieren",
sp_dens_s: "Dichte",
sp_dens_l: ["Anteil 0.0 - 1.0","empfohlen 0.15","0 zum deaktivieren"],
sp_size_s: "Breite der Säule",
sp_size_l: ["Breite der Säule","in mm"],
sp_offs_s: "Abstand",
sp_offs_l: ["Abstand vom Objekt","in mm"],
sp_gaps_s: "Schicht-Abstand",
sp_gaps_l: ["Anzahl der Schichten","zwischen Stützstruktur und Objekt"],
sp_span_s: "Maximale Spannbreite",
sp_span_l: ["Für Spannen über dieser Breite","werden Stützstrukturen erzeugt","in mm"],
sp_angl_s: "Maximaler Winkel",
sp_angl_l: ["Für Überhänge über diesem Winkel","werden Stützstrukturen erzeugt"],
sp_area_s: "Mindestfläche",
sp_area_l: ["Mindestfläche für","Eine Stützsäule","in mm"],
sp_xpnd_s: "Ausdehung",
sp_xpnd_l: ["Dehne die Stützstruktur","über die Objekt-Grenzen aus","in mm"],
sp_nozl_s: "Extruder",
sp_nozl_l: ["In Geräten mit mehreren Extrudern","welcher Extruder wird für","Stützmaterial verwendet"],
sp_auto_s: "Automatisch",
sp_auto_l: ["Erzeuge Stützstrukturen automatisch","beim Slicen","Stützstruktuen werden erst","Nach dem Slicen angezeigt"],
// LASER SLICING
ls_offs_s: "Abstand",
ls_offs_l: ["Anpassung for Strahlbreite","in millimeters"],
ls_lahi_s: "Höhe",
ls_lahi_l: ["Schichthöhe","in mm","0 = auto/erkennen"],
ls_lahm_s: "min",
ls_lahm_l: ["Minimale Schichthöhe","Autoslices unter dieser Höhe","werden zusammengeführt","in millimeters"],
ls_sngl_s: "Einfach",
ls_sngl_l: ["Nur einen Slice bei","angegebener Schichthöhe ausführen"],
// CNC COMMON terms
cc_tool: "Werkzeug",
cc_offs_s: "offset",
cc_offs_l: ["offset tool center","from chosen path"],
cc_spnd_s: "spindle rpm",
cc_spnd_l: ["spindle speed in","revolutions / minute"],
cc_sovr_s: "step over",
cc_sovr_l: ["as a fraction of","tool diameter"],
cc_sdwn_s: "step down",
cc_sdwn_l: ["step down depth","for each pass","in workspace units","0 to disable"],
cc_feed_s: "feed rate",
cc_feed_l: ["max cutting speed in","workspace units / minute"],
cc_plng_s: "plunge rate",
cc_plng_l: ["max z axis speed in","workspace units / minute"],
cc_sngl_s: "select lines only",
cc_sngl_l: ["select only single edges","instead of connected polylines"],
// CNC COMMON
cc_menu: "limits",
cc_flip: "flip",
cc_rapd_s: "xy feed",
cc_rapd_l: ["max xy moves feedrate","in workspace units / minute"],
cc_rzpd_s: "z feed",
cc_rzpd_l: ["max z moves feedrate","in workspace units / minute"],
cc_loff_s: "offset",
cc_loff_l: ["distance from stock face","for leveling pass","in workspace units"],
// CNC ROUGHING
cr_menu: "rough",
cr_lsto_s: "leave stock",
cr_lsto_l: ["horizontal offset from vertical faces","stock to leave for finishing pass","in workspace units"],
cr_ease_s: "ease down",
cr_ease_l: ["plunge cuts will","spiral down or ease","along a linear path"],
cr_clrt_s: "clear top",
cr_clrt_l: ["run a clearing pass over","the bounding area of the part","at z = 0"],
cr_clrp_s: "clear voids",
cr_clrp_l: ["mill out through pockets","instead of just the outline"],
cr_clrf_s: "clear faces",
cr_clrf_l: ["interpolate step down to","clear any detected flat areas"],
cr_olin_s: "inside only",
cr_olin_l: ["limit cutting to","inside part boundaries"],
// CNC OUTLINE
co_menu: "outline",
co_dogb_s: "dogbones",
co_dogb_l: ["insert dogbone cuts","into inside corners"],
co_wide_s: "wide cutout",
co_wide_l: ["widen outside cutout paths","for deep cuts in hard material"],
co_olin_s: "inside only",
co_olin_l: ["limit cutting to","inside part boundaries"],
co_olot_s: "outside only",
co_olot_l: ["limit cutting to","exterior part boundaries","which can be thought of","as the shadow outline"],
co_omit_s: "omit through",
co_omit_l: "eliminate thru holes",
co_olen_s: "enable",
co_olen_l: "enabled outline cutting",
// CNC CONTOUR
cn_menu: "contour",
cf_angl_s: "max angle",
cf_angl_l: ["angles greater than this","are considered vertical"],
cf_curv_s: "curves only",
cf_curv_l: ["limit linear cleanup","to curved surfaces"],
cf_olin_s: "inside only",
cf_olin_l: ["limit cutting to","inside part boundaries"],
cf_linx_s: "enable y pass",
cf_linx_l: "linear y-axis finishing",
cf_liny_s: "enable x pass",
cf_liny_l: "linear x-axis finishing",
// CNC TRACE
cu_menu: "trace",
cu_type_s: "type",
cu_type_l: ["follow = tool tip follows line","right or left = tool tip","follows line offset by tool radius"],
// CNC DRILLING
cd_menu: "drill",
cd_axis: "axis",
cd_points: "points",
cd_plpr_s: "plunge per",
cd_plpr_l: ["max plunge between","dwell periods","in workspace units","0 to disable"],
cd_dwll_s: "dwell time",
cd_dwll_l: ["dwell time","between plunges in","in milliseconds"],
cd_lift_s: "drill lift",
cd_lift_l: ["lift between plunges","after dwell period","in workspace units","0 to disable"],
cd_regi_s: "register",
cd_regi_l: ["drill registration holes","for double-sided parts","independent of enable","drilling but uses same","tool and settings"],
// CNC CUTOUT TABS
ct_menu: "tabs",
ct_angl_s: "angle",
ct_angl_l: ["starting angle for tab spacing","in degrees (0-360)"],
ct_numb_s: "count",
ct_numb_l: ["number of tabs to use","will be spaced evenly","around the part"],
ct_wdth_s: "width",
ct_wdth_l: "width in workspace units",
ct_hght_s: "height",
ct_hght_l: "height in workspace units",
ct_dpth_s: "depth",
ct_dpth_l: ["distance in workspace units","that the tab projects from","the part surface"],
ct_midl_s: "midline",
ct_midl_l: ["use midline of tab","instead of z bottom","for double-sided work"],
ct_nabl_s: "auto",
ct_nabl_l: ["auto generate radial tabs","projected from part center","using count and angle offset"],
// OUTPUT
ou_menu: "output",
// LASER KNIFE
dk_menu: "knife",
dk_dpth_s: "cut depth",
dk_dpth_l: ["final cut depth","in millimeters"],
dk_pass_s: "cut passes",
dk_pass_l: ["number of passes","down to cut depth"],
dk_offs_s: "tip offset",
dk_offs_l: ["distance from blade tip","to center of tool","in millimeters"],
// OUTPUT LASER
ou_spac_s: "spacing",
ou_spac_l: ["distance between layer output","in millimeters"],
ou_scal_s: "scaling",
ou_scal_l: "multiplier (0.1 to 100)",
ou_powr_s: "power",
ou_powr_l: ["0 - 100","represents %"],
ou_sped_s: "speed",
ou_sped_l: "millimeters / minute",
ou_mrgd_s: "merged",
ou_mrgd_l: ["merge all layers using","color coding to denote","stacking depth"],
ou_grpd_s: "grouped",
ou_grpd_l: ["retain each layer as","a unified grouping","instead of separated","polygons"],
ou_layr_s: "layer order",
ou_layr_l: ["output layer order","from top right to","bottom left"],
ou_layo_s: "layer color",
ou_layo_l: ["output layer colors","for each z index","overridden by merged"],
ou_drkn_s: "drag knife",
ou_drkn_l: ["enable drag knife","output in gcode","cut radii are added","to corners with","cut down passes"],
// OUTPUT FDM
ou_nozl_s: "Drucktemperatur",
ou_nozl_l: "In Grad Celsius",
ou_bedd_s: "Betttemperatur",
ou_bedd_l: "In Grad Celsius",
ou_feed_s: "Druckgewschwindigkeit",
ou_feed_l: ["Maximale Druckgeschwindigkeit","mm / min"],
ou_fini_s: "Außengeschwindigkeit",
ou_fini_l: ["Geschwindigkeit für Außenwände","mm / min"],
ou_move_s: "Bewegungsgeschwnidgkeit",
ou_move_l: ["Geschwndigkeit, wenn nicht gedruckt wird ","mm / min","0 = aktiviere G0 Befehle"],
ou_shml_s: "Wand-Faktor",
ou_flml_s: "Voll-Füll-Faktor",
ou_spml_s: "Füll-Material-Faktor",
ou_exml_l: ["Extrusions-Multiplikator","0.0 - 2.0"],
ou_fans_s: "Geschwindigkeit der Bauteilkühlung",
ou_fans_l: "0 - 255",
// OUTPUT CAM
ou_toll_s: "tolerance",
ou_toll_l: ["surface precision","in workspace units","lower is slower and","uses more memory","0 = automatic based","on animate preference"],
ou_zanc_s: "z anchor",
ou_zanc_l: ["controls the position of the part","when stock Z exceeds part Z"],
ou_ztof_s: "z offset",
ou_ztof_l: ["offset z anchor","in workspace units","has no effect when","anchor is middle"],
ou_zbot_s: "z bottom",
ou_zbot_l: ["offset from part bottom","to limit cutting depth","in workspace units"],
ou_zclr_s: "z clearance",
ou_zclr_l: ["safe travel offset","from top of part","in workspace units"],
ou_ztru_s: "z thru",
ou_ztru_l: ["extend cutout pass down","in workspace units"],
ou_conv_s: "conventional",
ou_conv_l: ["milling direction","uncheck for 'climb'"],
ou_depf_s: "depth first",
ou_depf_l: ["optimize pocket cuts","with depth priority"],
// CAM STOCK
cs_menu: "stock",
cs_wdth_s: "width",
cs_wdth_l: ["width (x) in workspace units","0 defaults to part size"],
cs_dpth_s: "depth",
cs_dpth_l: ["depth (y) in workspace units","0 defaults to part size"],
cs_hght_s: "height",
cs_hght_l: ["height (z) in workspace units","0 defaults to part size"],
cs_offs_s: "offset",
cs_offs_l: ["use width, depth, height","as offsets from max","part size on platform"],
cs_clip_s: "clip to",
cs_clip_l: ["rough and outline","clip cutting paths","to defined stock"],
cs_offe_s: "enable",
cs_offe_l: "enable milling stock",
// ORIGIN (CAM & LASER)
or_bnds_s: "origin bounds",
or_bnds_l: ["origin is relative to","boundary of all objects"],
or_cntr_s: "origin center",
or_cntr_l: "origin is referenced from the center",
or_topp_s: "origin top",
or_topp_l: "origin is references from the top of objects",
// FDM ADVANCED
ad_menu: "Fortgeschritten",
ad_rdst_s: "Einzugslänge",
ad_rdst_l: ["Wie weit das Filament eingezogen werden soll","bei langen Bewegungen (in mm)"],
ad_rrat_s: "Einzugsgeschwindigkeit",
ad_rrat_l: ["Geschwindigkeit des Filamenteinzugs","in mm/s"],
ad_rdwl_s: "Vorschub-Pause",
ad_wpln_s: "Einzugs-Bewegung",
ad_wpln_l: ["Bewegung ohne Druck","nach Einzug","in millimeters"],
ad_rdwl_l: ["Zeitabstand zwischen","Bewegung und Vorschub nach Einzug ","in msec"],
ad_scst_s: "Außenwand-Coasting",
ad_scst_l: ["Welche Strecke am Ende einer","Außenwand nicht extrudiert wird","in mm"],
ad_msol_s: "Mindestfläche für vollständige Füllung",
ad_msol_l: ["Mindestfläche in mm^2 ","für vollständige Füllung","muß > 0.1 sein"],
ad_mins_s: "Mindestgeschwindigkeit",
ad_mins_l: ["Mindestgeschwindigkeit","für kurze Segmente"],
ad_spol_s: "Kurze Segmente",
ad_spol_l: ["Segmente die kürzer sind","werden mit","Mindestgeschwindigkeit gedruckt","in mm"],
ad_arct_s: "arc tolerance",
ad_arct_l: ["convert faceted lines to arcs","center point drift tolerance","when matching arc points","consider values around 0.15","in millimeters","0 to disable"],
ad_zhop_s: "Z Hop",
ad_zhop_l: ["Hebe Düse bei Einzug an","in mm","0 zum deaaktivieren"],
ad_abkl_s: "anti-backlash",
ad_abkl_l: ["Für schöneres Aussehen Flacher Oberflächen","verwende Micro-Bewegungen","um Backlash auszugleichen","in mm","0 zum deaktivieren","Wenn Deine Firmware M425 unterstützt","verwende das im Start-GCode und setze hier auf 0"],
ad_lret_s: "Einzug bei Ebenenwechsel",
ad_lret_l: ["Erzwinge Einzug","bei Ebenen wechseln"],
ad_play_s: "Ebenen glätten",
ad_play_l: ["glätte bis zu","dieser Anzahl Ebenen"],
ad_pspd_s: "Glätt-Geschwindigkeit",
ad_pspd_l: ["Glätt-Geschwindigkeit","in mm / min"],
// CAM EXPERT
cx_fast_s: "skip shadow",
cx_fast_l: ["disable overhang detection","can be faster and use less","memory with complex models","but fails with overhangs","try enabling if slicing","hangs during shadowing"],
// FDM GCODE
ag_menu: "GCode",
ag_nozl_s: "Düse",
ag_nozl_l: "Wählt Düse oder Druckkopf aus",
ag_paws_s: "Pause bei Ebenen",
ag_paws_l: ["Liste von Ebenen (kommasepariert","vor denen pausiert werden soll"],
ag_loop_s: "Schichten wiederholen",
ag_loop_l: ["Schichten die wiederholt werden sollen, im Format ","erste-letzte-anzahl,erste-letzte-anzahl,...","omitted count = 1"],
// SLA MENU
sa_menu: "Slicen",
sa_lahe_s: "Schichthöhe",
sa_lahe_l: ["Schichthöhe","in mm"],
sa_shel_s: "Aushöhlen",
sa_shel_l: ["Wanddick mm","verwene Mehrfaches der Schichthöhe","setze 0 zum deaktivieren"],
sa_otop_s: "oben offen",
sa_otop_l: ["Wenn Aushöhlen aktiviert ist","bleibt das Objekt oben offen"],
sa_obas_s: "unten offen",
sa_obas_l: ["Wenn Aushöhlen aktiviert ist","bleibt das Objekt unten offen","deaktiviert, wenn Stützstrukturen verwendet werden"],
sa_layr_m: "layers",
sa_lton_s: "light on time",
sa_lton_l: ["layer light on","time in seconds"],
sa_ltof_s: "light off time",
sa_ltof_l: ["layer light off","time in seconds"],
sa_pldi_s: "peel distance",
sa_pldi_l: ["peel distance","in millimeters"],
sa_pllr_s: "peel lift rate",
sa_pllr_l: ["peel lift speed","in mm/sec"],
sa_pldr_s: "peel drop rate",
sa_pldr_l: ["peel drop speed","in mm/sec"],
sa_base_m: "base",
sa_balc_s: "layer count",
sa_balc_l: ["number of","base layers"],
sa_bltn_l: ["base layer light on","time in seconds"],
sa_bltf_l: ["base layer light off","time in seconds"],
sa_infl_m: "Füllstrukturen",
sa_ifdn_s: "Dichte",
sa_ifdn_l: ["percent infill","requires shell","0 = disabled","valid 0.0 - 1.0"],
sa_iflw_s: "line width",
sa_iflw_l: ["hatch line width","in millimeters"],
sa_supp_m: "support",
sa_slyr_s: "base layers",
sa_slyr_l: ["base support layers","value range 0-10"],
sa_slgp_s: "gap layers",
sa_slgp_l: ["number of layers between","raft and bottom of object"],
sa_sldn_s: "density",
sa_sldn_l: ["used to compute the","number of support pillars","0.0-1.0 (0 = disable)"],
sa_slsz_s: "size",
sa_slsz_l: ["max size of a","support pillar","in millimeters"],
sa_slpt_s: "points",
sa_slpt_l: ["number of points in","each support pillar","in millimeters"],
sl_slen_l: "enable supports",
sa_outp_m: "output",
sa_opzo_s: "z offset",
sa_opzo_l: ["z layer offset","almost always 0.0","0.0-1.0 in millimeters"],
sa_opaa_s: "anti alias",
sa_opaa_l: ["enable anti-aliasing","produces larger files","can blur details"]
};

177
web/kiri/lang/de-help.html Normal file
View file

@ -0,0 +1,177 @@
<div id="help" class="f-col a-stretch">
<div class="title f-row j-center">
<label>Kiri:Moto Slicer Help</label>
<label id="kiri-version"></label>
</div>
<div class="f-row">
<div id="help-sel" class="f-col">
<div class="help-label" tab="about">Über</div>
<div class="help-label" tab="start">starting</div>
<div class="help-label" tab="controls">Steuerung</div>
<div class="help-label" tab="support">Unterstützung</div>
<div class="help-label" tab="project">Das Projekt</div>
</div>
<div id="help-tabs" class="f-grow">
<div id="help-tab-about" class="help-tab f-col a-center">
<div class="content">
<p>
<b>Kiri:Moto</b> ist ein einzigartiger browser-basierter, komplett quelloffener slicer,
mit dem Du 3D-Modelle in Steuerbefehle für <u>3D-Drucker</u>, <u>CNC-Fräsen</u>,
uns <u>Laser Cutter</u> umwandeln kannst.
</p><br><p>
Browser-basiert bedeutet, dass <i>alle</i> Verarbeitung in Deinem Browser stattfindet,
und nicht in der Cloud. Deine Modelle und Daten bleiben <i>privat</i> auf Deinem Rechner.
</p><br><p>
Das heisst, das Kiri wie eine lokale Anwendung auf Deinem Computer läuft, aber zusätzlich
so sicher ist, wie Dein Browser.
</p><br><p>
Ein weiterer Vorteil von Kiri als Web-Anwendung ist, dass Du immer die aktuelle Version
verwendest. Neue Funktionen, Fehlerbehebungen, und andere Updates kommen regelmäßig und automatisch
bei Dir an. Wenn Du eine ältere, oder eine Entwicklungsversion von Kiri verwenden willst, kannst
Du diese im Nutzer-Menü oben rechts auswählen.
</p>
</div>
<div class="grow"></div>
<i>Kiri leitet sich von Kiri-e ab, der Japanischen Scherenschnitt-Kunst.</i>
<i>Moto ist ein Kürzel für "Modeling Tool" (Modellierungs-Werkzeug)</i>
</div>
<div id="help-tab-start" class="help-tab f-col"><div class="content">
<b class="header">Schnell-Anleitung</b>
<div class="content">
<p>
<li id="help-start">Schau Dir <a href="https://youtu.be/08795Sj22QE" target="youtube">The Definitive Video Guide to Kiri:Moto 2.5</a> an (auf Englisch)</li>
<li><button><i class="fas fa-cogs"></i></button> Im Einrichtungs-Menü wählst Du <button><i class="fas fa-cube"></i></button> Geräte und Arbeitsmodus</li>
<li><button><i class="fas fa-sliders-h"></i></button> preferences allow interface and mode-specific settings</li>
<li><button><i class="fas fa-plus"></i></button> Du kannst Geräte-Konfigurationen anpassen, wenn Du die Standard-Einstellungen verändern willst</li>
<li><button><i class="fas fa-folder"></i></button> Öffne Dateien <button><i class="fas fa-file-upload"></i></button> importiere Teile in den Arbeitsbereich</li>
<li>use the right-side profile menus to customize slicing parameters</li>
<li><button><i class="fas fa-bars"></i></button> Mit Slice siehst du, wie die Parameter sich auf Objekte auswirken</li>
<li><button><i class="fas fa-cubes"></i></button> Vorschau berechnet Pfade (Maschinen-Anweisungen) aus den gesliceten Objekten</li>
<li><button><i class="fas fa-file-download"></i></button> Export speichert diese Pfade in GCode- oder SVG-Dateien</li>
</p>
</div>
<div class="content">
<b class="header">Arbeits-Modi</b>
<p>Jeder Modus (FDM, CAM, Laser, SLA) hat unterschiedliche Funktionen und Einstellungsmöglichkeiten.
Schau Dir die Youtube-Videos an, um mehr zu erfahren.</p>
</div>
</div></div>
<div id="help-tab-print" class="help-tab f-col">Über 3d-Druck</div>
<div id="help-tab-mill" class="help-tab f-col">Über CNC-Fräsen</div>
<div id="help-tab-laser" class="help-tab f-col">Über Laser-Cutting</div>
<div id="help-tab-controls" class="help-tab f-col a-center"><div class="content">
<div class="f-row">
<table>
<tr><td colspan="2" class="header">Tastatur-Befehle</td><tr>
<tr><td></td></tr>
<tr><th>Taste</th><th>Befehl</th></tr>
<tr><td colspan=2 class="header2">Konfiguration</td></tr>
<tr><td><label>e</label></td><td>Geräte Auswahl</td></tr>
<tr><td><label>o</label></td><td>Auswahl des Fräswerkzeugs</td></tr>
<tr><td><label>c</label></td><td>Grid-Geräte im Netzwerk</td></tr>
<tr><td><label>q</label></td><td>Voreinstellungen</td></tr>
<tr><td><label>l</label></td><td>Arbeits-Profile</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Modus</td></tr>
<tr><td><label>a</label></td><td>Platzieren</td></tr>
<tr><td><label>s</label></td><td>Slice</td></tr>
<tr><td><label>p</label></td><td>Vorschau</td></tr>
<tr><td><label>g</label></td><td>Animieren</td></tr>
<tr><td><label>x</label></td><td>Export</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Datei</td></tr>
<tr><td><label>i</label></td><td>Importieren</td></tr>
<tr><td><label>r</label></td><td>Letzte Dateien</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Ansicht</td></tr>
<tr><td><label>h</label></td><td>Standard</td></tr>
<tr><td><label>t</label></td><td>Oben</td></tr>
<tr><td><label>f</label></td><td>Vorn</td></tr>
<tr><td><label>b</label></td><td>Hinten</td></tr>
<tr><td><label>z</label></td><td>Ansicht zurüksetzen</td></tr>
<tr><td><label>&lt; &gt;</label></td><td>Durch die Seitenansichten wechseln</td></tr>
<tr><td><label>v</label></td><td>Fokus (Platzieren)</td></tr>
<tr><td><label>v</label></td><td>Ebene (Slice/Vorschau)</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Objekte</td></tr>
<tr><td><label>d</label></td><td>Duplizieren</td></tr>
<tr><td><label>m</label></td><td>Spiegeln</td></tr>
<tr><td><label>o</label></td><td>Rotieren (Dialog)</td></tr>
<tr><td><label>delete</label></td><td>Löschen</td></tr>
<tr><td><label>arrow</label></td><td>Um 90&deg; rotieren</td></tr>
<tr><td><label>shift-arrow</label></td><td>Um 5&deg; rotieren</td></tr>
<tr><td><label>option-arrow</label></td><td>5mm bewegen</td></tr>
<tr><td><label>ctrl-a</label></td><td>Alle Objekte auswählen</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Hilfe</td></tr>
<tr><td><label>?</label></td><td>Dieses Fenster</td></tr>
<tr><td><br><br></td></tr>
<tr><td colspan="2" class="header">Maus-Kontrollen</td><tr>
<tr><td></td></tr>
<tr><th>Tastenkombination</th><th>Befehl</th></tr>
<tr><td colspan=2 class="header2">Ansicht</td></tr>
<tr><td><label>shift</label> wheel</td><td>Schicht wechseln</td></tr>
<tr><td><label>meta</label> verschieben</td><td>Ansicht schwenken</td></tr>
<tr><td><label>right-drag</label></td><td>Ansicht schwenken</td></tr>
<tr><td><label>left-drag</label></td><td>Ansicht rotieren</td></tr>
<tr><td><label>mouse-wheel</label></td><td>Ausschnitt verändern</td></tr>
<tr><td><label>middle-drag</label></td><td>Ausschnitt verändern</td></tr>
<tr><td><label>meta</label> drag</td><td>Ansicht schwenkden</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Objekte</td></tr>
<tr><td><label>ctrl-click</label></td><td>Seite aufs Bett ausrichten</td></tr>
<tr><td><label>meta-click</label></td><td>Seite aufs Bett ausrichten</td></tr>
<tr><td></td></tr>
</table>
</div>
</div></div>
<div id="help-tab-support" class="help-tab f-col"><div class="content">
<b class="header">Weitere Informationen</b>
<div class="content nounderline">
<a target="_rsc" href="https://docs.grid.space/projects/kiri-moto"><i class="fas fa-question-circle"></i>Weitereführende Dokumentation</a>
<a target="_rsc" href="https://forum.grid.space"><i class="fab fa-discourse"></i> Grid.Space forums</a>
<a target="_rsc" href="https://discord.gg/suyCCgr"><i class="fab fa-discord"></i> Discord live chat server</a>
<a target="_rsc" href="https://www.youtube.com/playlist?list=PLRoVgyRoWZpumQIKWm7oldx3bjDivqP24"><i class="fab fa-youtube"></i> Youtube videos und Tutorials</a>
<a target="_rsc" href="https://twitter.com/grid_space_3d"><i class="fab fa-twitter"></i>Grid.Space auf Twitter</a>
<a target="_rsc" href="https://facebook.com/groups/kirimoto"><i class="fab fa-facebook"></i> Kiri:Moto Gruppe aufn Facebook</a>
</div>
<b class="header">Weitere Optionen</b>
<p>
Im button><i class="fas fa-user-cog"></i></button> NutzerMenü open rechts, kannst Du die Funktion
<span class="mono">Export</span> verwenden, um eine Kopie Deiner Geräte Profile und (optional) Deines Arbeitsbereichs
herunterzuladen. Mit dieser Datei kannst Du jederzeit Deine komplette Arbeitsumgebung bei Kiri:Moto wieder herstellen.
Du kannst sie auch mit anderen Teilen, zum Beispiel um die Entwickler oder andere Nutzer bei der Fehlersuche zu unterstützen.
</p>
</div></div>
<div id="help-tab-project" class="help-tab f-col"><div class="content">
<b class="header">Open Source</b>
<p>
Kiri:Moto ist komplett <a href="https://github.com/GridSpace/grid-apps" target="_github">quelloffen</a>.
Mehr kannst Du auf der Github-Seite zum Projekt erfahten. Es gibt verwandte Projekte, sowohl Software als auch
Hardware. Neue Entwickler und Code-Erweiterungen werden gerne entgegen genommen. Der Quellcode ist unter der
MIT-Lizenz veröffentlicht.
</p><br><p>
<a target="_paypal" href="https://paypal.me/gridspace3d?locale.x=en_US">Spenden</a> sind natürlich auch willkommen.
</p>
<br><b class="header">Integrationen</b>
<p>Kiri:Moto ist auch in anderen Applikationen integriert.</p>
<li><a target="app" href="https://appstore.onshape.com/apps/CAM/EAAEWYIOMQKBENEMYW2N7MF253CT4WYL6SUJGEY=/description"><b>Onshape</b></a> im App store</li>
<li><a target="app" href="http://www.thingiverse.com/apps/kirimoto/"><b>Thingiverse</b></a> als Thing Apps</li>
<li><a target="app" href="https://simplyprint.io">SimplyPrint.io</a> verwendet Kiri:Moto als primären Slicer</li>
<li><a target="app" href="https://sienci.com/camlab-help/">CAMLab</a> ist eine Variante einer früheren Version von Kiri:Moto</li>
</div></div>
</div>
</div>
</div>

1
web/kiri/lang/de.js Symbolic link
View file

@ -0,0 +1 @@
de-de.js

View file

@ -11,13 +11,6 @@
<div class="help-label" tab="about">about</div>
<div class="help-label" tab="start">starting</div>
<div class="help-label" tab="controls">controls</div>
<!--
<hr width=75%>
<div class="help-label" tab="print">3d print</div>
<div class="help-label" tab="mill">cnc mill</div>
<div class="help-label" tab="laser">laser cut</div>
<hr width=75%>
-->
<div class="help-label" tab="support">support</div>
<div class="help-label" tab="project">project</div>
</div>
@ -83,6 +76,7 @@
<tr><td><label>o</label></td><td>CAM Tool Selection</td></tr>
<tr><td><label>c</label></td><td>Grid Local Devices</td></tr>
<tr><td><label>q</label></td><td>Preference panel</td></tr>
<tr><td><label>l</label></td><td>Work Profiles</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Mode</td></tr>
<tr><td><label>a</label></td><td>Arrange</td></tr>
@ -96,11 +90,14 @@
<tr><td><label>r</label></td><td>Recent</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">View</td></tr>
<tr><td><label>t</label></td><td>Top</td></tr>
<tr><td><label>h</label></td><td>Home</td></tr>
<tr><td><label>v</label></td><td>Layer</td></tr>
<tr><td><label>t</label></td><td>Top</td></tr>
<tr><td><label>f</label></td><td>Front</td></tr>
<tr><td><label>b</label></td><td>Back</td></tr>
<tr><td><label>z</label></td><td>Reset View</td></tr>
<tr><td><label>meta</label> drag</td><td>Pan</td></tr>
<tr><td><label>&lt; &gt;</label></td><td>Cycle Side Views</td></tr>
<tr><td><label>v</label></td><td>Focus (arrange)</td></tr>
<tr><td><label>v</label></td><td>Layer (slice/preview)</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Objects</td></tr>
<tr><td><label>d</label></td><td>Duplicate</td></tr>
@ -114,10 +111,7 @@
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Help</td></tr>
<tr><td><label>?</label></td><td>This Window</td></tr>
<tr><td></td></tr>
</table>
<div class="t-pad"></div>
<table>
<tr><td><br><br></td></tr>
<tr><td colspan="2" class="header">Mouse Controls</td><tr>
<tr><td></td></tr>
<tr><th>Combo</th><th>Action</th></tr>
@ -128,6 +122,7 @@
<tr><td><label>left-drag</label></td><td>Rotate</td></tr>
<tr><td><label>mouse-wheel</label></td><td>Zoom</td></tr>
<tr><td><label>middle-drag</label></td><td>Zoom</td></tr>
<tr><td><label>meta</label> drag</td><td>Pan</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Objects</td></tr>
<tr><td><label>ctrl-click</label></td><td>Align face to bed</td></tr>
@ -140,12 +135,12 @@
<div id="help-tab-support" class="help-tab f-col"><div class="content">
<b class="header">Resources</b>
<div class="content nounderline">
<a target="_rsc" href="https://docs.grid.space/projects/kiri-moto"><i class="fas fa-question-circle"></i> Extended Documentation</a>
<a target="_rsc" href="https://forum.grid.space"><i class="fab fa-discourse"></i> Grid.Space forums</a>
<a target="_rsc" href="https://discord.gg/suyCCgr"><i class="fab fa-discord"></i> Discord live chat server</a>
<a target="_rsc" href="https://www.youtube.com/playlist?list=PLRoVgyRoWZpumQIKWm7oldx3bjDivqP24"><i class="fab fa-youtube"></i> Youtube videos and tutorials</a>
<a target="_rsc" href="https://twitter.com/grid_space_3d"><i class="fab fa-twitter"></i> Follow Grid.Space on Twitter</a>
<a target="_rsc" href="https://facebook.com/groups/kirimoto"><i class="fab fa-facebook"></i> Kiri:Moto Users group on Facebook</a>
<a target="_rsc" href="https://wiki.grid.space/"><i class="fab fa-github"></i> Grid.Space Wiki</a>
</div>
<b class="header">In-App Options</b>
<p>

View file

@ -2,11 +2,76 @@
// map for any missing key/value pairs
kiri.lang['en'] =
kiri.lang['en-us'] = {
version: "version",
// common keys and menus
animate: "animate", // CAM animate button
arrange: "arrange", // layout workspace objects
axis: "axis", // left object scale pop menu
clear: "clear", // clear workspace (remove all objects)
copy: "copy",
delete: "delete",
detail: "detail",
done: "done",
enable: "enable",
export: "export",
files: "files",
help: "help",
ghost: "ghost", // left render pop menu (wireframe)
hide: "hide", // left render pop menu (invisible)
home: "home",
import: "import",
language: "language",
machine: "machine", // device or machine
metric: "metric",
name: "name",
prefs: "prefs", // left menu "preferences"
preview: "preview",
recent: "recent",
render: "render", // left render pop menu
reset: "reset",
rotate: "rotate", // left rotate pop menu
save: "save",
scale: "scale", // left object scale pop menu
setup: "setup",
settings: "settings",
size: "size",
slice: "slice",
solid: "solid", // view type pop menu
start: "start",
tool: "tool",
tools: "tools", // CAM tool menu button
top: "top",
type: "type", // CAM tool type
version: "version",
view: "view", // left view pop menu
wire: "wire", // left render pop menu
acct_xpo: "make a backup of your device\nand device profiles with the\noption to include workspace\nobjects and positions",
acct_xpo: ["make a backup of your device","and device profiles with the","option to include workspace","objects and positions"],
// RIGHT-CLICK CONTEXT MENU
rc_clws: "clear workspace",
rc_xpws: "export workspace",
rc_lafl: "lay flat",
rc_mirr: "mirror",
rc_dupl: "duplicate",
rc_xstl: "export as STL",
// DEVICE MENU and related dialogs
dm_sldt: "select a device type",
dm_stdd: "standard devices",
dm_mydd: "my devices",
dm_seld: "selected device",
dm_rcnt: "recent files",
dm_savs: "saved settings",
dm_appp: "Application Preferences",
// CAM Tool Dialog
td_tyem: "end", // end mill
td_tybm: "ball", // ball mill
td_tytm: "taper", // taper mill
td_tonm: "tool #",
td_shft: "shaft", // endmill shaft specs
td_flut: "flute", // endmill flute specs
td_tapr: "taper", // endmill taper specs
// DEVICE dialog groups
dv_gr_dev: "device",
@ -26,9 +91,9 @@ kiri.lang['en-us'] = {
dv_bedd_s: "depth",
dv_bedd_l: "workspace units",
dv_bedh_s: "height",
dv_bedh_l: "max build height\nin workspace units",
dv_bedh_l: ["max build height","in workspace units"],
dv_spmx_s: "max spindle",
dv_spmx_l: "max spindle rpm speed\n0 to disable",
dv_spmx_l: ["max spindle rpm speed","0 to disable"],
dv_xtab_s: "absolute positioning",
dv_xtab_l: "extrusion moves absolute",
dv_orgc_s: "origin center",
@ -39,16 +104,18 @@ kiri.lang['en-us'] = {
dv_bedc_l: "device bed is circular",
dv_belt_s: "belt bed",
dv_belt_l: "continuous printing bed",
dv_retr_s: "firmware retract",
dv_retr_l: ["device fimware supports G10/G11"],
dv_fanp_s: "fan power",
dv_fanp_l: "set cooling fan power",
dv_prog_s: "progress",
dv_prog_l: "output on each % progress",
dv_layr_s: "layer",
dv_layr_l: "output at each\nlayer change",
dv_layr_l: ["output at each","layer change"],
dv_tksp_s: "token spacer",
dv_tksp_l: "add a space between\ngcode axis parameters\nG0X0Y0X0\nvs\nG0 X0 Y0 Z0",
dv_tksp_l: ["add a space between","gcode axis parameters","G0X0Y0X0","vs","G0 X0 Y0 Z0"],
dv_strc_s: "strip comments",
dv_strc_l: "strip gcode comments\ncomments begin with ;",
dv_strc_l: ["strip gcode comments","comments begin with ;"],
dv_fext_s: "file extension",
dv_fext_l: "file name extension",
dv_dwll_s: "dwell",
@ -68,7 +135,9 @@ kiri.lang['en-us'] = {
dv_lzof_s: "laser off",
dv_lzof_l: "gcode laser off script",
dv_exts_s: "select",
dv_exts_l: "gcode to select this extruder",
dv_exts_l: "gcode run to enable this extruder",
dv_dext_s: "deselect",
dv_dext_l: "gcode run before enabling another extruder",
dv_extd_s: "deselect",
dv_extd_l: "gcode to deselect this extruder",
dv_exox_s: "offset x",
@ -124,40 +193,48 @@ kiri.lang['en-us'] = {
op_xprt_l: "show more settings options",
op_decl_s: "decals",
op_decl_l: "show device decals and logos",
op_dang_s: "experimental",
op_dang_l: "show experimental parameters",
op_hopo_s: "hover pop",
op_hopo_l: "enable menu hover\nto activate",
op_hopo_l: ["enable menu hover","to activate"],
op_dark_s: "dark mode",
op_dark_l: "dark mode interface",
op_comp_s: "compact ui",
op_comp_l: "compact user interface\nbetter for small screens\nand tablets",
op_comp_l: ["compact user interface","better for small screens","and tablets"],
op_shor_s: "show origin",
op_shor_l: "show device or process origin",
op_shru_s: "show rulers",
op_shru_l: "show axes rulers\non major gridlines",
op_shru_l: ["show axes rulers","on major gridlines"],
op_sped_s: "show speeds",
op_sped_l: "show speed to color bar\nin preview mode",
op_sped_l: ["show speed to color bar","in preview mode"],
op_auto_s: "auto layout",
op_auto_l: "automatically layout platform\nwhen new items added",
op_auto_l: ["automatically layout platform","when new items added"],
op_free_s: "free layout",
op_free_l: "permit dragable layout\nno effect in laser mode",
op_free_l: ["permit dragable layout","no effect in laser mode"],
op_spcr_s: "spacing",
op_spcr_l: ["spacing between objects","during auto layout","in workspace units"],
op_orth_s: "orthographic",
op_orth_l: ["orthographic display","requires page refresh"],
op_invr_s: "invert zoom",
op_invr_l: "invert mouse wheel\nscroll zoom",
op_invr_l: ["invert mouse wheel","scroll zoom"],
op_save_s: "auto save",
op_save_l: "preserve objects in workspace\nbetween application reloads",
op_save_l: ["preserve objects in workspace","between application reloads"],
op_line_s: "line type",
op_line_l: "line style for path rendering\nimpacts 3d performance\npath: 3d best\nflat: 2d good\nline = 1d fast",
op_line_l: ["line style for path rendering","impacts 3d performance","path: 3d best","flat: 2d good","line = 1d fast"],
op_unit_s: "units",
op_unit_l: "workspace units affects\nspeeds and distances",
op_unit_l: ["workspace units affects","speeds and distances"],
op_anim_s: "animate",
op_anim_l: "animation mesh density\nhigher is more dense\ntakes more memory\nand is slower",
op_anim_l: ["animation mesh density","higher is more dense","takes more memory","and is slower"],
lo_menu: "layout",
pt_menu: "parts",
pt_deci_s: "decimate",
pt_deci_l: "enable or disable point decimation\nduring port import. for faster slicing\nand lower memory usage",
pt_deci_l: ["enable or disable point decimation","during port import. for faster slicing","and lower memory usage"],
pt_qual_s: "quality",
pt_qual_l: "level of detail to retain\nduring slicing operations\nlower is faster",
pt_qual_l: ["level of detail to retain","during slicing operations","lower is faster"],
pt_heal_s: "heal mesh",
pt_heal_l: ["attempt to heal","non-manifold meshes","adds to slicing time"],
xp_menu: "exports",
@ -169,156 +246,164 @@ kiri.lang['en-us'] = {
// FDM SLICING
sl_menu: "layers",
sl_lahi_s: "height",
sl_lahi_l: "height of each slice\nlayer in millimeters",
sl_lahi_l: ["height of each slice","layer in millimeters"],
ad_minl_s: "height min",
ad_minl_l: "adaptive min layer height\nin millimeters\nmust be non-zero",
ad_minl_l: ["adaptive min layer height","in millimeters","must be non-zero"],
sl_ltop_s: "top layers",
sl_ltop_l: "number of solid layers\nto enforce at the\ntop of the print",
sl_ltop_l: ["number of solid layers","to enforce at the","top of the print"],
sl_lsld_s: "solid layers",
sl_lsld_l: "solid fill areas computed\nfrom layer deltas. see\nlayer pop menu",
sl_lsld_l: ["solid fill areas computed","from layer deltas. see","layer pop menu"],
sl_lbot_s: "base layers",
sl_lbot_l: "number of solid layers\nto enforce at the\nbottom of the print",
sl_lbot_l: ["number of solid layers","to enforce at the","bottom of the print"],
ad_adap_s: "adaptive",
ad_adap_l: "use adaptive layer heights\nwith 'layer height' as max\nand 'layer min' as the min",
ad_adap_l: ["use adaptive layer heights","with 'layer height' as max","and 'layer min' as the min"],
// FDM SHELLS
sl_shel_s: "shell count",
sl_shel_l: "number of perimeter\nwalls to generate",
sl_shel_l: ["number of perimeter","walls to generate"],
sl_ordr_s: "shell order",
sl_ordr_l: "output shell order\ninside to outside\nor outside to inside\naffects surface quality",
sl_ordr_l: ["output shell order","inside to outside","or outside to inside","affects surface quality"],
sl_strt_s: "layer start",
sl_strt_l: "layer starting point\nlast = last layer end\ncenter = part center\norigin = device origin",
sl_strt_l: ["layer starting point","last = last layer end","center = part center","origin = device origin"],
ad_thin_s: "thin walls",
ad_thin_l: "detect and fill gaps\nbetween shell walls",
ad_thin_l: ["detect and fill gaps","between shell walls"],
// FDM FILL
fi_menu: "infill",
fi_type: "type",
fi_pcnt_s: "fraction",
fi_pcnt_l: "fill density values\n0.0 - 1.0",
fi_angl_s: "start angle",
fi_angl_l: "base angle in degrees",
fi_over_s: "overlap",
fi_over_l: "overlap with shell and fill\nas % of nozzle width\nhigher bonds better\n0.0 - 1.0",
fi_type: "fill type",
fi_pcnt_s: "fill fraction",
fi_pcnt_l: ["fill density values","0.0 - 1.0"],
fi_angl_s: "solid start",
fi_angl_l: ["starting angle in degrees","90 degrees added to ","each following layer","applies only to solid layers"],
fi_wdth_s: "solid width",
fi_wdth_l: ["line width for solid fill","as a fraction of nozzle width","values < 1 are more dense","good for surface finishes","0.0 - 1.0"],
fi_over_s: "shell overlap",
fi_over_l: ["overlap with shell and other fill","as fraction of nozzle diameter","0.0 - 2.0"],
// fi_rate_s: "print speed",
fi_rate_l: ["extrusion speed for infill","set to 0 to use default","output print speeds"],
// FDM FIRST LAYER
fl_menu: "base",
fl_lahi_s: "layer height",
fl_lahi_l: "height of each slice\nin millimeters\nshould be >= slice height",
fl_lahi_l: ["height of each slice","in millimeters","should be >= slice height"],
fl_rate_s: "shell speed",
fl_rate_l: "shell printing max speed\nin millimeters / minute",
fl_rate_l: ["shell printing max speed","in millimeters / minute"],
fl_frat_s: "fill speed",
fl_frat_l: "fill printing max speed\nin millimeters / minute",
fl_frat_l: ["fill printing max speed","in millimeters / minute"],
fl_mult_s: "flow factor",
fl_mult_l: "extrusion multiplier\n0.0 - 2.0",
fl_mult_l: ["extrusion multiplier","0.0 - 2.0"],
fl_sfac_s: "width factor",
fl_sfac_l: "multiplier on nozzle size\nchanges line spacing",
fl_sfac_l: ["multiplier on nozzle size","changes line spacing"],
fl_skrt_s: "skirt count",
fl_skrt_l: "number of first-layer offset\nbrims to generate",
fl_skrt_l: ["number of first-layer offset","brims to generate"],
fl_skro_s: "skirt offset",
fl_skro_l: "skirt offset from part\nin millimeters",
fl_skro_l: ["skirt offset from part","in millimeters"],
fl_nozl_s: "nozzle temp",
fl_nozl_l: "degrees in celsius\noutput setting used\nwhen this is zero",
fl_nozl_l: ["degrees in celsius","output setting used","when this is zero"],
fl_bedd_s: "bed temp",
fl_bedd_l: "degrees in celsius\noutput setting used\nwhen this is zero",
fl_bedd_l: ["degrees in celsius","output setting used","when this is zero"],
fr_spac_s: "raft gap",
fr_spac_l: "additional layer spacing\nbetween 1st layer and raft\nin millimeters",
fr_spac_l: ["additional layer spacing","between 1st layer and raft","in millimeters"],
fr_nabl_s: "raft enable",
fr_nabl_l: "create a raft under the\nmodel for better adhesion\nuses skirt offset and\ndisables skirt output",
fr_nabl_l: ["create a raft under the","model for better adhesion","uses skirt offset and","disables skirt output"],
// FDM BELT ONLY
fl_zoff_s: "layer offset",
fl_zoff_l: "height offset from belt\nof lowest extrusion\nin millimeters",
fl_zoff_s: "belt offset",
fl_zoff_l: ["height offset from belt","of lowest extrusion","in millimeters"],
fl_brim_s: "brim size",
fl_brim_l: "add brim to part bottom\nsize is width in millimeters\n0 to disable",
fl_brim_l: ["add brim to part bottom","size is width in millimeters","0 to disable"],
fl_brmn_s: "brim trigger",
fl_brmn_l: "add brims only when segment\nfacing belt is shorter than this\nvalue in millimeters\n0 = Infinity",
fl_brmn_l: ["add brims only when segment","facing belt is shorter than this","value in millimeters","0 = Infinity"],
fl_bled_s: "part anchor",
fl_bled_l: ["belt part anchor","at start of print","in millimeters"],
// FDM SUPPORT
sp_menu: "support",
sp_detect: "detect",
sp_dens_s: "density",
sp_dens_l: "fraction 0.0 - 1.0\nrecommended 0.15\n0 to disable",
sp_dens_l: ["fraction 0.0 - 1.0","recommended 0.15","0 to disable"],
sp_size_s: "pillar size",
sp_size_l: "pillar width\nin millimeters",
sp_size_l: ["pillar width","in millimeters"],
sp_offs_s: "part offset",
sp_offs_l: "offset from part\nin millimeters",
sp_offs_l: ["offset from part","in millimeters"],
sp_gaps_s: "gap layers",
sp_gaps_l: "number of layers\noffset from part",
sp_gaps_l: ["number of layers","offset from part"],
sp_span_s: "max span",
sp_span_l: "unsupported span that causes\na new support to be generated\nin millimeters",
sp_span_l: ["unsupported span that causes","a new support to be generated","in millimeters"],
sp_angl_s: "max angle",
sp_angl_l: "max overhang angle before\nsupporting pillar is generated",
sp_angl_l: ["max overhang angle before","supporting pillar is generated"],
sp_area_s: "min area",
sp_area_l: "minimum area for\na support column\nin millimeters",
sp_area_l: ["minimum area for","a support column","in millimeters"],
sp_xpnd_s: "expand",
sp_xpnd_l: "expand support area\nbeyond part boundary\nin millimeters",
sp_xpnd_l: ["expand support area","beyond part boundary","in millimeters"],
sp_nozl_s: "extruder",
sp_nozl_l: "in multi-extruder systems\nthe extruder to use for\nsupport material",
sp_nozl_l: ["in multi-extruder systems","the extruder to use for","support material"],
sp_auto_s: "automatic",
sp_auto_l: "enable generated supports\nusing at-slice-time geometry\nsupports will only appear\nafter slicing completes",
sp_auto_l: ["enable generated supports","using at-slice-time geometry","supports will only appear","after slicing completes"],
// LASER SLICING
ls_offs_s: "offset",
ls_offs_l: "adjust for beam width\nin millimeters",
ls_offs_l: ["adjust for beam width","in millimeters"],
ls_lahi_s: "height",
ls_lahi_l: "layer height\nin millimeters\n0 = auto/detect",
ls_lahi_l: ["layer height","in millimeters","0 = auto/detect"],
ls_lahm_s: "min",
ls_lahm_l: "layer height minimum\nwill merge auto slices\nunder this thickness\nin millimeters",
ls_lahm_l: ["layer height minimum","will merge auto slices","under this thickness","in millimeters"],
ls_sngl_s: "single",
ls_sngl_l: "perform only one slice\nat specified layer height",
ls_sngl_l: ["perform only one slice","at specified layer height"],
// CNC COMMON terms
cc_tool: "tool",
cc_offs_s: "offset",
cc_offs_l: "offset tool center\nfrom chosen path",
cc_offs_l: ["offset tool center","from chosen path"],
cc_spnd_s: "spindle rpm",
cc_spnd_l: "spindle speed in\nrevolutions / minute",
cc_spnd_l: ["spindle speed in","revolutions / minute"],
cc_sovr_s: "step over",
cc_sovr_l: "as a fraction of\ntool diameter",
cc_sovr_l: ["as a fraction of","tool diameter"],
cc_sdwn_s: "step down",
cc_sdwn_l: "step down depth\nfor each pass\nin workspace units\n0 to disable",
cc_sdwn_l: ["step down depth","for each pass","in workspace units","0 to disable"],
cc_feed_s: "feed rate",
cc_feed_l: "max cutting speed in\nworkspace units / minute",
cc_feed_l: ["max cutting speed in","workspace units / minute"],
cc_plng_s: "plunge rate",
cc_plng_l: "max z axis speed in\nworkspace units / minute",
cc_plng_l: ["max z axis speed in","workspace units / minute"],
cc_sngl_s: "select lines only",
cc_sngl_l: ["select only single edges","instead of connected polylines"],
// CNC COMMON
cc_menu: "limits",
cc_flip: "flip",
cc_rapd_s: "xy feed",
cc_rapd_l: "max xy moves feedrate\nin workspace units / minute",
cc_rapd_l: ["max xy moves feedrate","in workspace units / minute"],
cc_rzpd_s: "z feed",
cc_rzpd_l: "max z moves feedrate\nin workspace units / minute",
cc_rzpd_l: ["max z moves feedrate","in workspace units / minute"],
cc_loff_s: "offset",
cc_loff_l: "distance from stock face\nfor leveling pass\nin workspace units",
cc_loff_l: ["distance from stock face","for leveling pass","in workspace units"],
// CNC ROUGHING
cr_menu: "rough",
cr_lsto_s: "leave stock",
cr_lsto_l: "horizontal offset from vertical faces\nstock to leave for finishing pass\nin workspace units",
cr_lsto_l: ["horizontal offset from vertical faces","stock to leave for finishing pass","in workspace units"],
cr_ease_s: "ease down",
cr_ease_l: "plunge cuts will\nspiral down or ease\nalong a linear path",
cr_ease_l: ["plunge cuts will","spiral down or ease","along a linear path"],
cr_clrt_s: "clear top",
cr_clrt_l: "run a clearing pass over\nthe bounding area of the part\nat z = 0",
cr_clrt_l: ["run a clearing pass over","the bounding area of the part","at z = 0"],
cr_clrp_s: "clear voids",
cr_clrp_l: "mill out through pockets\ninstead of just the outline",
cr_clrp_l: ["mill out through pockets","instead of just the outline"],
cr_clrf_s: "clear faces",
cr_clrf_l: "interpolate step down to\nclear any detected flat areas",
cr_clrf_l: ["interpolate step down to","clear any detected flat areas"],
cr_olin_s: "inside only",
cr_olin_l: "limit cutting to\ninside part boundaries",
cr_olin_l: ["limit cutting to","inside part boundaries"],
// CNC OUTLINE
co_menu: "outline",
co_dogb_s: "dogbones",
co_dogb_l: "insert dogbone cuts\ninto inside corners",
co_dogb_l: ["insert dogbone cuts","into inside corners"],
co_wide_s: "wide cutout",
co_wide_l: "widen outside cutout paths\nfor deep cuts in hard material",
co_wide_l: ["widen outside cutout paths","for deep cuts in hard material"],
co_olin_s: "inside only",
co_olin_l: "limit cutting to\ninside part boundaries",
co_olin_l: ["limit cutting to","inside part boundaries"],
co_olot_s: "outside only",
co_olot_l: "limit cutting to\nexterior part boundaries\nwhich can be thought of\nas the shadow outline",
co_olot_l: ["limit cutting to","exterior part boundaries","which can be thought of","as the shadow outline"],
co_omit_s: "omit through",
co_omit_l: "eliminate thru holes",
co_olen_s: "enable",
@ -327,11 +412,11 @@ kiri.lang['en-us'] = {
// CNC CONTOUR
cn_menu: "contour",
cf_angl_s: "max angle",
cf_angl_l: "angles greater than this\nare considered vertical",
cf_angl_l: ["angles greater than this","are considered vertical"],
cf_curv_s: "curves only",
cf_curv_l: "limit linear cleanup\nto curved surfaces",
cf_curv_l: ["limit linear cleanup","to curved surfaces"],
cf_olin_s: "inside only",
cf_olin_l: "limit cutting to\ninside part boundaries",
cf_olin_l: ["limit cutting to","inside part boundaries"],
cf_linx_s: "enable y pass",
cf_linx_l: "linear y-axis finishing",
cf_liny_s: "enable x pass",
@ -340,37 +425,37 @@ kiri.lang['en-us'] = {
// CNC TRACE
cu_menu: "trace",
cu_type_s: "type",
cu_type_l: "follow = tool tip follows line\nright or left = tool tip\nfollows line offset by tool radius",
cu_type_l: ["follow = tool tip follows line","right or left = tool tip","follows line offset by tool radius"],
// CNC DRILLING
cd_menu: "drill",
cd_axis: "axis",
cd_points: "points",
cd_plpr_s: "plunge per",
cd_plpr_l: "max plunge between\ndwell periods\nin workspace units\n0 to disable",
cd_plpr_l: ["max plunge between","dwell periods","in workspace units","0 to disable"],
cd_dwll_s: "dwell time",
cd_dwll_l: "dwell time\nbetween plunges in\nin milliseconds",
cd_dwll_l: ["dwell time","between plunges in","in milliseconds"],
cd_lift_s: "drill lift",
cd_lift_l: "lift between plunges\nafter dwell period\nin workspace units\n0 to disable",
cd_lift_l: ["lift between plunges","after dwell period","in workspace units","0 to disable"],
cd_regi_s: "register",
cd_regi_l: "drill registration holes\nfor double-sided parts\nindependent of enable\ndrilling but uses same\ntool and settings",
cd_regi_l: ["drill registration holes","for double-sided parts","independent of enable","drilling but uses same","tool and settings"],
// CNC CUTOUT TABS
ct_menu: "tabs",
ct_angl_s: "angle",
ct_angl_l: "starting angle for tab spacing\nin degrees (0-360)",
ct_angl_l: ["starting angle for tab spacing","in degrees (0-360)"],
ct_numb_s: "count",
ct_numb_l: "number of tabs to use\nwill be spaced evenly\naround the part",
ct_numb_l: ["number of tabs to use","will be spaced evenly","around the part"],
ct_wdth_s: "width",
ct_wdth_l: "width in workspace units",
ct_hght_s: "height",
ct_hght_l: "height in workspace units",
ct_dpth_s: "depth",
ct_dpth_l: "distance in workspace units\nthat the tab projects from\nthe part surface",
ct_dpth_l: ["distance in workspace units","that the tab projects from","the part surface"],
ct_midl_s: "midline",
ct_midl_l: "use midline of tab\ninstead of z bottom\nfor double-sided work",
ct_midl_l: ["use midline of tab","instead of z bottom","for double-sided work"],
ct_nabl_s: "auto",
ct_nabl_l: "auto generate radial tabs\nprojected from part center\nusing count and angle offset",
ct_nabl_l: ["auto generate radial tabs","projected from part center","using count and angle offset"],
// OUTPUT
ou_menu: "output",
@ -378,31 +463,31 @@ kiri.lang['en-us'] = {
// LASER KNIFE
dk_menu: "knife",
dk_dpth_s: "cut depth",
dk_dpth_l: "final cut depth\nin millimeters",
dk_dpth_l: ["final cut depth","in millimeters"],
dk_pass_s: "cut passes",
dk_pass_l: "number of passes\ndown to cut depth",
dk_pass_l: ["number of passes","down to cut depth"],
dk_offs_s: "tip offset",
dk_offs_l: "distance from blade tip\nto center of tool\nin millimeters",
dk_offs_l: ["distance from blade tip","to center of tool","in millimeters"],
// OUTPUT LASER
ou_spac_s: "spacing",
ou_spac_l: "distance between layer output\nin millimeters",
ou_spac_l: ["distance between layer output","in millimeters"],
ou_scal_s: "scaling",
ou_scal_l: "multiplier (0.1 to 100)",
ou_powr_s: "power",
ou_powr_l: "0 - 100\nrepresents %",
ou_powr_l: ["0 - 100","represents %"],
ou_sped_s: "speed",
ou_sped_l: "millimeters / minute",
ou_mrgd_s: "merged",
ou_mrgd_l: "merge all layers using\ncolor coding to denote\nstacking depth",
ou_mrgd_l: ["merge all layers using","color coding to denote","stacking depth"],
ou_grpd_s: "grouped",
ou_grpd_l: "retain each layer as\na unified grouping\ninstead of separated\npolygons",
ou_grpd_l: ["retain each layer as","a unified grouping","instead of separated","polygons"],
ou_layr_s: "layer order",
ou_layr_l: "output layer order\nfrom top right to\nbottom left",
ou_layr_l: ["output layer order","from top right to","bottom left"],
ou_layo_s: "layer color",
ou_layo_l: "output layer colors\nfor each z index\noverridden by merged",
ou_layo_l: ["output layer colors","for each z index","overridden by merged"],
ou_drkn_s: "drag knife",
ou_drkn_l: "enable drag knife\noutput in gcode\ncut radii are added\nto corners with\ncut down passes",
ou_drkn_l: ["enable drag knife","output in gcode","cut radii are added","to corners with","cut down passes"],
// OUTPUT FDM
ou_nozl_s: "nozzle temp",
@ -410,54 +495,54 @@ kiri.lang['en-us'] = {
ou_bedd_s: "bed temp",
ou_bedd_l: "degrees in celsius",
ou_feed_s: "print speed",
ou_feed_l: "max print speed\nmillimeters / minute",
ou_feed_l: ["max print speed","millimeters / minute"],
ou_fini_s: "finish speed",
ou_fini_l: "outermost shell speed\nmillimeters / minute",
ou_fini_l: ["outermost shell speed","millimeters / minute"],
ou_move_s: "move speed",
ou_move_l: "non-print move speed\nmillimeters / minute\n0 = enable G0 moves",
ou_move_l: ["non-print move speed","millimeters / minute","0 = enable G0 moves"],
ou_shml_s: "shell factor",
ou_flml_s: "solid factor",
ou_spml_s: "infill factor",
ou_exml_l: "extrusion multiplier\n0.0 - 2.0",
ou_fanl_s: "fan layer",
ou_fanl_l: "layer to enable fan",
ou_exml_l: ["extrusion multiplier","0.0 - 2.0"],
ou_fans_s: "fan speed",
ou_fans_l: "0 - 255",
// OUTPUT CAM
ou_toll_s: "tolerance",
ou_toll_l: "surface precision\nin workspace units\nlower is slower and\nuses more memory\n0 = automatic based\non animate preference",
ou_toll_l: ["surface precision","in workspace units","lower is slower and","uses more memory","0 = automatic based","on animate preference"],
ou_zanc_s: "z anchor",
ou_zanc_l: "controls the position of the part\nwhen stock Z exceeds part Z",
ou_zanc_l: ["controls the position of the part","when stock Z exceeds part Z"],
ou_ztof_s: "z offset",
ou_ztof_l: "offset z anchor\nin workspace units\nhas no effect when\nanchor is middle",
ou_ztof_l: ["offset z anchor","in workspace units","has no effect when","anchor is middle"],
ou_zbot_s: "z bottom",
ou_zbot_l: "offset from part bottom\nto limit cutting depth\nin workspace units",
ou_zbot_l: ["offset from part bottom","to limit cutting depth","in workspace units"],
ou_zclr_s: "z clearance",
ou_zclr_l: "safe travel offset\nfrom top of part\nin workspace units",
ou_zclr_l: ["safe travel offset","from top of part","in workspace units"],
ou_ztru_s: "z thru",
ou_ztru_l: "extend cutout pass down\nin workspace units",
ou_ztru_l: ["extend cutout pass down","in workspace units"],
ou_conv_s: "conventional",
ou_conv_l: "milling direction\nuncheck for 'climb'",
ou_conv_l: ["milling direction","uncheck for 'climb'"],
ou_depf_s: "depth first",
ou_depf_l: "optimize pocket cuts\nwith depth priority",
ou_depf_l: ["optimize pocket cuts","with depth priority"],
// CAM STOCK
cs_menu: "stock",
cs_wdth_s: "width",
cs_wdth_l: "width (x) in workspace units\n0 defaults to part size",
cs_wdth_l: ["width (x) in workspace units","0 defaults to part size"],
cs_dpth_s: "depth",
cs_dpth_l: "depth (y) in workspace units\n0 defaults to part size",
cs_dpth_l: ["depth (y) in workspace units","0 defaults to part size"],
cs_hght_s: "height",
cs_hght_l: "height (z) in workspace units\n0 defaults to part size",
cs_hght_l: ["height (z) in workspace units","0 defaults to part size"],
cs_offs_s: "offset",
cs_offs_l: "use width, depth, height\nas offsets from max\npart size on platform",
cs_offs_l: ["use width, depth, height","as offsets from max","part size on platform"],
cs_clip_s: "clip to",
cs_clip_l: "rough and outline\nclip cutting paths\nto defined stock",
cs_clip_l: ["rough and outline","clip cutting paths","to defined stock"],
cs_offe_s: "enable",
cs_offe_l: "enable milling stock",
// ORIGIN (CAM & LASER)
or_bnds_s: "origin bounds",
or_bnds_l: "origin is relative to\nboundary of all objects",
or_bnds_l: ["origin is relative to","boundary of all objects"],
or_cntr_s: "origin center",
or_cntr_l: "origin is referenced from the center",
or_topp_s: "origin top",
@ -466,94 +551,100 @@ kiri.lang['en-us'] = {
// FDM ADVANCED
ad_menu: "expert",
ad_rdst_s: "retract dist",
ad_rdst_l: "amount to retract filament\nfor long moves. in millimeters",
ad_rdst_l: ["amount to retract filament","for long moves. in millimeters"],
ad_rrat_s: "retract rate",
ad_rrat_l: "speed of filament\nretraction in mm/s",
ad_rrat_l: ["speed of filament","retraction in mm/s"],
ad_rdwl_s: "engage dwell",
ad_rdwl_l: "time between re-engaging\nfilament and movement\nin milliseconds",
ad_wpln_s: "retract wipe",
ad_wpln_l: ["non-printing move","after retraction","in millimeters"],
ad_rdwl_l: ["time between re-engaging","filament and movement","in milliseconds"],
ad_scst_s: "shell coast",
ad_scst_l: "non-printing end\nof perimeter shells\nin millimeters",
ad_scst_l: ["non-printing end","of perimeter shells","in millimeters"],
ad_msol_s: "min solid",
ad_msol_l: "minimum area (mm^2)\nrequired to keep solid\nmust be > 0.1",
ad_msol_l: ["minimum area (mm^2)","required to keep solid","must be > 0.1"],
ad_mins_s: "min speed",
ad_mins_l: "minimum speed\nfor short segments",
ad_spol_s: "slow poly",
ad_spol_l: "polygons shorter than this\nwill have their print speed\nscaled down to min speed\nin millimeters",
ad_mins_l: ["minimum speed","for short segments"],
ad_spol_s: "short path",
ad_spol_l: ["polygons shorter than this","will have their print speed","scaled down to min speed","in millimeters"],
ad_arct_s: "arc tolerance",
ad_arct_l: ["convert faceted lines to arcs","center point drift tolerance","when matching arc points","consider values around 0.15","in millimeters","0 to disable"],
ad_zhop_s: "z hop dist",
ad_zhop_l: "amount to raise z\non retraction moves\nin millimeters\n0 to disable",
ad_zhop_l: ["amount to raise z","on retraction moves","in millimeters","0 to disable"],
ad_abkl_s: "anti-backlash",
ad_abkl_l: "use micro-movements to cancel\nbacklash during fills\nin millimeters",
ad_abkl_l: ["for better flat surface finish","use micro-movements to cancel","backlash in solid layer output","in millimeters","0 to disable","if your firmware has M425","put that in the gcode header","and leave this as 0"],
ad_lret_s: "layer retract",
ad_lret_l: "force filament retraction\nbetween layers",
ad_lret_l: ["force filament retraction","between layers"],
ad_play_s: "polish layers",
ad_play_l: "polish up to specified\n# of layers at a time",
ad_play_l: ["polish up to specified","# of layers at a time"],
ad_pspd_s: "polish speed",
ad_pspd_l: "polishing speed\nin millimeters / minute",
ad_pspd_l: ["polishing speed","in millimeters / minute"],
// CAM EXPERT
cx_fast_s: "skip shadow",
cx_fast_l: "disable overhang detection\ncan be faster and use less\nmemory with complex models\nbut fails with overhangs\ntry enabling if slicing\nhangs during shadowing",
cx_fast_l: ["disable overhang detection","can be faster and use less","memory with complex models","but fails with overhangs","try enabling if slicing","hangs during shadowing"],
// FDM GCODE
ag_menu: "gcode",
ag_nozl_s: "nozzle",
ag_nozl_l: "select output nozzle or head",
ag_peel_s: "peel guard",
ag_peel_l: ["starting at this belt z positoin","periodically roll the print off and","back on to the belt to unstick it","and prevent rolling deflection"],
ag_paws_s: "pause layers",
ag_paws_l: "comma-separated list of layers\nto inject pause commands before",
ag_paws_l: ["comma-separated list of layers","to inject pause commands before"],
ag_loop_s: "loop layers",
ag_loop_l: "layer ranges to repeat in the format\nfirst-last-count,first-last-count,...\nomitted count = 1",
ag_loop_l: ["layer ranges to repeat in the format","first-last-count,first-last-count,...","omitted count = 1"],
// SLA MENU
sa_menu: "slicing",
sa_lahe_s: "layer height",
sa_lahe_l: "layer height\nin millimeters",
sa_lahe_l: ["layer height","in millimeters"],
sa_shel_s: "hollow shell",
sa_shel_l: "shell thickness in mm\nuse multiple of layer height\nuse 0 for solid (disabled)",
sa_shel_l: ["shell thickness in mm","use multiple of layer height","use 0 for solid (disabled)"],
sa_otop_s: "open top",
sa_otop_l: "if shell is enabled\nresults in an open top",
sa_otop_l: ["if shell is enabled","results in an open top"],
sa_obas_s: "open base",
sa_obas_l: "if shell is enabled\nresults in an open base\ndisabled with supports",
sa_obas_l: ["if shell is enabled","results in an open base","disabled with supports"],
sa_layr_m: "layers",
sa_lton_s: "light on time",
sa_lton_l: "layer light on\ntime in seconds",
sa_lton_l: ["layer light on","time in seconds"],
sa_ltof_s: "light off time",
sa_ltof_l: "layer light off\ntime in seconds",
sa_ltof_l: ["layer light off","time in seconds"],
sa_pldi_s: "peel distance",
sa_pldi_l: "peel distance\nin millimeters",
sa_pldi_l: ["peel distance","in millimeters"],
sa_pllr_s: "peel lift rate",
sa_pllr_l: "peel lift speed\nin mm/sec",
sa_pllr_l: ["peel lift speed","in mm/sec"],
sa_pldr_s: "peel drop rate",
sa_pldr_l: "peel drop speed\nin mm/sec",
sa_pldr_l: ["peel drop speed","in mm/sec"],
sa_base_m: "base",
sa_balc_s: "layer count",
sa_balc_l: "number of\nbase layers",
sa_bltn_l: "base layer light on\ntime in seconds",
sa_bltf_l: "base layer light off\ntime in seconds",
sa_balc_l: ["number of","base layers"],
sa_bltn_l: ["base layer light on","time in seconds"],
sa_bltf_l: ["base layer light off","time in seconds"],
sa_infl_m: "infill",
sa_ifdn_s: "density",
sa_ifdn_l: "percent infill\nrequires shell\n0 = disabled\nvalid 0.0 - 1.0",
sa_ifdn_l: ["percent infill","requires shell","0 = disabled","valid 0.0 - 1.0"],
sa_iflw_s: "line width",
sa_iflw_l: "hatch line width\nin millimeters",
sa_iflw_l: ["hatch line width","in millimeters"],
sa_supp_m: "support",
sa_slyr_s: "base layers",
sa_slyr_l: "base support layers\nvalue range 0-10",
sa_slyr_l: ["base support layers","value range 0-10"],
sa_slgp_s: "gap layers",
sa_slgp_l: "number of layers between\nraft and bottom of object",
sa_slgp_l: ["number of layers between","raft and bottom of object"],
sa_sldn_s: "density",
sa_sldn_l: "used to compute the\nnumber of support pillars\n0.0-1.0 (0 = disable)",
sa_sldn_l: ["used to compute the","number of support pillars","0.0-1.0 (0 = disable)"],
sa_slsz_s: "size",
sa_slsz_l: "max size of a\nsupport pillar\nin millimeters",
sa_slsz_l: ["max size of a","support pillar","in millimeters"],
sa_slpt_s: "points",
sa_slpt_l: "number of points in\neach support pillar\nin millimeters",
sa_slpt_l: ["number of points in","each support pillar","in millimeters"],
sl_slen_l: "enable supports",
sa_outp_m: "output",
sa_opzo_s: "z offset",
sa_opzo_l: "z layer offset\nalmost always 0.0\n0.0-1.0 in millimeters",
sa_opzo_l: ["z layer offset","almost always 0.0","0.0-1.0 in millimeters"],
sa_opaa_s: "anti alias",
sa_opaa_l: "enable anti-aliasing\nproduces larger files\ncan blur details"
sa_opaa_l: ["enable anti-aliasing","produces larger files","can blur details"]
};

View file

@ -0,0 +1 @@
fr-help.html

1
web/kiri/lang/fr-fr.js Symbolic link
View file

@ -0,0 +1 @@
fr.js

176
web/kiri/lang/fr-help.html Normal file
View file

@ -0,0 +1,176 @@
<div id="help" class="f-col a-stretch">
<div class="title f-row j-center">
<label>Kiri:Moto Aide</label>
<label id="kiri-version"></label>
</div>
<div class="f-row">
<div id="help-sel" class="f-col">
<div class="help-label" tab="about">A propos</div>
<div class="help-label" tab="start">débuter</div>
<div class="help-label" tab="controls">contrôles</div>
<div class="help-label" tab="support">support</div>
<div class="help-label" tab="project">projet</div>
</div>
<div id="help-tabs" class="f-grow">
<div id="help-tab-about" class="help-tab f-col a-center">
<div class="content">
<p>
<b>Kiri:Moto</b> est un logiciel de découpe unique, fonctionnant dans un navigateur, entièrement open source, qui
transforme les modèles 3d en instructions de machine pour <u>des imprimantes 3D</u>, <u>des CNC</u>,
et <u>des graveurs laser</u>.
</p><br><p>
Le fait d'être basé sur un navigateur signifie que <i>tout</i> le processus de découpe a lieu dans le navigateur local,
et non dans le nuage. Vos données et modèles restent <i>totalement privés</i>.
</p><br><p>
Cela signifie que Kiri se comporte comme une application native de votre ordinateur avec en plus
la sécurité de l'exécution dans le bac à sable de sécurité du navigateur.
</p><br><p>
L'autre avantage de fonctionner à partir du web est que Kiri est toujours à jour. Les nouvelles
fonctionnalités, les corrections de bogues et les mises à jour sont fréquemment mises en place. Si vous souhaitez exécuter une ancienne
ou une version de développement de Kiri, sélectionnez cette version dans le menu utilisateur en haut à droite.
</p>
</div>
<div class="grow"></div>
<i>Kiri vient de Kiri-e qui est l'art Japonais du découpage de papiers</i>
<i>Moto est un raccourci pour "Modeling Tool" (Outil de modélisation)</i>
</div>
<div id="help-tab-start" class="help-tab f-col"><div class="content">
<b class="header">Guide de démarrage rapide</b>
<div class="content">
<p>
<li id="help-start">visionner <a href="https://youtu.be/08795Sj22QE" target="youtube">The Definitive Video Guide to Kiri:Moto 2.5</a></li>
<li><button><i class="fas fa-cogs"></i></button> le menu setup sélectionne le <button><i class="fas fa-cube"></i></button> type de matériel et un appareil</li>
<li><button><i class="fas fa-sliders-h"></i></button> les préférences permettent de régler l'interface et les caractéristiques de l'appareil</li>
<li><button><i class="fas fa-plus"></i></button> personnaliser un appareil si vous souhaitez modifier les paramètres par défaut</li>
<li><button><i class="fas fa-folder"></i></button> ouvre un fichier ou <button><i class="fas fa-file-upload"></i></button> importe des pièces dans l'espace de travail</li>
<li>utiliser les menus de droite pour personnaliser les paramètres de découpage</li>
<li><button><i class="fas fa-bars"></i></button> découper les objets pour voir l'effet des paramètres</li>
<li><button><i class="fas fa-cubes"></i></button> la prévisualisation calcule les données de cheminement des objets tranchés</li>
<li><button><i class="fas fa-file-download"></i></button> l'exportation convertit les chemins d'accès aux fichiers GCode et SVG</li>
</p>
</div>
<div class="content">
<b class="header">Modes de fonctionnement</b>
<p>Chaque mode (FDM, CAM, Laser, SLA) a des caractéristiques et des paramètres différents.
Explorez les vidéos Youtube pour en savoir plus.</p>
</div>
</div></div>
<div id="help-tab-print" class="help-tab f-col">about 3d printing</div>
<div id="help-tab-mill" class="help-tab f-col">about cnc milling</div>
<div id="help-tab-laser" class="help-tab f-col">about laser cutting</div>
<div id="help-tab-controls" class="help-tab f-col a-center"><div class="content">
<div class="f-row">
<table>
<tr><td colspan="2" class="header">Contrôles via le clavier</td><tr>
<tr><td></td></tr>
<tr><th>Touche</th><th>Action</th></tr>
<tr><td colspan=2 class="header2">Configuration</td></tr>
<tr><td><label>e</label></td><td>Sélection de l'appareil</td></tr>
<tr><td><label>o</label></td><td>CAM Sélection des outils</td></tr>
<tr><td><label>c</label></td><td>Grid Local Devices</td></tr>
<tr><td><label>q</label></td><td>Panneau des préférence</td></tr>
<tr><td><label>l</label></td><td>Profils de travail</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Mode</td></tr>
<tr><td><label>a</label></td><td>Organiser</td></tr>
<tr><td><label>s</label></td><td>Découper</td></tr>
<tr><td><label>p</label></td><td>Prévisualiser</td></tr>
<tr><td><label>g</label></td><td>Animer</td></tr>
<tr><td><label>x</label></td><td>Exporter</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Fichier</td></tr>
<tr><td><label>i</label></td><td>Importer</td></tr>
<tr><td><label>r</label></td><td>Récent</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Voir</td></tr>
<tr><td><label>h</label></td><td>Home</td></tr>
<tr><td><label>t</label></td><td>Top</td></tr>
<tr><td><label>f</label></td><td>Avant</td></tr>
<tr><td><label>b</label></td><td>Arrière</td></tr>
<tr><td><label>z</label></td><td>Réinitialiser la vue</td></tr>
<tr><td><label>&lt; &gt;</label></td><td>Alterner les vues de côtés</td></tr>
<tr><td><label>v</label></td><td>Focus (organiser)</td></tr>
<tr><td><label>v</label></td><td>Couche (découper/prévisualiser)</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Objets</td></tr>
<tr><td><label>d</label></td><td>Dupliquer</td></tr>
<tr><td><label>m</label></td><td>Miroir</td></tr>
<tr><td><label>o</label></td><td>Pivoter/tourner</td></tr>
<tr><td><label>delete</label></td><td>Supprimer</td></tr>
<tr><td><label>arrow</label></td><td>Tounrer de 90&deg;</td></tr>
<tr><td><label>shift-arrow</label></td><td>Tourner de 5&deg;</td></tr>
<tr><td><label>option-arrow</label></td><td>Déplacer de 5mm</td></tr>
<tr><td><label>ctrl-a</label></td><td>Tout sélectionner</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Aide</td></tr>
<tr><td><label>?</label></td><td>Cette fenêtre</td></tr>
<tr><td><br><br></td></tr>
<tr><td colspan="2" class="header">Contrôles via la souris</td><tr>
<tr><td></td></tr>
<tr><th>Combo</th><th>Action</th></tr>
<tr><td colspan=2 class="header2">View</td></tr>
<tr><td><label>shift</label> molette</td><td>Changer de couche</td></tr>
<tr><td><label>meta</label> déplacer</td><td>Pan</td></tr>
<tr><td><label>déplacer-droit</label></td><td>Pan</td></tr>
<tr><td><label>déplacer-gauche</label></td><td>Tourner</td></tr>
<tr><td><label>molette-souris</label></td><td>Zoom</td></tr>
<tr><td><label>déplacer-milieu</label></td><td>Zoom</td></tr>
<tr><td><label>meta</label> déplacer</td><td>Pan</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Objets</td></tr>
<tr><td><label>ctrl-clic</label></td><td>Aligner la face sur le plateau</td></tr>
<tr><td><label>meta-clic</label></td><td>Aligner la face sur le plateau</td></tr>
<tr><td></td></tr>
</table>
</div>
</div></div>
<div id="help-tab-support" class="help-tab f-col"><div class="content">
<b class="header">Ressources</b>
<div class="content nounderline">
<a target="_rsc" href="https://docs.grid.space/projects/kiri-moto"><i class="fas fa-question-circle"></i> Documentation étendue</a>
<a target="_rsc" href="https://forum.grid.space"><i class="fab fa-discourse"></i> fora Grid.Space</a>
<a target="_rsc" href="https://discord.gg/suyCCgr"><i class="fab fa-discord"></i> Discord serveur de chat en direct</a>
<a target="_rsc" href="https://www.youtube.com/playlist?list=PLRoVgyRoWZpumQIKWm7oldx3bjDivqP24"><i class="fab fa-youtube"></i> Youtube vidéos et tutoriels</a>
<a target="_rsc" href="https://twitter.com/grid_space_3d"><i class="fab fa-twitter"></i> Suivre Grid.Space sur Twitter</a>
<a target="_rsc" href="https://facebook.com/groups/kirimoto"><i class="fab fa-facebook"></i> Kiri:Moto Groupe d'usagers sur Facebook</a>
</div>
<b class="header">Options complémentaires</b>
<p>
Dans <button><i class="fas fa-user-cog"></i></button> le menu utilsateur en haut à droite,
utiliser l'option <span class="mono">export</span> pour créer unecopie téléchargeable des
profils du matériel et (optionellement) de l'espace de travail. Ce fichier pourra ensuite
être utilisé pour contrôler et rétablir un instantané de votre installation à tout moment.
Il peut également être partagé afin que les développeurs et autres utilisateurs puissent
reproduire votre environnement pour aider au débogage.
</p>
</div></div>
<div id="help-tab-project" class="help-tab f-col"><div class="content">
<b class="header">Open Source</b>
<p>
Kiri:Moto est totalement <a href="https://github.com/GridSpace/grid-apps" target="_github">open-source</a>.
Consultez la page du projet Github. Il existe des projets complémentaires, tant sur le plan matériel que logiciel.
Les nouveaux développeurs et les contributions au code sont les bienvenus à tout moment. La licence du code est celle du MIT.
</p><br><p>
<a target="_paypal" href="https://paypal.me/gridspace3d?locale.x=en_US">Toute donation</a> est également bienvenue.
</p>
<br><b class="header">Intégrations</b>
<p>Kiri:Moto est également intégré dans plusieurs autres applications web</p>
<li><a target="app" href="https://appstore.onshape.com/apps/CAM/EAAEWYIOMQKBENEMYW2N7MF253CT4WYL6SUJGEY=/description"><b>Onshape</b></a> via son magasin d'Applications</li>
<li><a target="app" href="http://www.thingiverse.com/apps/kirimoto/"><b>Thingiverse</b></a> en tant que native Thing Apps</li>
<li><a target="app" href="https://simplyprint.io">SimplyPrint.io</a> as the primary slicer under the covers</li>
<li><a target="app" href="https://sienci.com/camlab-help/">CAMLab</a> est une branche d'une version antérieure de Kiri:Moto</li>
</div></div>
</div>
</div>
</div>

642
web/kiri/lang/fr.js Normal file
View file

@ -0,0 +1,642 @@
// French localization for Kiri:Moto
// Will defer to English map for any missing key/value pairs
kiri.lang['fr'] =
kiri.lang['fr-fr'] = {
// common keys and menus
animate: "animer", // CAM animate button
arrange: "organiser", // layout workspace objects
axis: "axe", // left object scale pop menu
clear: "effacer", // clear workspace (remove all objects)
copy: "copier",
delete: "supprimer",
detail: "détail",
done: "terminé",
enable: "permettre",
export: "exporter",
files: "fichiers",
help: "aide",
ghost: "translucide", // left render pop menu (wireframe)
hide: "cacher", // left render pop menu (invisible)
home: "position d'origine",
import: "importer",
language: "langue",
machine: "machine", // device or machine
metric: "métrique",
name: "nom",
prefs: "préfs", // left menu "preferences"
preview: "prévisualiser",
recent: "récent",
render: "rendu", // left render pop menu
reset: "remettre",
rotate: "tourner", // left rotate pop menu
save: "sauvegarder",
scale: "échelle", // left object scale pop menu
setup: "setup",
settings: "paramètres",
size: "taille",
solid: "solide", // view type pop menu
tool: "outil",
tools: "outils", // CAM tool menu button
top: "haut",
type: "genre", // CAM tool type
version: "version",
view: "vue", // left view pop menu
wire: "filaire", // left render pop menu
acct_xpo: ["faites une sauvegarde de votre machine","et des profils des machines avec","l'option d'inclure les","objets et positions d'environnement"],
// RIGHT-CLICK CONTEXT MENU
rc_clws: "effacer l'environnement",
rc_xpws: "exporter l'environnement",
rc_lafl: "poser à plat",
rc_mirr: "refléter",
rc_dupl: "dupliquer",
rc_xstl: "exporter en STL",
// DEVICE MENU and related dialogs
dm_sldt: "sélectionner un genre de machine",
dm_stdd: "machines standards",
dm_mydd: "mes machines",
dm_seld: "machine sélectionnée",
dm_rcnt: "fichiers récents",
dm_savs: "paramètres sauvegardés",
dm_appp: "Préférences d'Application",
// CAM Tool Dialog
td_tyem: "plat", // end mill
td_tybm: "boule", // ball mill
td_tytm: "v forme", // taper mill
td_tonm: "outil #",
td_shft: "tige", // endmill shaft specs
td_flut: "flûte", // endmill flute specs
td_tapr: "cône", // endmill taper specs
// DEVICE dialog groups
dv_gr_dev: "périphérique",
dv_gr_ext: "extrudeur",
dv_gr_out: "sortie",
dv_gr_gco: "macros gcode",
// DEVICE dialog (_s = label, _l = hover help)
dv_name_s: "nom",
dv_name_l: "nom du matériel",
dv_fila_s: "filament",
dv_fila_l: "diamètre en millimètres",
dv_nozl_s: "buse",
dv_nozl_l: "diamètre en millimètres",
dv_bedw_s: "largeur",
dv_bedw_l: "unités de l'espace de travail",
dv_bedd_s: "profondeur",
dv_bedd_l: "unités de l'espace de travail",
dv_bedh_s: "hauteur",
dv_bedh_l: ["hauteur de construction maximale","en unités de l'espace de travail"],
dv_spmx_s: "max spindle",
dv_spmx_l: ["max spindle rpm speed","0 to disable"],
dv_xtab_s: "positionnement absolu",
dv_xtab_l: "déplacements extrusion absolu",
dv_orgc_s: "origine au centre",
dv_orgc_l: "origin du plateau au centre",
// dv_orgt_s: "origin top",
// dv_orgt_l: "part z origin top",
dv_bedc_s: "plateau circulaire",
dv_bedc_l: "le plateau du matériel est circulaire",
dv_belt_s: "plateau tapis-roulant",
dv_belt_l: "plateau à impression continue",
dv_retr_s: "rétraction firmware",
dv_retr_l: ["le firmware supporte G10/G11"],
dv_fanp_s: "refroidissement",
dv_fanp_l: "régler la puissance du ventilateur de refroidissement",
dv_prog_s: "progression",
dv_prog_l: "résultats de chaque % de progression",
dv_layr_s: "couche",
dv_layr_l: ["résultats de chaque","changement de couche"],
dv_tksp_s: "délimiteur d'instruction",
dv_tksp_l: ["ajouter un espace","entre les paramètres des axes","G0X0Y0X0","vs","G0 X0 Y0 Z0"],
dv_strc_s: "éliminer les commentaires",
dv_strc_l: ["éliminer les commentaires du gcode","les commentaires débutent par ;"],
dv_fext_s: "extension du fichier",
dv_fext_l: "extension du nom de fichier",
dv_dwll_s: "dwell",
dv_dwll_l: "script gcode dwell",
dv_tool_s: "changement d'outil",
dv_tool_l: "script de changement d'outil",
dv_sspd_s: "spindle speed",
dv_sspd_l: "set spindle speed",
dv_paus_s: "pause",
dv_paus_l: "script gcode de pause",
dv_head_s: "header",
dv_head_l: "script gcode de démarrage",
dv_foot_s: "footer",
dv_foot_l: "script gcode de fin",
dv_lzon_s: "laser on",
dv_lzon_l: "gcode laser on script",
dv_lzof_s: "laser off",
dv_lzof_l: "gcode laser off script",
dv_exts_s: "sélectionner",
dv_exts_l: "gcode pour activer cet extrudeur",
dv_dext_s: "désélectionner",
dv_dext_l: "gcode à lancer avant d'activer un autre extrudeur",
dv_extd_s: "désélectionner",
dv_extd_l: "gcode pour désactiver cet extrudeur",
dv_exox_s: "décalage x",
dv_exox_l: "décalage de buse en x",
dv_exoy_s: "décalage y",
dv_exoy_l: "décalage de buse en y",
// MODE
mo_menu: "mode",
mo_fdmp: "Impression FDM",
mo_slap: "Impression SLA",
mo_lazr: "Découpe / gravure Laser",
mo_cncm: "Routeur CNC",
// SETUP
su_menu: "installation",
su_devi: "Matériels",
su_tool: "Outils",
su_locl: "Local",
su_xprt: "Exporter",
su_help: "Aide",
// LOAD
fe_menu: "fichier",
fn_recn: "Récents",
fn_impo: "Importer",
// FUNCTION
fn_menu: "action",
fn_arra: "Organiser",
fn_slic: "Découper",
fn_prev: "Prévisualiser",
fn_expo: "Exporter",
// VIEW
vu_menu: "vue",
vu_home: "Origine",
vu_rset: "RAZ",
vu_sptp: "Haut",
vu_spfr: "Avant",
vu_splt: "Gauche",
vu_sprt: "Droite",
// WORKSPACE
ws_menu: "vue",
ws_save: "Sauvegarder",
ws_cler: "Effacer",
// OPTIONS
op_menu: "interface",
op_disp: "affichage",
op_xprt_s: "expert",
op_xprt_l: "afficher plus d'options de paramétrage",
op_decl_s: "logos",
op_decl_l: "montrer les logos des appareils",
op_dang_s: "expérimental",
op_dang_l: "montrer les paramètres expérimentaux",
op_hopo_s: "survol du menu",
op_hopo_l: ["activer le survol du menu", "pour activer"],
op_dark_s: "mode sombre",
op_dark_l: "interface en mode sombre",
op_comp_s: "ui compacte",
op_comp_l: ["interface utilisateur compacte", "plus adapté aux petits écrans", "et tablettes"],
op_shor_s: "montrer l'origine",
op_shor_l: "indiquer l'origine du dispositif ou du processus",
op_shru_s: "afficher les règles",
op_shru_l: ["montrer les règles des axes", "sur le quadrillage"],
op_sped_s: "afficher les vitesses",
op_sped_l: ["afficher la vitesse en barre de couleur","en mode prévisualisation"],
op_auto_s: "disposition automatique",
op_auto_l: ["disposition automatique sur le plateau", "lorsque de nouveaux éléments sont ajoutés"],
op_free_s: "disposition libre",
op_free_l: ["autoriser la disposition déplaçable","sans effet en mode laser"],
op_spcr_s: "espacement",
op_spcr_l: ["espacement entre les objets", "avec la disposition automatique", "en unités d'espace de travail"],
op_invr_s: "inverser zoom",
op_invr_l: ["inverser la molette de la souris", "faire défiler le zoom"],
op_save_s: "auto enregt",
op_save_l: ["préserve les objets dans l'espace de travail","entre rechargement de l'application"],
op_line_s: "type de ligne",
op_line_l: ["style de ligne pour le rendu","impacte les performances 3d","chemin: 3d meilleur","plat: 2d bon","ligne = 1d rapide"],
op_unit_s: "unités",
op_unit_l: ["les unités d'espace de travail affectent","vitesses et distances"],
op_anim_s: "animer",
op_anim_l: ["densité du maillage de l'animation","plus haut = plus dense","nécessite plus de mémoire","et est plus lent"],
lo_menu: "disposition",
pt_menu: "pièces",
pt_deci_s: "compression",
pt_deci_l: ["activer / désactiver la compression","durant l'importation. Tranchage plus rapide","et usage réduit de la mémoire"],
pt_qual_s: "qualité",
pt_qual_l: ["niveau de détail à garder", "lors des opérations de tranchage", "plus bas = plus rapide"],
pt_heal_s: "correction de maillage",
pt_heal_l: ["tentative de correction", "des mailles non-fermées", "accroit le temps de tranchage"],
xp_menu: "exports",
// SETTINGS
se_menu: "profil",
se_load: "charger",
se_save: "sauvegarder",
// FDM SLICING
sl_menu: "couches",
sl_lahi_s: "hauteur",
sl_lahi_l: ["hauteur de chaque couche", "couche en millimètres"],
ad_minl_s: "hauteur min",
ad_minl_l: ["hauteur de couche minimale adaptative", "en millimètres", "doit être non nulle"],
sl_ltop_s: "couches du haut",
sl_ltop_l: ["nombre de couches solides", "à appliquer en", "haut de l'impression"],
sl_lsld_s: "couches solides",
sl_lsld_l: ["zones de remplissage solide calculées", "à partir des deltas de couches. voir", "menu surgissant des couches"],
sl_lbot_s: "couches de base",
sl_lbot_l: ["nombre de couches solides", "à appliquer en", "bas de l'impression"],
ad_adap_s: "adaptive",
ad_adap_l: ["utiliser des hauteurs de couche adaptatives", "avec 'hauteur de couche' en maximum", "et 'hauteur min' en minimum"],
// FDM SHELLS
sl_shel_s: "parois",
sl_shel_l: ["nombre de périmètre", "murs à générer"],
sl_ordr_s: "ordre des parois",
sl_ordr_l: ["ordre d'impression des parois","de l'intérieur vers l'extérieur", "ou de l'extérieur vers l'intérieur","affecte la qualité de surface"],
sl_strt_s: "début de couche",
sl_strt_l: ["point de départ de la couche", "dernier = fin de la couche", "centre = centre de la pièce", "origine = origine du dispositif"],
ad_thin_s: "parois fines",
ad_thin_l: ["détecte et comble les vides", "entre les parois de la coque"],
// FDM FILL
fi_menu: "remplissage",
fi_type: "type",
fi_pcnt_s: "fraction de remplissage",
fi_pcnt_l: ["valeurs de densité de remplissage","0.0 - 1.0"],
fi_angl_s: "départ solide",
fi_angl_l: ["angle de départ en degrés", "90 degrés ajoutés à ", "chaque couche suivante", "s'applique uniquement aux couches solides"],
fi_over_s: "chevauchement",
fi_over_l: ["chevauchement de l'enveloppe et du remplissage", "en tant que fraction du diamètre de la buse", "0,0 - 1,0"],
// fi_rate_s: "vitesse d'impression",
fi_rate_l: ["vitesse d'extrusion du remplissage", "régler à 0 pour utiliser par défaut", "la vitesse d'impression"],
// FDM FIRST LAYER
fl_menu: "base",
fl_lahi_s: "hauteur de couche",
fl_lahi_l: ["hauteur de chaque couche", "en millimètres", "devrait être >= hauteur de la couche"],
fl_rate_s: "vitesse périmètres",
fl_rate_l: ["vitesse maximale d'impression des coques", "en millimètres / minute"],
fl_frat_s: "vitesse remplissage",
fl_frat_l: ["vitesse maximale d'impression du remplissage", "en millimètres / minute"],
fl_mult_s: "débit",
fl_mult_l: ["multiplicateur d'extrusion","0.0 - 2.0"],
fl_sfac_s: "largeur",
fl_sfac_l: ["multiplicateur de la taille de la buse", "modifie l'espacement des lignes"],
fl_skrt_s: "jupe nb",
fl_skrt_l: ["nombre de lignes de la jupe", "à générer pour la première couche"],
fl_skro_s: "décalage bordure",
fl_skro_l: ["jupe décalée de la partie", "en millimètres"],
fl_nozl_s: "buse",
fl_nozl_l: ["degrés en Celsius", "réglage de sortie utilisé", "lorsque celui-ci est égal à zéro"],
fl_bedd_s: "température plateau",
fl_bedd_l: ["degrés en Celsius", "réglage de sortie utilisé", "lorsque celui-ci est égal à zéro"],
fr_spac_s: "écart radeau",
fr_spac_l: ["espacement des couches supplémentaires", "entre la 1ère couche et le radeau", "en millimètres"],
fr_nabl_s: "activer un radeau",
fr_nabl_l: ["créer un radeau sous le ", "modèle pour une meilleure adhérence", "utilise le décalage des jupes et", "désactive la sortie des jupes"],
// FDM BELT ONLY
fl_zoff_s: "décalage bande",
fl_zoff_l: ["décalage de hauteur par rapport à la bande", "de l'extrusion la plus basse", "en millimètres"],
fl_brim_s: "taille de bordure",
fl_brim_l: ["ajoute une bordure à la partie inférieure","la taille est la largeur en millimètres","0 désactive"],
fl_brmn_s: "seuil de bordure",
fl_brmn_l: ["ajout de bordures uniquement quand le segment", "face à la bande est plus court que cela", "valeur en millimètres", "0 = infini"],
fl_bled_s: "ancrage de pièce",
fl_bled_l: ["ancrage de la pièce sur la bande", "au début de l'impression", "en millimètres"],
// FDM SUPPORT
sp_menu: "support",
sp_detect: "détecter",
sp_dens_s: "densité",
sp_dens_l: ["fraction 0.0 - 1.0","recommendé 0.15","0 désactive"],
sp_size_s: "taille pilier",
sp_size_l: ["largeur du pilier","en millimètres"],
sp_offs_s: "décalage du pilier",
sp_offs_l: ["décalage par rapport à la pièce","en millimètres"],
sp_gaps_s: "écart des couches",
sp_gaps_l: ["nombre de couches","de décalage de la pièce"],
sp_span_s: "portée maximale",
sp_span_l: ["portée non soutenue nécessitant", "un nouveau soutien à générer","en millimètres"],
sp_angl_s: "angle max",
sp_angl_l: ["angle de surplomb maximum avant", "de générer un pilier de soutien"],
sp_area_s: "surface min",
sp_area_l: ["surface minimale de","la colonne support","en millimètres"],
sp_xpnd_s: "étendre",
sp_xpnd_l: ["étendre la zone de soutien", "au-delà des limites d'une pièce","en millimètres"],
sp_nozl_s: "extrudeur",
sp_nozl_l: ["dans les systèmes multi-extrudeurs", "l'extrudeur à utiliser pour", "le matériau de support"],
sp_auto_s: "automatique",
sp_auto_l: ["activer les supports générés", "utiliser la géométrie at-slice-time", "les supports n'apparaîtront", "qu'une fois le tranchage terminé"],
// LASER SLICING
ls_offs_s: "offset",
ls_offs_l: ["adjust for beam width","en millimètres"],
ls_lahi_s: "height",
ls_lahi_l: ["layer height","in millimeters","0 = auto/detect"],
ls_lahm_s: "min",
ls_lahm_l: ["layer height minimum","will merge auto slices","under this thickness","in millimeters"],
ls_sngl_s: "single",
ls_sngl_l: ["perform only one slice","at specified layer height"],
// CNC COMMON terms
cc_tool: "tool",
cc_offs_s: "offset",
cc_offs_l: ["offset tool center","from chosen path"],
cc_spnd_s: "spindle rpm",
cc_spnd_l: ["spindle speed in","revolutions / minute"],
cc_sovr_s: "step over",
cc_sovr_l: ["as a fraction of","tool diameter"],
cc_sdwn_s: "step down",
cc_sdwn_l: ["step down depth","for each pass","in workspace units","0 to disable"],
cc_feed_s: "feed rate",
cc_feed_l: ["max cutting speed in","workspace units / minute"],
cc_plng_s: "plunge rate",
cc_plng_l: ["max z axis speed in","workspace units / minute"],
cc_sngl_s: "select lines only",
cc_sngl_l: ["select only single edges","instead of connected polylines"],
// CNC COMMON
cc_menu: "limits",
cc_flip: "flip",
cc_rapd_s: "xy feed",
cc_rapd_l: ["max xy moves feedrate","in workspace units / minute"],
cc_rzpd_s: "z feed",
cc_rzpd_l: ["max z moves feedrate","in workspace units / minute"],
cc_loff_s: "offset",
cc_loff_l: ["distance from stock face","for leveling pass","in workspace units"],
// CNC ROUGHING
cr_menu: "rough",
cr_lsto_s: "leave stock",
cr_lsto_l: ["horizontal offset from vertical faces","stock to leave for finishing pass","in workspace units"],
cr_ease_s: "ease down",
cr_ease_l: ["plunge cuts will","spiral down or ease","along a linear path"],
cr_clrt_s: "clear top",
cr_clrt_l: ["run a clearing pass over","the bounding area of the part","at z = 0"],
cr_clrp_s: "clear voids",
cr_clrp_l: ["mill out through pockets","instead of just the outline"],
cr_clrf_s: "clear faces",
cr_clrf_l: ["interpolate step down to","clear any detected flat areas"],
cr_olin_s: "inside only",
cr_olin_l: ["limit cutting to","inside part boundaries"],
// CNC OUTLINE
co_menu: "outline",
co_dogb_s: "dogbones",
co_dogb_l: ["insert dogbone cuts","into inside corners"],
co_wide_s: "wide cutout",
co_wide_l: ["widen outside cutout paths","for deep cuts in hard material"],
co_olin_s: "inside only",
co_olin_l: ["limit cutting to","inside part boundaries"],
co_olot_s: "outside only",
co_olot_l: ["limit cutting to","exterior part boundaries","which can be thought of","as the shadow outline"],
co_omit_s: "omit through",
co_omit_l: "eliminate thru holes",
co_olen_s: "enable",
co_olen_l: "enabled outline cutting",
// CNC CONTOUR
cn_menu: "contour",
cf_angl_s: "max angle",
cf_angl_l: ["angles greater than this","are considered vertical"],
cf_curv_s: "curves only",
cf_curv_l: ["limit linear cleanup","to curved surfaces"],
cf_olin_s: "inside only",
cf_olin_l: ["limit cutting to","inside part boundaries"],
cf_linx_s: "enable y pass",
cf_linx_l: "linear y-axis finishing",
cf_liny_s: "enable x pass",
cf_liny_l: "linear x-axis finishing",
// CNC TRACE
cu_menu: "trace",
cu_type_s: "type",
cu_type_l: ["follow = tool tip follows line","right or left = tool tip","follows line offset by tool radius"],
// CNC DRILLING
cd_menu: "drill",
cd_axis: "axis",
cd_points: "points",
cd_plpr_s: "plunge per",
cd_plpr_l: ["max plunge between","dwell periods","in workspace units","0 to disable"],
cd_dwll_s: "dwell time",
cd_dwll_l: ["dwell time","between plunges in","in milliseconds"],
cd_lift_s: "drill lift",
cd_lift_l: ["lift between plunges","after dwell period","in workspace units","0 to disable"],
cd_regi_s: "register",
cd_regi_l: ["drill registration holes","for double-sided parts","independent of enable","drilling but uses same","tool and settings"],
// CNC CUTOUT TABS
ct_menu: "tabs",
ct_angl_s: "angle",
ct_angl_l: ["starting angle for tab spacing","in degrees (0-360)"],
ct_numb_s: "count",
ct_numb_l: ["number of tabs to use","will be spaced evenly","around the part"],
ct_wdth_s: "width",
ct_wdth_l: "width in workspace units",
ct_hght_s: "height",
ct_hght_l: "height in workspace units",
ct_dpth_s: "depth",
ct_dpth_l: ["distance in workspace units","that the tab projects from","the part surface"],
ct_midl_s: "midline",
ct_midl_l: ["use midline of tab","instead of z bottom","for double-sided work"],
ct_nabl_s: "auto",
ct_nabl_l: ["auto generate radial tabs","projected from part center","using count and angle offset"],
// OUTPUT
ou_menu: "sortie",
// LASER KNIFE
dk_menu: "knife",
dk_dpth_s: "cut depth",
dk_dpth_l: ["final cut depth","in millimeters"],
dk_pass_s: "cut passes",
dk_pass_l: ["number of passes","down to cut depth"],
dk_offs_s: "tip offset",
dk_offs_l: ["distance from blade tip","to center of tool","in millimeters"],
// OUTPUT LASER
ou_spac_s: "spacing",
ou_spac_l: ["distance between layer output","in millimeters"],
ou_scal_s: "scaling",
ou_scal_l: "multiplier (0.1 to 100)",
ou_powr_s: "power",
ou_powr_l: ["0 - 100","represents %"],
ou_sped_s: "speed",
ou_sped_l: "millimeters / minute",
ou_mrgd_s: "merged",
ou_mrgd_l: ["merge all layers using","color coding to denote","stacking depth"],
ou_grpd_s: "grouped",
ou_grpd_l: ["retain each layer as","a unified grouping","instead of separated","polygons"],
ou_layr_s: "layer order",
ou_layr_l: ["output layer order","from top right to","bottom left"],
ou_layo_s: "layer color",
ou_layo_l: ["output layer colors","for each z index","overridden by merged"],
ou_drkn_s: "drag knife",
ou_drkn_l: ["enable drag knife","output in gcode","cut radii are added","to corners with","cut down passes"],
// OUTPUT FDM
ou_nozl_s: "température buse",
ou_nozl_l: "degrés en celsius",
ou_bedd_s: "température plateau",
ou_bedd_l: "degrés en celsius",
ou_feed_s: "vitesse impression",
ou_feed_l: ["vitesse d'impression maximale","millimètres / minute"],
ou_fini_s: "vitesse coque",
ou_fini_l: ["vitesse de la coque extérieure","millimètres / minute"],
ou_move_s: "vitesse déplacement",
ou_move_l: ["vitesse de déplacement sans impression","millimètres / minute","0 = active déplacements G0"],
ou_shml_s: "débit de coque",
ou_flml_s: "débit de solidité",
ou_spml_s: "débit de remplissage",
ou_exml_l: ["multiplicateur d'extrusion","0.0 - 2.0"],
ou_fans_s: "vitesse ventilateur",
ou_fans_l: "0 - 255",
// OUTPUT CAM
ou_toll_s: "tolerance",
ou_toll_l: ["surface precision","in workspace units","lower is slower and","uses more memory","0 = automatic based","on animate preference"],
ou_zanc_s: "z anchor",
ou_zanc_l: ["controls the position of the part","when stock Z exceeds part Z"],
ou_ztof_s: "z offset",
ou_ztof_l: ["offset z anchor","in workspace units","has no effect when","anchor is middle"],
ou_zbot_s: "z bottom",
ou_zbot_l: ["offset from part bottom","to limit cutting depth","in workspace units"],
ou_zclr_s: "z clearance",
ou_zclr_l: ["safe travel offset","from top of part","in workspace units"],
ou_ztru_s: "z thru",
ou_ztru_l: ["extend cutout pass down","in workspace units"],
ou_conv_s: "conventional",
ou_conv_l: ["milling direction","uncheck for 'climb'"],
ou_depf_s: "depth first",
ou_depf_l: ["optimize pocket cuts","with depth priority"],
// CAM STOCK
cs_menu: "stock",
cs_wdth_s: "width",
cs_wdth_l: ["width (x) in workspace units","0 defaults to part size"],
cs_dpth_s: "depth",
cs_dpth_l: ["depth (y) in workspace units","0 defaults to part size"],
cs_hght_s: "height",
cs_hght_l: ["height (z) in workspace units","0 defaults to part size"],
cs_offs_s: "offset",
cs_offs_l: ["use width, depth, height","as offsets from max","part size on platform"],
cs_clip_s: "clip to",
cs_clip_l: ["rough and outline","clip cutting paths","to defined stock"],
cs_offe_s: "enable",
cs_offe_l: "enable milling stock",
// ORIGIN (CAM & LASER)
or_bnds_s: "origin bounds",
or_bnds_l: ["origin is relative to","boundary of all objects"],
or_cntr_s: "origin center",
or_cntr_l: "origin is referenced from the center",
or_topp_s: "origin top",
or_topp_l: "origin is references from the top of objects",
// FDM ADVANCED
ad_menu: "expert",
ad_rdst_s: "distance de rétraction",
ad_rdst_l: ["longueur de filament à rétracter", "pour les longs mouvements. en millimètres"],
ad_rrat_s: "vitesse de rétraction",
ad_rrat_l: ["vitesse de rétraction","du filament en mm/s"],
ad_rdwl_s: "réenclencher",
ad_wpln_s: "essuyage rétractation",
ad_wpln_l: ["mouvement sans impression", "après rétraction", "en millimètres"],
ad_rdwl_l: ["durée de réenclenchement", "filament et mouvement", "en millisecondes"],
ad_scst_s: "cabotage",
ad_scst_l: ["extrémité non imprimée", "des coques du périmètre", "en millimètres"],
ad_msol_s: "min solide",
ad_msol_l: ["aire minimale (mm^2)","requise pour rester solide","doit être > 0.1"],
ad_mins_s: "vitesse minimale",
ad_mins_l: ["vitesse minimale","des segments courts"],
ad_spol_s: "chemin court",
ad_spol_l: ["les polygones plus courts", "auront la vitesse réduite et", "seront ramenés à la vitesse minimale", "en millimètres"],
ad_arct_s: "tolérance arc",
ad_arct_l: ["convertir les lignes des facettes en arcs", "tolérance de dérive du point central", "lors de la correspondance des points d'arc", "considérer les valeurs autour de 0,15", "en millimètres", "0 désactive"],
ad_zhop_s: "décalage en Z",
ad_zhop_l: ["levée du z","sur les mouvements de rétraction", "en millimètres", "0 désactive"],
ad_abkl_s: "anti-backlash",
ad_abkl_l: ["pour une meilleure finition de la surface plane", "utiliser des micro-mouvements pour annuler", "le jeu dans la sortie de la couche solide", "en millimètres", "0 désactive", "si le firmware reconnait le M425", "l'utiliser dans l'en-tête du gcode", "et mettre à 0"],
ad_lret_s: "rétraction au changement de couche",
ad_lret_l: ["forcer la rétraction du filament", "entre les couches"],
ad_play_s: "couches de polissage",
ad_play_l: ["polir jusqu'au nombre spécifié", "# de couches à la fois"],
ad_pspd_s: "vitesse de polissage",
ad_pspd_l: ["vitesse de polissage", "en millimètres / minute"],
// CAM EXPERT
cx_fast_s: "skip shadow",
cx_fast_l: ["disable overhang detection","can be faster and use less","memory with complex models","but fails with overhangs","try enabling if slicing","hangs during shadowing"],
// FDM GCODE
ag_menu: "gcode",
ag_nozl_s: "buse",
ag_nozl_l: "sélectionner la buse ou la tête d'impression",
ag_paws_s: "couches de pause",
ag_paws_l: ["liste de couches séparées par des virgules", "pour injecter des commandes de pause avant"],
ag_loop_s: "couches de répétitions",
ag_loop_l: ["des plages de couches à répéter dans le format ", "premier-compte dernier,premier-compte dernier,...", "compte omis = 1"],
// SLA MENU
sa_menu: "tranchage",
sa_lahe_s: "hauteur de couche",
sa_lahe_l: ["hauteur de couche","en millimètres"],
sa_shel_s: "hollow shell",
sa_shel_l: ["shell thickness in mm","use multiple of layer height","use 0 for solid (disabled)"],
sa_otop_s: "open top",
sa_otop_l: ["if shell is enabled","results in an open top"],
sa_obas_s: "open base",
sa_obas_l: ["if shell is enabled","results in an open base","disabled with supports"],
sa_layr_m: "layers",
sa_lton_s: "light on time",
sa_lton_l: ["layer light on","time in seconds"],
sa_ltof_s: "light off time",
sa_ltof_l: ["layer light off","time in seconds"],
sa_pldi_s: "peel distance",
sa_pldi_l: ["peel distance","in millimeters"],
sa_pllr_s: "peel lift rate",
sa_pllr_l: ["peel lift speed","in mm/sec"],
sa_pldr_s: "peel drop rate",
sa_pldr_l: ["peel drop speed","in mm/sec"],
sa_base_m: "base",
sa_balc_s: "layer count",
sa_balc_l: ["number of","base layers"],
sa_bltn_l: ["base layer light on","time in seconds"],
sa_bltf_l: ["base layer light off","time in seconds"],
sa_infl_m: "infill",
sa_ifdn_s: "density",
sa_ifdn_l: ["percent infill","requires shell","0 = disabled","valid 0.0 - 1.0"],
sa_iflw_s: "line width",
sa_iflw_l: ["hatch line width","in millimeters"],
sa_supp_m: "support",
sa_slyr_s: "base layers",
sa_slyr_l: ["base support layers","value range 0-10"],
sa_slgp_s: "gap layers",
sa_slgp_l: ["number of layers between","raft and bottom of object"],
sa_sldn_s: "density",
sa_sldn_l: ["used to compute the","number of support pillars","0.0-1.0 (0 = disable)"],
sa_slsz_s: "size",
sa_slsz_l: ["max size of a","support pillar","in millimeters"],
sa_slpt_s: "points",
sa_slpt_l: ["number of points in","each support pillar","in millimeters"],
sl_slen_l: "enable supports",
sa_outp_m: "output",
sa_opzo_s: "z offset",
sa_opzo_l: ["z layer offset","almost always 0.0","0.0-1.0 in millimeters"],
sa_opaa_s: "anti alias",
sa_opaa_l: ["enable anti-aliasing","produces larger files","can blur details"]
};

1
web/kiri/lang/pl-help.html Symbolic link
View file

@ -0,0 +1 @@
pl-pl-help.html

View file

@ -0,0 +1,180 @@
<div id="help" class="f-col a-stretch">
<!-- tłumaczenie jako własność publiczna (bez licencji) -->
<div class="title f-row j-center">
<label>Kiri:Moto Pomoc dla programu do cięcia modeli trójwymiarowych</label>
<label id="kiri-version"></label>
</div>
<div class="f-row">
<div id="help-sel" class="f-col">
<div class="help-label" tab="about">o programie</div>
<div class="help-label" tab="start">początki</div>
<div class="help-label" tab="controls">sterowanie</div>
<div class="help-label" tab="support">wsparcie</div>
<div class="help-label" tab="project">projekt</div>
</div>
<div id="help-tabs" class="f-grow">
<div id="help-tab-about" class="help-tab f-col a-center">
<div class="content">
<p>
<b>Kiri:Moto</b> jest unikalnym, opartym na przeglądarce, w pełni otwartym programem
do cięcia modeli, który zamienia modele 3d w instrukcje maszynowe dla <u>drukarek 3D</u>,
<u>frezarek CNC</u> i <u>wycinarek laserowych</u>.
</p><br>
<p>
Oparcie na przeglądarce oznacza, że <i>całe</i> przetwarzanie odbywa się w lokalnej
przeglądarce, a nie w chmurze. Twoje dane i modele pozostają <i>prywatne</i> tylko dla Ciebie.
</p><br>
<p>
Oznacza to, że Kiri zachowuje się tak samo, jak natywny program na komputerze użytkownika.
Bezpieczna tak jak bezpieczna jest piaskownica przeglądarki.
</p><br>
<p>
Kolejną zaletą działania w sieci jest to, że Kiri jest zawsze aktualny. Nowa strona
nowe funkcje, poprawki błędów i aktualizacje pojawiają się często. Jeśli chcesz uruchomić
starszą wersję Kiri lub wersję rozwojową, należy wybrać ją z menu użytkownika w prawym górnym rogu.
</p>
</div>
<div class="grow"></div>
<i>Słowo Kiri pochodzi od kiri-e, która jest japońską sztuką wycinania papieru</i>.
<i>Moto to skrót od "Modeling Tool"</i>.
</div>
<div id="help-tab-start" class="help-tab f-col"><div class="content">
<b class="header">Skrócona instrukcja obsługi</b>
<div class="content">
<p>
<li id="help-start">obejrzyj (po angielsku) <a href="https://youtu.be/08795Sj22QE" target="youtube">The Definitive Video Guide to Kiri:Moto 2.5</a></li>
<li><button><i class="fas fa-cogs"></i></button> menu ustawienia <button><i class="fas fa-cube"></i></button> urządzenia i tryby</li>
<li><button><i class="fas fa-sliders-h"></i></button> ustawienia interfejsu użytkownika i trybów</li>
<li><button><i class="fas fa-plus"></i></button> dopasowanie parametrów urządzenia, jeśli chcesz zmienić ustawienia domyślne</li>
<li><button><i class="fas fa-folder"></i></button> otwórz plik lub <button><i class="fas fa-file-upload"></i></button> zaimportuj element do przestrzeni roboczej</li>
<li>uzyj menu po prawej stronie by dopasować parametry krojenia obiektów</li>
<li><button><i class="fas fa-bars"></i></button> aby zobaczyć jak działają parametry, pokrój obiekty</li>
<li><button><i class="fas fa-cubes"></i></button> podgląd ścieżki z pokrojonych obiektów</li>
<li><button><i class="fas fa-file-download"></i></button> eksportuj ścieżki zmian do g-kodu lub pliku svg</li>
</p>
</div>
<div class="content">
<b class="header">Tryby pracy</b>
<p>Każdy tryb (FDM, CAM, Laser, SLA) ma inne funkcje i ustawienia.
Obejrzyj filmy na Youtube, aby dowiedzieć się więcej.
</p>
</div>
</div></div>
<div id="help-tab-print" class="help-tab f-col">o druku 3d</div>
<div id="help-tab-mill" class="help-tab f-col">o frezowaniu cnc</div>
<div id="help-tab-laser" class="help-tab f-col">o cięciu laserem</div>
<div id="help-tab-controls" class="help-tab f-col a-center"><div class="content">
<div class="f-row">
<table>
<tr><td colspan="2" class="header">Sterowanie klawiaturą</td><tr>
<tr><td></td></tr>
<tr><th>Klawisz</th><th>Działanie</th></tr>
<tr><td colspan=2 class="header2">Konfiguracja</td></tr>
<tr><td><label>e</label></td><td>Wybór urządzenia</td></tr>
<tr><td><label>o</label></td><td>Wybór narzędzi CAM</td></tr>
<tr><td><label>c</label></td><td>Lokalna siatka odniesienia urządzeń</td></tr>
<tr><td><label>q</label></td><td>Panel sterowania</td></tr>
<tr><td><label>l</label></td><td>Profile prac</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Tryb</td></tr>
<tr><td><label>a</label></td><td>Wyrównanie</td></tr>
<tr><td><label>s</label></td><td>Krojenie</td></tr>
<tr><td><label>p</label></td><td>Podgląd</td></tr>
<tr><td><label>g</label></td><td>Animacja</td></tr>
<tr><td><label>x</label></td><td>Eksport</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Plik</td></tr>
<tr><td><label>i</label></td><td>Import</td></tr>
<tr><td><label>r</label></td><td>Ostatnie</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Widok</td></tr>
<tr><td><label>h</label></td><td>Początek</td></tr>
<tr><td><label>t</label></td><td>Góra</td></tr>
<tr><td><label>f</label></td><td>Z przodu</td></tr>
<tr><td><label>b</label></td><td>Z tyłu</td></tr>
<tr><td><label>z</label></td><td>Przywrócenie ustawień widoku</td></tr>
<tr><td><label>&lt; &gt;</label></td><td>Widok cykli</td></tr>
<tr><td><label>v</label></td><td>Podświetl (wyrównanie)</td></tr>
<tr><td><label>v</label></td><td>Warstwa (plastry/podgląd)</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Obiekty</td></tr>
<tr><td><label>d</label></td><td>Powiel</td></tr>
<tr><td><label>m</label></td><td>Odbij</td></tr>
<tr><td><label>o</label></td><td>Ustawienia obrotów</td></tr>
<tr><td><label>delete</label></td><td>Kasowanie</td></tr>
<tr><td><label>arrow</label></td><td>Obrót o 90&deg;</td></tr>
<tr><td><label>shift-arrow</label></td><td>Obrót o 5&deg;</td></tr>
<tr><td><label>option-arrow</label></td><td>Przesuń o 5mm</td></tr>
<tr><td><label>ctrl-a</label></td><td>Zaznacz wszystko</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Pomoc</td></tr>
<tr><td><label>?</label></td><td>To okno</td></tr>
<tr><td><br><br></td></tr>
<tr><td colspan="2" class="header">Ustawienia myszy</td><tr>
<tr><td></td></tr>
<tr><th>Combo</th><th>Akcje</th></tr>
<tr><td colspan=2 class="header2">Widok</td></tr>
<tr><td><label>przesuń</label> koła</td><td>Zmiana warstwy</td></tr>
<tr><td><label>meta</label> przemieść</td><td>przemieść obiekty</td></tr>
<tr><td><label>prawy-przesuń</label></td><td>przemieść obiekty</td></tr>
<tr><td><label>lewy-drag</label></td><td>Obróć</td></tr>
<tr><td><label>kółko myszy</label></td><td>Powiększ</td></tr>
<tr><td><label>środkowy-przesuń</label></td><td>Powiększ</td></tr>
<tr><td><label>meta</label> przesuń</td><td>przemieść obiekty</td></tr>
<tr><td></td></tr>
<tr><td colspan=2 class="header2">Obiekty</td></tr>
<tr><td><label>ctrl-click</label></td><td>Dopasowanie ściany do podłoża</td></tr>
<tr><td><label>meta-click</label></td><td>Dopasowanie ściany do podłoża</td></tr>
<tr><td></td></tr>
</table>
</div>
</div></div>
<div id="help-tab-support" class="help-tab f-col"><div class="content">
<b class="header">Zasoby</b>
<div class="content nounderline">
<a target="_rsc" href="https://docs.grid.space/projects/kiri-moto"><i class="fas fa-question-circle"></i> Rozszerzona dokumentacja</a>
<a target="_rsc" href="https://forum.grid.space"><i class="fab fa-discourse"></i> Forum Grid.Space</a>
<a target="_rsc" href="https://discord.gg/suyCCgr"><i class="fab fa-discord"></i> czat na Discord</a>
<a target="_rsc" href="https://www.youtube.com/playlist?list=PLRoVgyRoWZpumQIKWm7oldx3bjDivqP24"><i class="fab fa-youtube"></i> Filmy i poradniki na Youtube</a>
<a target="_rsc" href="https://twitter.com/grid_space_3d"><i class="fab fa-twitter"></i> Śledź Grid.Space na Twitterze</a>
<a target="_rsc" href="https://facebook.com/groups/kirimoto"><i class="fab fa-facebook"></i> Kiri:Moto Grupa uzytkowników na feceboku (mordoksiążce)</a>
</div>
<b class="header">Opcje w programie</b>
<p>
Pod przyciskiem <button><i class="fas fa-user-cog"></i></button> menu użytkownika w prawym górnym rogu,
jest opcja <span class="mono">eksport</span>, tworzy on kopię do pobrania profili urządzenia
(opcjonalnie również obszaru roboczego). Plik ten może być użyty do tworzenia kolejnych wersji i
przywracania pełnej migawki konfiguracji w dowolnym momencie. Można go również udostępnić programistom
lub innym użytkownikom, aby mogli odtworzyć środowisko użytkownika. Może to pomóc w usuwaniu błędów.
</p>
</div></div>
<div id="help-tab-project" class="help-tab f-col"><div class="content">
<b class="header">Otwarte Oprogramowanie</b>
<p>
Kiri:Moto jest w całości <a href="https://github.com/GridSpace/grid-apps" target="_github">wolnym oprogramowaniem</a>.
Sprawdź stronę projektu na Githubie. Istnieją projekty towarzyszące, zarówno sprzętowe jak i programowe.
Nowi programiści i wkłady w kod źródłowy są oczywiście mile widziane. Kod źródłowy na licencji MIT.
</p><br>
<p>
<a target="_paypal" href="https://paypal.me/gridspace3d?locale.x=en_US">Dotacje</a> mile widziane.
</p>
<br><b class="header">Integrations</b>
<p>Kiri:Moto współdziała z różnymi innymi aplikacjami jako strona internetowa</p>
<li><a target="app" href="https://appstore.onshape.com/apps/CAM/EAAEWYIOMQKBENEMYW2N7MF253CT4WYL6SUJGEY=/description"><b>Onshape</b></a> w sklepie App Store</li>
<li><a target="app" href="http://www.thingiverse.com/apps/kirimoto/"><b>Thingiverse</b></a> jako natywne 'Thing Apps'</li>
<li>Jako główny program do cięcia uzywany przez <a target="app" href="https://simplyprint.io">SimplyPrint.io</a></li>
<li><a target="app" href="https://sienci.com/camlab-help/">CAMLab</a> jest programem pochodnym poprzedniej wersji Kiri:Moto</li>
</div></div>
</div>
</div>
</div>

645
web/kiri/lang/pl-pl.js Normal file
View file

@ -0,0 +1,645 @@
// Polish. Język polski tłumaczony na zasadach własności publicznej
kiri.lang['pl'] =
kiri.lang['pl-pl'] = {
// hasło i tłumaczenie
animate: "animacja", // CAM animate button
arrange: "rozmieść", // layout workspace objects
axis: "osie", // left object scale pop menu
clear: "wyczyść", //"czyszczenie z obiektów", // clear workspace (remove all objects)
copy: "kopiowanie",
delete: "kasowanie",
detail: "szczegóły",
done: "wykonaj",
enable: "włącz",
export: "eksport",
files: "pliki",
help: "pomoc",
ghost: "druciak", // left render pop menu (wireframe)
hide: "ukryj", // left render pop menu (invisible)
home: "początek",
import: "import",
language: "język",
machine: "maszyna", // device or machine
metric: "metryka",
name: "nazwa",
prefs: "ustawienia", // left menu "preferences"
preview: "podgląd",
recent: "recent",
render: "render", // left render pop menu
reset: "reset",
rotate: "obróć", // left rotate pop menu
save: "zapisz",
scale: "skala", // left object scale pop menu
setup: "ustaw",
settings: "ustawienia",
size: "wielkość",
slice: "plaserki",
solid: "wypełniony", // view type pop menu
start: "start",
tool: "narzędzie",
tools: "narzędzia", // CAM tool menu button
top: "góra",
type: "typ", // CAM tool type
version: "wersja",
view: "widok", // left view pop menu
wire: "druciak", // left render pop menu "wire"
acct_xpo: ["zrób backup swojego urządzenia","i profil urządzenia z","opcja by zapisać przestrzeń roboczą","obiekty i pozycje"],
// RIGHT-CLICK CONTEXT MENU
rc_clws: "wyczyść przestrzeń roboczą",
rc_xpws: "wyeksportuj przestrzeń roboczą",
rc_lafl: "ułóż płasko",
rc_mirr: "odbicie", // lustro
rc_dupl: "skopiuj",
rc_xstl: "zapisz w formacie STL",
// DEVICE MENU and related dialogs
dm_sldt: "wybierz rodzaj swojego urządzenia",
dm_stdd: "zwykłe urządzenia",
dm_mydd: "moje urządzenia",
dm_seld: "wybierz urządzenie",
dm_rcnt: "ostatnio uzywane pliki",
dm_savs: "zachowane ustawienia",
dm_appp: "Ustawienia programu",
// CAM Tool Dialog
td_tyem: "tępo zakończone", // end mill
td_tybm: "kula na końcu", // ball mill
td_tytm: "zwężający się", // taper mill
td_tonm: "narzędzie #",
td_shft: "wał (pionowy)", // endmill shaft specs
td_flut: "czołowy", // endmill flute specs
td_tapr: "zwężający się", // endmill taper specs
// DEVICE dialog groups
dv_gr_dev: "urządzenie",
dv_gr_ext: "ekstruder",
dv_gr_out: "wynik",
dv_gr_gco: "makra gkodu",
// DEVICE dialog (_s = label, _l = hover help)
dv_name_s: "nazwa",
dv_name_l: "nazwa urządzenia",
dv_fila_s: "filament",
dv_fila_l: "średnica w milimetrach",
dv_nozl_s: "dysza",
dv_nozl_l: "średnica w milimetrach",
dv_bedw_s: "szerokość",
dv_bedw_l: "pole robocze w jednostkach",
dv_bedd_s: "głębokość",
dv_bedd_l: "pole robocze w jednostkach",
dv_bedh_s: "wysokość",
dv_bedh_l: ["maksymalna wysokość budowania","w jednostkach pola roboczego"],
dv_spmx_s: "maksymalna prędkość wrzeciona",
dv_spmx_l: ["maksymalna prędkość wrzeciona w obrotach na minutę","0 wyłączone"],
dv_xtab_s: "ustawienie pozycji absolutnie",
dv_xtab_l: "absolutna wartość wypchnięcia",
dv_orgc_s: "środek odniesienia",
dv_orgc_l: "środek odniesienia na platformie",
// dv_orgt_s: "odniesienie u góry",
// dv_orgt_l: "oś z odniesienie u góry",
dv_bedc_s: "okrądła platforma drukowania",
dv_bedc_l: "platforma druku jest okrągła",
dv_belt_s: "pas na platformie",
dv_belt_l: "kontynuacja drukowania platformy",
dv_retr_s: "domyślne cofnięcie (retrakcja)",
dv_retr_l: ["firmware urządzenia wspiera kody G10/G11"],
dv_fanp_s: "zasilanie wiatraczków",
dv_fanp_l: "ustawienia zasilania wiatraczków",
dv_prog_s: "postęp",
dv_prog_l: "wynik w czasie każdego % postępu",
dv_layr_s: "warstwa",
dv_layr_l: ["output at each","layer change"],
dv_tksp_s: "token spacer",
dv_tksp_l: ["add a space between","gcode axis parameters","G0X0Y0X0","vs","G0 X0 Y0 Z0"],
dv_strc_s: "strip comments",
dv_strc_l: ["strip gcode comments","comments begin with ;"],
dv_fext_s: "rozszerzenie pliku",
dv_fext_l: "rozszerzenie pliku",
dv_dwll_s: "dwell",
dv_dwll_l: "gcode dwell script",
dv_tool_s: "zmiana narzędzia",
dv_tool_l: "skrypt zmiany narzędzia",
dv_sspd_s: "prędkość wrzeciona",
dv_sspd_l: "ustaw prędkość wrzeciona",
dv_paus_s: "pauza",
dv_paus_l: "gkod skryptu pauzy",
dv_head_s: "nagłówek (początek)",
dv_head_l: "skrypt gkod nagłówka",
dv_foot_s: "stopka (zakończenie)",
dv_foot_l: "skrypt gkod stopki",
dv_lzon_s: "laser włączony",
dv_lzon_l: "gcode laser on script",
dv_lzof_s: "laser wyłączony",
dv_lzof_l: "gcode laser off script",
dv_exts_s: "zaznacz",
dv_exts_l: "gcode run to enable this extruder",
dv_dext_s: "odznacz",
dv_dext_l: "gcode run before enabling another extruder",
dv_extd_s: "deselect",
dv_extd_l: "gcode to deselect this extruder",
dv_exox_s: "offset x",
dv_exox_l: "nozzle offset x",
dv_exoy_s: "offset y",
dv_exoy_l: "nozzle offset y",
// MODE
mo_menu: "tryb",
mo_fdmp: "FDM Druk przyrostowy",
mo_slap: "SLA Druk przyrostowy",
mo_lazr: "Ciecie laserem",
mo_cncm: "Frezowanie CNC",
// SETUP
su_menu: "ustawienia",
su_devi: "Urządzenia",
su_tool: "Narzędzia",
su_locl: "Lokalnie",
su_xprt: "Eksport",
su_help: "Pomoc",
// LOAD
fe_menu: "plik",
fn_recn: "ostatnie pliki",
fn_impo: "Importowanie",
// FUNCTION
fn_menu: "action",
fn_arra: "Arrange",
fn_slic: "Slice",
fn_prev: "Preview",
fn_expo: "Export",
// VIEW
vu_menu: "view",
vu_home: "Home",
vu_rset: "Reset",
vu_sptp: "Top",
vu_spfr: "Front",
vu_splt: "Left",
vu_sprt: "Right",
// WORKSPACE
ws_menu: "view",
ws_save: "Save",
ws_cler: "Clear",
// OPTIONS
op_menu: "interface",
op_disp: "display",
op_xprt_s: "expert",
op_xprt_l: "show more settings options",
op_decl_s: "decals",
op_decl_l: "show device decals and logos",
op_dang_s: "experimental",
op_dang_l: "show experimental parameters",
op_hopo_s: "hover pop",
op_hopo_l: ["enable menu hover","to activate"],
op_dark_s: "dark mode",
op_dark_l: "dark mode interface",
op_comp_s: "compact ui",
op_comp_l: ["compact user interface","better for small screens","and tablets"],
op_shor_s: "show origin",
op_shor_l: "show device or process origin",
op_shru_s: "show rulers",
op_shru_l: ["show axes rulers","on major gridlines"],
op_sped_s: "show speeds",
op_sped_l: ["show speed to color bar","in preview mode"],
op_auto_s: "auto layout",
op_auto_l: ["automatically layout platform","when new items added"],
op_free_s: "free layout",
op_free_l: ["permit dragable layout","no effect in laser mode"],
op_spcr_s: "spacing",
op_spcr_l: ["spacing between objects","during auto layout","in workspace units"],
op_orth_s: "orthographic",
op_orth_l: ["orthographic display","requires page refresh"],
op_invr_s: "invert zoom",
op_invr_l: ["invert mouse wheel","scroll zoom"],
op_save_s: "auto save",
op_save_l: ["preserve objects in workspace","between application reloads"],
op_line_s: "line type",
op_line_l: ["line style for path rendering","impacts 3d performance","path: 3d best","flat: 2d good","line = 1d fast"],
op_unit_s: "units",
op_unit_l: ["workspace units affects","speeds and distances"],
op_anim_s: "animate",
op_anim_l: ["animation mesh density","higher is more dense","takes more memory","and is slower"],
lo_menu: "layout",
pt_menu: "parts",
pt_deci_s: "decimate",
pt_deci_l: ["enable or disable point decimation","during port import. for faster slicing","and lower memory usage"],
pt_qual_s: "quality",
pt_qual_l: ["level of detail to retain","during slicing operations","lower is faster"],
pt_heal_s: "heal mesh",
pt_heal_l: ["attempt to heal","non-manifold meshes","adds to slicing time"],
xp_menu: "exports",
// SETTINGS
se_menu: "profile",
se_load: "load",
se_save: "save",
// FDM SLICING
sl_menu: "layers",
sl_lahi_s: "height",
sl_lahi_l: ["height of each slice","layer in millimeters"],
ad_minl_s: "height min",
ad_minl_l: ["adaptive min layer height","in millimeters","must be non-zero"],
sl_ltop_s: "top layers",
sl_ltop_l: ["number of solid layers","to enforce at the","top of the print"],
sl_lsld_s: "solid layers",
sl_lsld_l: ["solid fill areas computed","from layer deltas. see","layer pop menu"],
sl_lbot_s: "base layers",
sl_lbot_l: ["number of solid layers","to enforce at the","bottom of the print"],
ad_adap_s: "adaptive",
ad_adap_l: ["use adaptive layer heights","with 'layer height' as max","and 'layer min' as the min"],
// FDM SHELLS
sl_shel_s: "shell count",
sl_shel_l: ["number of perimeter","walls to generate"],
sl_ordr_s: "shell order",
sl_ordr_l: ["output shell order","inside to outside","or outside to inside","affects surface quality"],
sl_strt_s: "layer start",
sl_strt_l: ["layer starting point","last = last layer end","center = part center","origin = device origin"],
ad_thin_s: "thin walls",
ad_thin_l: ["detect and fill gaps","between shell walls"],
// FDM FILL
fi_menu: "infill",
fi_type: "type",
fi_pcnt_s: "fill fraction",
fi_pcnt_l: ["fill density values","0.0 - 1.0"],
fi_angl_s: "solid start",
fi_angl_l: ["starting angle in degrees","90 degrees added to ","each following layer","applies only to solid layers"],
fi_over_s: "overlap",
fi_over_l: ["overlap with shell and other fill","as fraction of nozzle diameter","0.0 - 1.0"],
// fi_rate_s: "print speed",
fi_rate_l: ["extrusion speed for infill","set to 0 to use default","output print speeds"],
// FDM FIRST LAYER
fl_menu: "base",
fl_lahi_s: "layer height",
fl_lahi_l: ["height of each slice","in millimeters","should be >= slice height"],
fl_rate_s: "shell speed",
fl_rate_l: ["shell printing max speed","in millimeters / minute"],
fl_frat_s: "fill speed",
fl_frat_l: ["fill printing max speed","in millimeters / minute"],
fl_mult_s: "flow factor",
fl_mult_l: ["extrusion multiplier","0.0 - 2.0"],
fl_sfac_s: "width factor",
fl_sfac_l: ["multiplier on nozzle size","changes line spacing"],
fl_skrt_s: "skirt count",
fl_skrt_l: ["number of first-layer offset","brims to generate"],
fl_skro_s: "skirt offset",
fl_skro_l: ["skirt offset from part","in millimeters"],
fl_nozl_s: "nozzle temp",
fl_nozl_l: ["degrees in celsius","output setting used","when this is zero"],
fl_bedd_s: "bed temp",
fl_bedd_l: ["degrees in celsius","output setting used","when this is zero"],
fr_spac_s: "raft gap",
fr_spac_l: ["additional layer spacing","between 1st layer and raft","in millimeters"],
fr_nabl_s: "raft enable",
fr_nabl_l: ["create a raft under the","model for better adhesion","uses skirt offset and","disables skirt output"],
// FDM BELT ONLY
fl_zoff_s: "belt offset",
fl_zoff_l: ["height offset from belt","of lowest extrusion","in millimeters"],
fl_brim_s: "brim size",
fl_brim_l: ["add brim to part bottom","size is width in millimeters","0 to disable"],
fl_brmn_s: "brim trigger",
fl_brmn_l: ["add brims only when segment","facing belt is shorter than this","value in millimeters","0 = Infinity"],
fl_bled_s: "part anchor",
fl_bled_l: ["belt part anchor","at start of print","in millimeters"],
// FDM SUPPORT
sp_menu: "support",
sp_detect: "detect",
sp_dens_s: "density",
sp_dens_l: ["fraction 0.0 - 1.0","recommended 0.15","0 to disable"],
sp_size_s: "pillar size",
sp_size_l: ["pillar width","in millimeters"],
sp_offs_s: "part offset",
sp_offs_l: ["offset from part","in millimeters"],
sp_gaps_s: "gap layers",
sp_gaps_l: ["number of layers","offset from part"],
sp_span_s: "max span",
sp_span_l: ["unsupported span that causes","a new support to be generated","in millimeters"],
sp_angl_s: "max angle",
sp_angl_l: ["max overhang angle before","supporting pillar is generated"],
sp_area_s: "min area",
sp_area_l: ["minimum area for","a support column","in millimeters"],
sp_xpnd_s: "expand",
sp_xpnd_l: ["expand support area","beyond part boundary","in millimeters"],
sp_nozl_s: "extruder",
sp_nozl_l: ["in multi-extruder systems","the extruder to use for","support material"],
sp_auto_s: "automatic",
sp_auto_l: ["enable generated supports","using at-slice-time geometry","supports will only appear","after slicing completes"],
// LASER SLICING
ls_offs_s: "offset",
ls_offs_l: ["adjust for beam width","in millimeters"],
ls_lahi_s: "height",
ls_lahi_l: ["layer height","in millimeters","0 = auto/detect"],
ls_lahm_s: "min",
ls_lahm_l: ["layer height minimum","will merge auto slices","under this thickness","in millimeters"],
ls_sngl_s: "single",
ls_sngl_l: ["perform only one slice","at specified layer height"],
// CNC COMMON terms
cc_tool: "tool",
cc_offs_s: "offset",
cc_offs_l: ["offset tool center","from chosen path"],
cc_spnd_s: "spindle rpm",
cc_spnd_l: ["spindle speed in","revolutions / minute"],
cc_sovr_s: "step over",
cc_sovr_l: ["as a fraction of","tool diameter"],
cc_sdwn_s: "step down",
cc_sdwn_l: ["step down depth","for each pass","in workspace units","0 to disable"],
cc_feed_s: "feed rate",
cc_feed_l: ["max cutting speed in","workspace units / minute"],
cc_plng_s: "plunge rate",
cc_plng_l: ["max z axis speed in","workspace units / minute"],
cc_sngl_s: "select lines only",
cc_sngl_l: ["select only single edges","instead of connected polylines"],
// CNC COMMON
cc_menu: "limits",
cc_flip: "flip",
cc_rapd_s: "xy feed",
cc_rapd_l: ["max xy moves feedrate","in workspace units / minute"],
cc_rzpd_s: "z feed",
cc_rzpd_l: ["max z moves feedrate","in workspace units / minute"],
cc_loff_s: "offset",
cc_loff_l: ["distance from stock face","for leveling pass","in workspace units"],
// CNC ROUGHING
cr_menu: "rough",
cr_lsto_s: "leave stock",
cr_lsto_l: ["horizontal offset from vertical faces","stock to leave for finishing pass","in workspace units"],
cr_ease_s: "ease down",
cr_ease_l: ["plunge cuts will","spiral down or ease","along a linear path"],
cr_clrt_s: "clear top",
cr_clrt_l: ["run a clearing pass over","the bounding area of the part","at z = 0"],
cr_clrp_s: "clear voids",
cr_clrp_l: ["mill out through pockets","instead of just the outline"],
cr_clrf_s: "clear faces",
cr_clrf_l: ["interpolate step down to","clear any detected flat areas"],
cr_olin_s: "inside only",
cr_olin_l: ["limit cutting to","inside part boundaries"],
// CNC OUTLINE
co_menu: "outline",
co_dogb_s: "dogbones",
co_dogb_l: ["insert dogbone cuts","into inside corners"],
co_wide_s: "wide cutout",
co_wide_l: ["widen outside cutout paths","for deep cuts in hard material"],
co_olin_s: "inside only",
co_olin_l: ["limit cutting to","inside part boundaries"],
co_olot_s: "outside only",
co_olot_l: ["limit cutting to","exterior part boundaries","which can be thought of","as the shadow outline"],
co_omit_s: "omit through",
co_omit_l: "eliminate thru holes",
co_olen_s: "enable",
co_olen_l: "enabled outline cutting",
// CNC CONTOUR
cn_menu: "contour",
cf_angl_s: "max angle",
cf_angl_l: ["angles greater than this","are considered vertical"],
cf_curv_s: "curves only",
cf_curv_l: ["limit linear cleanup","to curved surfaces"],
cf_olin_s: "inside only",
cf_olin_l: ["limit cutting to","inside part boundaries"],
cf_linx_s: "enable y pass",
cf_linx_l: "linear y-axis finishing",
cf_liny_s: "enable x pass",
cf_liny_l: "linear x-axis finishing",
// CNC TRACE
cu_menu: "trace",
cu_type_s: "type",
cu_type_l: ["follow = tool tip follows line","right or left = tool tip","follows line offset by tool radius"],
// CNC DRILLING
cd_menu: "drill",
cd_axis: "axis",
cd_points: "points",
cd_plpr_s: "plunge per",
cd_plpr_l: ["max plunge between","dwell periods","in workspace units","0 to disable"],
cd_dwll_s: "dwell time",
cd_dwll_l: ["dwell time","between plunges in","in milliseconds"],
cd_lift_s: "drill lift",
cd_lift_l: ["lift between plunges","after dwell period","in workspace units","0 to disable"],
cd_regi_s: "register",
cd_regi_l: ["drill registration holes","for double-sided parts","independent of enable","drilling but uses same","tool and settings"],
// CNC CUTOUT TABS
ct_menu: "tabs",
ct_angl_s: "angle",
ct_angl_l: ["starting angle for tab spacing","in degrees (0-360)"],
ct_numb_s: "count",
ct_numb_l: ["number of tabs to use","will be spaced evenly","around the part"],
ct_wdth_s: "width",
ct_wdth_l: "width in workspace units",
ct_hght_s: "height",
ct_hght_l: "height in workspace units",
ct_dpth_s: "depth",
ct_dpth_l: ["distance in workspace units","that the tab projects from","the part surface"],
ct_midl_s: "midline",
ct_midl_l: ["use midline of tab","instead of z bottom","for double-sided work"],
ct_nabl_s: "auto",
ct_nabl_l: ["auto generate radial tabs","projected from part center","using count and angle offset"],
// OUTPUT
ou_menu: "output",
// LASER KNIFE
dk_menu: "knife",
dk_dpth_s: "cut depth",
dk_dpth_l: ["final cut depth","in millimeters"],
dk_pass_s: "cut passes",
dk_pass_l: ["number of passes","down to cut depth"],
dk_offs_s: "tip offset",
dk_offs_l: ["distance from blade tip","to center of tool","in millimeters"],
// OUTPUT LASER
ou_spac_s: "spacing",
ou_spac_l: ["distance between layer output","in millimeters"],
ou_scal_s: "scaling",
ou_scal_l: "multiplier (0.1 to 100)",
ou_powr_s: "power",
ou_powr_l: ["0 - 100","represents %"],
ou_sped_s: "speed",
ou_sped_l: "millimeters / minute",
ou_mrgd_s: "merged",
ou_mrgd_l: ["merge all layers using","color coding to denote","stacking depth"],
ou_grpd_s: "grouped",
ou_grpd_l: ["retain each layer as","a unified grouping","instead of separated","polygons"],
ou_layr_s: "layer order",
ou_layr_l: ["output layer order","from top right to","bottom left"],
ou_layo_s: "layer color",
ou_layo_l: ["output layer colors","for each z index","overridden by merged"],
ou_drkn_s: "drag knife",
ou_drkn_l: ["enable drag knife","output in gcode","cut radii are added","to corners with","cut down passes"],
// OUTPUT FDM
ou_nozl_s: "nozzle temp",
ou_nozl_l: "degrees in celsius",
ou_bedd_s: "bed temp",
ou_bedd_l: "degrees in celsius",
ou_feed_s: "print speed",
ou_feed_l: ["max print speed","millimeters / minute"],
ou_fini_s: "finish speed",
ou_fini_l: ["outermost shell speed","millimeters / minute"],
ou_move_s: "move speed",
ou_move_l: ["non-print move speed","millimeters / minute","0 = enable G0 moves"],
ou_shml_s: "shell factor",
ou_flml_s: "solid factor",
ou_spml_s: "infill factor",
ou_exml_l: ["extrusion multiplier","0.0 - 2.0"],
ou_fans_s: "fan speed",
ou_fans_l: "0 - 255",
// OUTPUT CAM
ou_toll_s: "tolerance",
ou_toll_l: ["surface precision","in workspace units","lower is slower and","uses more memory","0 = automatic based","on animate preference"],
ou_zanc_s: "z anchor",
ou_zanc_l: ["controls the position of the part","when stock Z exceeds part Z"],
ou_ztof_s: "z offset",
ou_ztof_l: ["offset z anchor","in workspace units","has no effect when","anchor is middle"],
ou_zbot_s: "z bottom",
ou_zbot_l: ["offset from part bottom","to limit cutting depth","in workspace units"],
ou_zclr_s: "z clearance",
ou_zclr_l: ["safe travel offset","from top of part","in workspace units"],
ou_ztru_s: "z thru",
ou_ztru_l: ["extend cutout pass down","in workspace units"],
ou_conv_s: "conventional",
ou_conv_l: ["milling direction","uncheck for 'climb'"],
ou_depf_s: "depth first",
ou_depf_l: ["optimize pocket cuts","with depth priority"],
// CAM STOCK
cs_menu: "stock",
cs_wdth_s: "width",
cs_wdth_l: ["width (x) in workspace units","0 defaults to part size"],
cs_dpth_s: "depth",
cs_dpth_l: ["depth (y) in workspace units","0 defaults to part size"],
cs_hght_s: "height",
cs_hght_l: ["height (z) in workspace units","0 defaults to part size"],
cs_offs_s: "offset",
cs_offs_l: ["use width, depth, height","as offsets from max","part size on platform"],
cs_clip_s: "clip to",
cs_clip_l: ["rough and outline","clip cutting paths","to defined stock"],
cs_offe_s: "enable",
cs_offe_l: "enable milling stock",
// ORIGIN (CAM & LASER)
or_bnds_s: "origin bounds",
or_bnds_l: ["origin is relative to","boundary of all objects"],
or_cntr_s: "origin center",
or_cntr_l: "origin is referenced from the center",
or_topp_s: "origin top",
or_topp_l: "origin is references from the top of objects",
// FDM ADVANCED
ad_menu: "expert",
ad_rdst_s: "retract dist",
ad_rdst_l: ["amount to retract filament","for long moves. in millimeters"],
ad_rrat_s: "retract rate",
ad_rrat_l: ["speed of filament","retraction in mm/s"],
ad_rdwl_s: "engage dwell",
ad_wpln_s: "retract wipe",
ad_wpln_l: ["non-printing move","after retraction","in millimeters"],
ad_rdwl_l: ["time between re-engaging","filament and movement","in milliseconds"],
ad_scst_s: "shell coast",
ad_scst_l: ["non-printing end","of perimeter shells","in millimeters"],
ad_msol_s: "min solid",
ad_msol_l: ["minimum area (mm^2)","required to keep solid","must be > 0.1"],
ad_mins_s: "min speed",
ad_mins_l: ["minimum speed","for short segments"],
ad_spol_s: "short path",
ad_spol_l: ["polygons shorter than this","will have their print speed","scaled down to min speed","in millimeters"],
ad_arct_s: "arc tolerance",
ad_arct_l: ["convert faceted lines to arcs","center point drift tolerance","when matching arc points","consider values around 0.15","in millimeters","0 to disable"],
ad_zhop_s: "z hop dist",
ad_zhop_l: ["amount to raise z","on retraction moves","in millimeters","0 to disable"],
ad_abkl_s: "anti-backlash",
ad_abkl_l: ["for better flat surface finish","use micro-movements to cancel","backlash in solid layer output","in millimeters","0 to disable","if your firmware has M425","put that in the gcode header","and leave this as 0"],
ad_lret_s: "layer retract",
ad_lret_l: ["force filament retraction","between layers"],
ad_play_s: "polish layers",
ad_play_l: ["polish up to specified","# of layers at a time"],
ad_pspd_s: "polish speed",
ad_pspd_l: ["polishing speed","in millimeters / minute"],
// CAM EXPERT
cx_fast_s: "skip shadow",
cx_fast_l: ["disable overhang detection","can be faster and use less","memory with complex models","but fails with overhangs","try enabling if slicing","hangs during shadowing"],
// FDM GCODE
ag_menu: "gcode",
ag_nozl_s: "nozzle",
ag_nozl_l: "select output nozzle or head",
ag_paws_s: "pause layers",
ag_paws_l: ["comma-separated list of layers","to inject pause commands before"],
ag_loop_s: "loop layers",
ag_loop_l: ["layer ranges to repeat in the format","first-last-count,first-last-count,...","omitted count = 1"],
// SLA MENU
sa_menu: "slicing",
sa_lahe_s: "layer height",
sa_lahe_l: ["layer height","in millimeters"],
sa_shel_s: "hollow shell",
sa_shel_l: ["shell thickness in mm","use multiple of layer height","use 0 for solid (disabled)"],
sa_otop_s: "open top",
sa_otop_l: ["if shell is enabled","results in an open top"],
sa_obas_s: "open base",
sa_obas_l: ["if shell is enabled","results in an open base","disabled with supports"],
sa_layr_m: "layers",
sa_lton_s: "light on time",
sa_lton_l: ["layer light on","time in seconds"],
sa_ltof_s: "light off time",
sa_ltof_l: ["layer light off","time in seconds"],
sa_pldi_s: "peel distance",
sa_pldi_l: ["peel distance","in millimeters"],
sa_pllr_s: "peel lift rate",
sa_pllr_l: ["peel lift speed","in mm/sec"],
sa_pldr_s: "peel drop rate",
sa_pldr_l: ["peel drop speed","in mm/sec"],
sa_base_m: "base",
sa_balc_s: "layer count",
sa_balc_l: ["number of","base layers"],
sa_bltn_l: ["base layer light on","time in seconds"],
sa_bltf_l: ["base layer light off","time in seconds"],
sa_infl_m: "infill",
sa_ifdn_s: "density",
sa_ifdn_l: ["percent infill","requires shell","0 = disabled","valid 0.0 - 1.0"],
sa_iflw_s: "line width",
sa_iflw_l: ["hatch line width","in millimeters"],
sa_supp_m: "support",
sa_slyr_s: "base layers",
sa_slyr_l: ["base support layers","value range 0-10"],
sa_slgp_s: "gap layers",
sa_slgp_l: ["number of layers between","raft and bottom of object"],
sa_sldn_s: "density",
sa_sldn_l: ["used to compute the","number of support pillars","0.0-1.0 (0 = disable)"],
sa_slsz_s: "size",
sa_slsz_l: ["max size of a","support pillar","in millimeters"],
sa_slpt_s: "points",
sa_slpt_l: ["number of points in","each support pillar","in millimeters"],
sl_slen_l: "enable supports",
sa_outp_m: "output",
sa_opzo_s: "przesunięcie osi Z",
sa_opzo_l: ["z layer offset","almost always 0.0","0.0-1.0 in millimeters"],
sa_opaa_s: "antyaliasing",
sa_opaa_l: ["włączenie tej opcji","tworzy większe pliki","ale pokazuje przejścia tonalne"]
};

1
web/kiri/lang/pl.js Symbolic link
View file

@ -0,0 +1 @@
pl-pl.js