diff --git a/js/kiri-driver-fdm.js b/js/kiri-driver-fdm.js
index ed1d0f7f..ac83e367 100644
--- a/js/kiri-driver-fdm.js
+++ b/js/kiri-driver-fdm.js
@@ -45,11 +45,12 @@ var gs_kiri_fdm = exports;
update_start = time(),
minSolid = spro.sliceSolidMinArea,
solidLayers = spro.sliceSolidLayers,
- doSolidLayers = solidLayers && !spro.sliceVase,
+ vaseMode = spro.sliceFillType === 'vase',
+ doSolidLayers = solidLayers && !vaseMode,
firstOffset = sdev.nozzleSize / 2,
shellOffset = sdev.nozzleSize * spro.sliceShellSpacing,
fillOffset = shellOffset * settings.synth.fillOffsetMult,
- fillSpacing = sdev.nozzleSize * spro.sliceFillSpacing,
+ fillSpacing = sdev.nozzleSize,
sliceFillAngle = spro.sliceFillAngle,
view = widget.mesh && widget.mesh.newGroup ? widget.mesh.newGroup() : null;
@@ -111,9 +112,9 @@ var gs_kiri_fdm = exports;
slice.index < spro.sliceBottomLayers ||
slice.index > slices.length - spro.sliceTopLayers-1 ||
spro.sliceFillSparse > 0.95
- ) && !spro.sliceVase;
+ ) && !vaseMode;
slice.doShells(spro.sliceShells, firstOffset, shellOffset, fillOffset, {
- vase: spro.sliceVase,
+ vase: vaseMode,
thin: false && spro.detectThinWalls
});
if (solid) slice.doSolidLayerFill(fillSpacing, sliceFillAngle);
@@ -147,14 +148,14 @@ var gs_kiri_fdm = exports;
}
// sparse layers only present when non-vase mose and sparse % > 0
- if (!spro.sliceVase && spro.sliceFillSparse > 0.0) {
+ if (!vaseMode && spro.sliceFillSparse > 0.0) {
forSlices(0.8, 1.0, function(slice) {
slice.doSparseLayerFill({
spacing: fillSpacing,
density: spro.sliceFillSparse,
bounds: widget.getBoundingBox(),
height: spro.sliceHeight,
- type: spro.sliceFillGyroid ? 'gyroid' : 'hex'
+ type: spro.sliceFillType
});
}, "infill");
}
diff --git a/js/kiri-fill.js b/js/kiri-fill.js
new file mode 100644
index 00000000..aeec2d13
--- /dev/null
+++ b/js/kiri-fill.js
@@ -0,0 +1,121 @@
+/** Copyright 2014-2019 Stewart Allen -- All Rights Reserved */
+
+"use strict";
+
+var gs_kiri_fill = exports;
+
+(function() {
+
+ if (!self.kiri) self.kiri = {};
+ if (self.kiri.fill) return;
+
+ var KIRI = self.kiri,
+ BASE = self.base,
+ UTIL = BASE.util,
+ ROUND = UTIL.round,
+ DEG2RAD = Math.PI / 180,
+ FILL = self.kiri.fill = {
+ hex: sparseFillHexFull,
+ gyroid: sparseFillGyroid
+ };
+
+ function sparseFillHexFull(target) {
+ sparseFillHex(target, true);
+ }
+
+ /**
+ * emitter creates a hex infill pattern and sends to target
+ *
+ * @param {Object} target
+ * @param {boolean} full continuous walls
+ */
+ function sparseFillHex(target, full) {
+ // compute segment lengths (vert/horiz and 45)
+ var spacing = target.offset() / 2,
+ vhlen = (1 - target.density()) * 4 + spacing,
+ anxlen = ROUND(Math.cos(30 * DEG2RAD) * vhlen, 7),
+ anylen = ROUND(Math.sin(30 * DEG2RAD) * vhlen, 7),
+ bounds = target.bounds(),
+ even = true,
+ evenZ = target.zIndex() % 2 === 0,
+ maxy = bounds.max.y + (vhlen + anylen * 2),
+ x, y;
+
+ if (full || evenZ) {
+ x = bounds.min.x;
+ for (;;) {
+ if (even && x > bounds.max.x) break;
+ if (!even && x > bounds.max.x + anxlen + spacing) break;
+ y = bounds.min.y;
+ target.newline();
+ while (y <= maxy) {
+ target.emit(x,y);
+ y += vhlen;
+ target.emit(x,y);
+ if (even) x += anxlen; else x -= anxlen;
+ y += anylen;
+ target.emit(x,y);
+ y += vhlen;
+ target.emit(x,y);
+ if (even) x -= anxlen; else x += anxlen;
+ y += anylen;
+ }
+ x += spacing;
+ if (even) x += (anxlen * 2);
+ even = !even;
+ target.newline();
+ }
+ } else {
+ y = bounds.min.y + vhlen;
+ for (;;) {
+ if (even && y > bounds.max.y) break;
+ if (!even && y > bounds.max.y + anylen) break;
+ x = bounds.min.x;
+ target.newline();
+ while (x < bounds.max.x) {
+ target.emit(x,y);
+ if (even) y += anylen; else y -= anylen;
+ x += anxlen;
+ target.emit(x,y);
+ x += spacing;
+ target.emit(x,y);
+ if (even) y -= anylen; else y += anylen;
+ x += anxlen;
+ target.emit(x,y);
+ x += spacing;
+ }
+ y += vhlen;
+ if (even) y += (anylen * 2);
+ even = !even;
+ target.newline();
+ }
+ }
+ }
+
+ function sparseFillGyroid(target) {
+ let bounds = target.bounds();
+ let height = target.zHeight();
+ let span_x = bounds.max.x - bounds.min.x;
+ let span_y = bounds.max.y - bounds.min.y;
+ let density = target.density();
+ let tile = 1 + (1 - density) * 15;
+ let tile_x = span_x / tile;
+ let tile_y = span_y / tile;
+ let tile_z = 1 / tile;
+ let gyroid = BASE.gyroid.slice(target.zValue() * tile_z, (1 - density) * 500);
+
+ gyroid.polys.forEach(poly => {
+ for (let tx=0; tx<=tile_x; tx++) {
+ for (let ty=0; ty<=tile_y; ty++) {
+ target.newline();
+ let bx = tx * tile + bounds.min.x;
+ let by = ty * tile + bounds.min.y;
+ poly.forEach(point => {
+ target.emit(bx + point.x * tile, by + point.y * tile);
+ });
+ }
+ }
+ });
+ }
+
+})();
diff --git a/js/kiri-slice.js b/js/kiri-slice.js
index 01752b00..ec1e4905 100644
--- a/js/kiri-slice.js
+++ b/js/kiri-slice.js
@@ -10,16 +10,15 @@ var gs_kiri_slice = exports;
if (self.kiri.Slice) return;
var KIRI = self.kiri,
+ FILL = KIRI.fill,
BASE = self.base,
UTIL = BASE.util,
POLY = BASE.polygons,
PRO = Slice.prototype,
fillArea = POLY.fillArea,
newPoint = BASE.newPoint,
- ROUND = UTIL.round,
MIN = Math.min,
MAX = Math.max,
- DEG2RAD = Math.PI / 180,
NOKEY = BASE.key.NONE,
outline_colors = [0xffff00, 0x00ffff, 0xff00ff, 0xff0000, 0x00ff00, 0x0000ff, 0xffffff, 0x000000],
trace_color = 0x000000,
@@ -636,9 +635,8 @@ var gs_kiri_slice = exports;
scope.isSparseFill = true;
// use specified fill type
- switch (type) {
- case 'hex': sparseFillHex(target, true); break;
- case 'gyroid': sparseFillGyroid(target); break;
+ if (type && FILL[type]) {
+ FILL[type](target);
}
// force emit of last line
@@ -1219,101 +1217,6 @@ var gs_kiri_slice = exports;
}
}
- /**
- * emitter creates a hex infill pattern and sends to target
- *
- * @param {Object} target
- * @param {boolean} full continuous walls
- */
- function sparseFillHex(target, full) {
- // compute segment lengths (vert/horiz and 45)
- var spacing = target.offset() / 2,
- vhlen = (1 - target.density()) * 4 + spacing,
- anxlen = ROUND(Math.cos(30 * DEG2RAD) * vhlen, 7),
- anylen = ROUND(Math.sin(30 * DEG2RAD) * vhlen, 7),
- bounds = target.bounds(),
- even = true,
- evenZ = target.zIndex() % 2 === 0,
- maxy = bounds.max.y + (vhlen + anylen * 2),
- x, y;
-
- if (full || evenZ) {
- x = bounds.min.x;
- for (;;) {
- if (even && x > bounds.max.x) break;
- if (!even && x > bounds.max.x + anxlen + spacing) break;
- y = bounds.min.y;
- target.newline();
- while (y <= maxy) {
- target.emit(x,y);
- y += vhlen;
- target.emit(x,y);
- if (even) x += anxlen; else x -= anxlen;
- y += anylen;
- target.emit(x,y);
- y += vhlen;
- target.emit(x,y);
- if (even) x -= anxlen; else x += anxlen;
- y += anylen;
- }
- x += spacing;
- if (even) x += (anxlen * 2);
- even = !even;
- target.newline();
- }
- } else {
- y = bounds.min.y + vhlen;
- for (;;) {
- if (even && y > bounds.max.y) break;
- if (!even && y > bounds.max.y + anylen) break;
- x = bounds.min.x;
- target.newline();
- while (x < bounds.max.x) {
- target.emit(x,y);
- if (even) y += anylen; else y -= anylen;
- x += anxlen;
- target.emit(x,y);
- x += spacing;
- target.emit(x,y);
- if (even) y -= anylen; else y += anylen;
- x += anxlen;
- target.emit(x,y);
- x += spacing;
- }
- y += vhlen;
- if (even) y += (anylen * 2);
- even = !even;
- target.newline();
- }
- }
- }
-
- function sparseFillGyroid(target) {
- let bounds = target.bounds();
- let height = target.zHeight();
- let span_x = bounds.max.x - bounds.min.x;
- let span_y = bounds.max.y - bounds.min.y;
- let density = target.density();
- let tile = 1 + (1 - density) * 15;
- let tile_x = span_x / tile;
- let tile_y = span_y / tile;
- let tile_z = 1 / tile;
- let gyroid = BASE.gyroid.slice(target.zValue() * tile_z, (1 - density) * 500);
-
- gyroid.polys.forEach(poly => {
- for (let tx=0; tx<=tile_x; tx++) {
- for (let ty=0; ty<=tile_y; ty++) {
- target.newline();
- let bx = tx * tile + bounds.min.x;
- let by = ty * tile + bounds.min.y;
- poly.forEach(point => {
- target.emit(bx + point.x * tile, by + point.y * tile);
- });
- }
- }
- });
- }
-
function newTop(poly) {
return new Top(poly);
}
diff --git a/js/kiri-work.js b/js/kiri-work.js
index 124de09f..869534ef 100644
--- a/js/kiri-work.js
+++ b/js/kiri-work.js
@@ -154,8 +154,8 @@ if (self.window) {
[
"license","ext-n3d","ext-clip","add-array",
"add-three","geo","geo-point","geo-debug","geo-bounds",
- "geo-line","geo-slope","geo-polygon","geo-polygons", "geo-gyroid",
- "kiri-slice","kiri-slicer","kiri-driver-fdm","kiri-driver-cam",
+ "geo-line","geo-slope","geo-polygon","geo-polygons","geo-gyroid",
+ "kiri-fill","kiri-slice","kiri-slicer","kiri-driver-fdm","kiri-driver-cam",
"kiri-driver-laser","kiri-widget","kiri-pack","kiri-print","kiri-codec"
].forEach(function(scr) {
importScripts(["/js/",scr,".js","/v"+ver].join(''));
diff --git a/js/kiri.js b/js/kiri.js
index 2842228d..58e51b65 100644
--- a/js/kiri.js
+++ b/js/kiri.js
@@ -58,9 +58,8 @@ self.kiri.license = exports.LICENSE;
sliceShellSpacing: 1,
sliceFillAngle: 1,
sliceFillOverlap: 1,
- sliceFillSpacing: 1,
sliceFillSparse: 1,
- sliceFillGyroid: 1,
+ sliceFillType: 1,
sliceSupportEnable: 1,
sliceSupportDensity: 1,
sliceSupportOffset: 1,
@@ -73,7 +72,6 @@ self.kiri.license = exports.LICENSE;
sliceSolidLayers: 1,
sliceBottomLayers: 1,
sliceTopLayers: 1,
- sliceVase: 1,
firstSliceHeight: 1,
firstLayerRate: 1,
firstLayerFillRate: 1,
@@ -202,6 +200,17 @@ self.kiri.license = exports.LICENSE;
},
// --------------- (default)
settings = {
+ infill:[
+ {
+ name: "vase"
+ },
+ {
+ name: "hex"
+ },
+ {
+ name: "gyroid"
+ }
+ ],
// CAM only
tools:[
{
@@ -272,9 +281,8 @@ self.kiri.license = exports.LICENSE;
sliceShellSpacing: 1.0,
sliceFillAngle: 45,
sliceFillOverlap: 0.3,
- sliceFillSpacing: 1.0,
sliceFillSparse: 0.5,
- sliceFillGyroid: false,
+ sliceFillType: "hex",
sliceSupportEnable: false,
sliceSupportDensity: 0.25,
@@ -289,7 +297,6 @@ self.kiri.license = exports.LICENSE;
sliceSolidLayers: 3,
sliceBottomLayers: 3,
sliceTopLayers: 3,
- sliceVase: false,
firstSliceHeight: 0.25,
firstLayerRate: 30,
@@ -1707,6 +1714,7 @@ self.kiri.license = exports.LICENSE;
var key, val;
fillMissingSettings(settingsDefault, settings);
+ settings.infill = settingsDefault.infill;
for (key in scope) {
if (!scope.hasOwnProperty(key)) continue;
@@ -1721,11 +1729,16 @@ self.kiri.license = exports.LICENSE;
} else if (typ === 'select-one') {
uie.innerHTML = '';
var chosen = null;
- settings.tools.forEach(function(tool,index) {
- if (val === tool.id) chosen = index+1;
+ var source = uie.parentNode.getAttribute('source');
+ var list = settings[source];
+ list.forEach(function(tool, index) {
+ let id = tool.id || tool.name;
+ if (val === id) {
+ chosen = index + 1;
+ }
var opt = DOC.createElement('option');
opt.appendChild(DOC.createTextNode(tool.name));
- opt.setAttribute('value', tool.id);
+ opt.setAttribute('value', id);
uie.appendChild(opt);
});
if (chosen) uie.selectedIndex = chosen;
@@ -1750,7 +1763,8 @@ self.kiri.license = exports.LICENSE;
if (!scope.hasOwnProperty(key)) continue;
if (UI.hasOwnProperty(key)) {
var nval = null,
- uie = UI[key];
+ uie = UI[key],
+ src = uie.parentNode.getAttribute('source');
// skip empty UI values
if (!uie || uie === '') continue;
if (uie.type === 'text') {
@@ -1759,7 +1773,10 @@ self.kiri.license = exports.LICENSE;
nval = UI[key].checked;
} else if (uie.type === 'select-one') {
if (uie.selectedIndex > 0) {
- nval = parseInt(uie.options[uie.selectedIndex].value);
+ nval = uie.options[uie.selectedIndex].value;
+ if (src === 'tools') {
+ nval = parseInt(nval);
+ }
}
}
if (scope[key] != nval) {
@@ -2479,22 +2496,22 @@ self.kiri.license = exports.LICENSE;
bedWidth: UC.newInput("width", {title:"millimeters", convert:UC.toInt, modes:LASER}),
bedDepth: UC.newInput("depth", {title:"millimeters", convert:UC.toInt, modes:LASER}),
- process: UC.newGroup("process", control, {modes:FDM_LASER}),
+ process: UC.newGroup("slicing", control, {modes:FDM_LASER}),
// 3d print
sliceHeight: UC.newInput("layer height", {title:"millimeters", convert:UC.toFloat, modes:FDM}),
sliceShells: UC.newInput("shell count", {convert:UC.toInt, modes:FDM}),
sliceShellSpacing: UC.newInput("shell spacing", {title:"as a percentage of nozzle width\n< 1.0 causes shell overlap\nrecommended 0.85 - 1.0", convert:UC.toFloat, bound:UC.bound(0.5,1.0), modes:FDM}),
- sliceFillOverlap: UC.newInput("fill overlap", {title:"overlap with shell\nas % of nozzle width\nhigher bonds better\n0.0 - 1.0", convert:UC.toFloat, bound:UC.bound(0.0,2.0), modes:FDM}),
- sliceFillSpacing: UC.newInput("fill spacing", {title:"for solid fill areas\nas a percentage of nozzle width\n< 1.0 causes fill overlap\nrecommended 0.85 - 1.0", convert:UC.toFloat, bound:UC.bound(0.0,2.0), modes:FDM}),
- sliceFillAngle: UC.newInput("fill angle", {title:"base angle in degrees", convert:UC.toFloat, modes:FDM}),
- sliceFillSparse: UC.newInput("fill ratio", {title:"for infill areas\n0.0 - 1.0", convert:UC.toFloat, bound:UC.bound(0.0,1.0), modes:FDM}),
sliceSolidMinArea: UC.newInput("solid area", {title:"minimum area (mm^2)\nrequired to keep solid\nmust be > 0.1", convert:UC.toFloat, modes:FDM}),
sliceSolidLayers: UC.newInput("solid layers", {title:"flat area fill projections\nbased on layer deltas", convert:UC.toInt, modes:FDM}),
sliceBottomLayers: UC.newInput("base layers", {title:"bottom solid layer count", convert:UC.toInt, modes:FDM}),
sliceTopLayers: UC.newInput("top layers", {title:"top solid layer count", convert:UC.toInt, modes:FDM}),
- sliceVase: UC.newBoolean("vase mode", onBooleanClick, {modes:FDM}),
- sliceFillGyroid: UC.newBoolean("gyroid infill", onBooleanClick, {title: "best with fine layers\nand large interior voids", modes:FDM}),
+
+ process: UC.newGroup("fill", control, {modes:FDM}),
+ sliceFillOverlap: UC.newInput("shell overlap", {title:"overlap with shell\nas % of nozzle width\nhigher bonds better\n0.0 - 1.0", convert:UC.toFloat, bound:UC.bound(0.0,2.0), modes:FDM}),
+ sliceFillAngle: UC.newInput("solid angle", {title:"base angle in degrees", convert:UC.toFloat, modes:FDM}),
+ sliceFillSparse: UC.newInput("percentage", {title:"for infill areas\n0.0 - 1.0", convert:UC.toFloat, bound:UC.bound(0.0,1.0), modes:FDM}),
+ sliceFillType: UC.newSelectField("type", {modes:FDM}, "infill"),
// laser
laserOffset: UC.newInput("cut offset", {title:"millimeters\nadjust for beam width", convert:UC.toFloat, modes:LASER}),
diff --git a/js/license.js b/js/license.js
index 031f4f7b..c753573a 100644
--- a/js/license.js
+++ b/js/license.js
@@ -1,7 +1,7 @@
var exports = {
COPYRIGHT:"Copyright (C) 2014-2019 Stewart Allen - All Rights Reserved",
LICENSE:"See the license.md file included with the source distribution",
- VERSION:"1.3.3"
+ VERSION:"1.4.0"
};
if (!module) var module = {};
module.exports = exports;
diff --git a/js/moto-ui.js b/js/moto-ui.js
index 60c09a7e..05337420 100644
--- a/js/moto-ui.js
+++ b/js/moto-ui.js
@@ -291,7 +291,7 @@ var gs_moto_ui = exports;
return ip;
}
- function newSelectField(label, options) {
+ function newSelectField(label, options, source) {
var row = newDiv(options),
ip = DOC.createElement('select'),
hide = options && options.hide,
@@ -299,6 +299,7 @@ var gs_moto_ui = exports;
lastDiv.appendChild(row);
row.appendChild(newLabel(label));
row.appendChild(ip);
+ row.setAttribute("source", source || "tools");
row.setAttribute("class", ["flow-row",lastGroup].join(" "));
row.style.display = hide ? 'none' : '';
if (options) {
diff --git a/js/web-server.js b/js/web-server.js
index 44427faf..4cf408ec 100644
--- a/js/web-server.js
+++ b/js/web-server.js
@@ -759,6 +759,7 @@ var ver = require('../js/license.js'),
"moto-db",
"moto-ui",
"kiri-lang",
+ "kiri-fill",
"kiri-db",
"kiri-slice",
"kiri-slicer",
@@ -805,6 +806,7 @@ var ver = require('../js/license.js'),
"geo-polygons",
"geo-gyroid",
"kiri-lang",
+ "kiri-fill",
"kiri-slice",
"kiri-slicer",
"kiri-driver-fdm",
diff --git a/web/kiri/style.css b/web/kiri/style.css
index a92d09ee..cc76196b 100644
--- a/web/kiri/style.css
+++ b/web/kiri/style.css
@@ -345,7 +345,7 @@ button.selected {
flex-grow: 1;
width: 100%;
height: 100%;
- color: white;
+ color: black;
border: 0;
margin-bottom: 2px;
padding-bottom: 2px;