initial commit of gyroid infill
This commit is contained in:
parent
f9756c8844
commit
a6615fcde1
7 changed files with 214 additions and 13 deletions
156
js/geo-gyroid.js
Normal file
156
js/geo-gyroid.js
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
/** Copyright 2014-2018 Stewart Allen -- All Rights Reserved */
|
||||
|
||||
"use strict";
|
||||
|
||||
var gs_base_gyroid = exports;
|
||||
|
||||
(function() {
|
||||
if (!self.base) self.base = {};
|
||||
if (self.base.gyroid) return;
|
||||
|
||||
var base = self.base;
|
||||
|
||||
let PI2 = Math.PI * 2;
|
||||
|
||||
/**
|
||||
* @param off {number} z offset value from 0-1
|
||||
* @param res {number} resolution (pixels/slices per side)
|
||||
*/
|
||||
function slice(off, res) {
|
||||
let rez = parseInt(res || 200);
|
||||
let inc = PI2 / rez;
|
||||
let z = PI2 * off;
|
||||
|
||||
let edge = [];
|
||||
let vals = [];
|
||||
let points = 0;
|
||||
let points_lr = 0;
|
||||
let points_td = 0;
|
||||
for (let x=0; x<PI2; x += inc) {
|
||||
let vrow = []; // raw values row
|
||||
let erow = []; // edge values row
|
||||
edge.push(erow);
|
||||
vals.push(vrow);
|
||||
for (let y=0; y<PI2; y += inc) {
|
||||
erow.push(0);
|
||||
vrow.push(
|
||||
Math.sin(x) * Math.cos(y) +
|
||||
Math.sin(y) * Math.cos(z) +
|
||||
Math.sin(z) * Math.cos(x)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// left-right threshold search (red)
|
||||
vals.forEach((vrow, y) => {
|
||||
let erow = edge[y];
|
||||
let lval = vrow[vrow.length - 1];
|
||||
vrow.forEach((val, x) => {
|
||||
if (lval <= 0 && val >= 0) {
|
||||
erow[x] = 1;
|
||||
points++;
|
||||
points_lr++;
|
||||
}
|
||||
lval = val;
|
||||
})
|
||||
});
|
||||
|
||||
// top-down threshold search (green)
|
||||
for (let x=0; x<rez; x++) {
|
||||
let lval = vals[vals.length-1][x];
|
||||
for (let y=0; y<rez; y++) {
|
||||
let val = vals[y][x];
|
||||
if (lval <= 0 && val >= 0) {
|
||||
if (edge[y][x]) {
|
||||
edge[y][x] = 3;
|
||||
} else {
|
||||
edge[y][x] = 2;
|
||||
points++
|
||||
}
|
||||
points_td++;
|
||||
}
|
||||
lval = val;
|
||||
}
|
||||
}
|
||||
|
||||
// deterime prevailing direction for chaining
|
||||
let dir = points_td > points_lr ? 'lr' : 'td';
|
||||
|
||||
// create sparse representation
|
||||
let sparse = [];
|
||||
let center = rez / 2;
|
||||
edge.forEach((row,y) => {
|
||||
row.forEach((val,x) => {
|
||||
if (val) {
|
||||
let dx = Math.abs(x - center);
|
||||
let dy = Math.abs(y - center);
|
||||
sparse.push({x: x/rez, y: y/rez, val, dist: Math.max(dx,dy)});
|
||||
}
|
||||
});
|
||||
});
|
||||
sparse.sort((a,b) => {
|
||||
return b.dist - a.dist;
|
||||
});
|
||||
|
||||
// join sparse points array by closest distance
|
||||
let polys = [];
|
||||
let chain;
|
||||
let added;
|
||||
let cleared = 0;
|
||||
let maxdist = 0.05;
|
||||
|
||||
do {
|
||||
chain = null;
|
||||
for (let i=0; i<sparse.length; i++) {
|
||||
if (sparse[i]) {
|
||||
chain = [ sparse[i] ];
|
||||
polys.push(chain);
|
||||
sparse[i] = null;
|
||||
cleared++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
do {
|
||||
added = false;
|
||||
let target = chain[chain.length - 1];
|
||||
let cl_elm = null;
|
||||
let cl_idx = null;
|
||||
let cl_dst = Infinity;
|
||||
for (let i=0; i<sparse.length; i++) {
|
||||
let test_el = sparse[i];
|
||||
if (test_el) {
|
||||
let dst = dist(target, test_el, dir);
|
||||
if (cl_idx === null || dst < cl_dst) {
|
||||
cl_idx = i;
|
||||
cl_elm = test_el;
|
||||
cl_dst = dst;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cl_elm) {
|
||||
if (cl_dst > maxdist) {
|
||||
break;
|
||||
}
|
||||
sparse[cl_idx] = null;
|
||||
cleared++;
|
||||
chain.push(cl_elm);
|
||||
added = true;
|
||||
}
|
||||
} while (added);
|
||||
} while (cleared < sparse.length);
|
||||
|
||||
return {edge, points, dir, polys};
|
||||
}
|
||||
|
||||
function dist(a, b, dir) {
|
||||
let dx = a.x - b.x;
|
||||
let dy = a.y - b.y;
|
||||
// bias distance by prevailing direction of discovery to join stragglers
|
||||
if (dir === 'lr') dx = dx / 2;
|
||||
if (dir === 'td') dy = dy / 2;
|
||||
return Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
base.gyroid = { slice };
|
||||
|
||||
})();
|
||||
|
|
@ -65,7 +65,7 @@ var gs_kiri_fdm = exports;
|
|||
|
||||
SLICER.sliceWidget(widget, {
|
||||
height: spro.sliceHeight,
|
||||
minHeight: spro.sliceMinHeight,
|
||||
minHeight: spro.sliceHeight > spro.sliceMinHeight ? spro.sliceMinHeight : 0,
|
||||
firstHeight: spro.firstSliceHeight,
|
||||
view: view
|
||||
}, onSliceDone, onSliceUpdate);
|
||||
|
|
@ -149,7 +149,13 @@ var gs_kiri_fdm = exports;
|
|||
// sparse layers only present when non-vase mose and sparse % > 0
|
||||
if (!spro.sliceVase && spro.sliceFillSparse > 0.0) {
|
||||
forSlices(0.8, 1.0, function(slice) {
|
||||
slice.doSparseLayerFill(fillSpacing, spro.sliceFillSparse, widget.getBoundingBox());
|
||||
slice.doSparseLayerFill({
|
||||
spacing: fillSpacing,
|
||||
density: spro.sliceFillSparse,
|
||||
bounds: widget.getBoundingBox(),
|
||||
height: spro.sliceHeight,
|
||||
type: spro.sliceFillGyroid ? 'gyroid' : 'hex'
|
||||
});
|
||||
}, "infill");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -593,14 +593,16 @@ var gs_kiri_slice = exports;
|
|||
/**
|
||||
* Take output from pluggable sparse infill algorithm and clip to
|
||||
* the bounds of the top polygons and their inner solid areas.
|
||||
*
|
||||
* @param {number} spacing space between fill lines
|
||||
* @param {number} percent infill 0.0 - 1.0
|
||||
* @param {Object} bounds -- TODO calc w/out mesh so it can run in a worker
|
||||
*/
|
||||
PRO.doSparseLayerFill = function(spacing, percent, bounds) {
|
||||
PRO.doSparseLayerFill = function(options) {
|
||||
let spacing = options.spacing, // spacing space between fill lines
|
||||
density = options.density, // density of infill 0.0 - 1.0
|
||||
bounds = options.bounds, // bounding box of widget
|
||||
height = options.height, // z layer height
|
||||
type = options.type || 'hex';
|
||||
|
||||
this.isSparseFill = false;
|
||||
if (this.tops.length === 0 || percent === 0.0 || this.isSolidFill) return;
|
||||
if (this.tops.length === 0 || density === 0.0 || this.isSolidFill) return;
|
||||
|
||||
var scope = this,
|
||||
tops = scope.tops,
|
||||
|
|
@ -619,8 +621,9 @@ var gs_kiri_slice = exports;
|
|||
bounds: function() { return bounds },
|
||||
zIndex: function() { return scope.index },
|
||||
zValue: function() { return scope.z },
|
||||
density: function() { return percent },
|
||||
zHeight: function() { return height },
|
||||
offset: function() { return spacing },
|
||||
density: function() { return density },
|
||||
emit: function(x,y) { line.push(newPoint(x,y,scope.z)) },
|
||||
newline: function() {
|
||||
if (line.length > 0) {
|
||||
|
|
@ -631,8 +634,13 @@ var gs_kiri_slice = exports;
|
|||
};
|
||||
|
||||
scope.isSparseFill = true;
|
||||
// this is pluggable
|
||||
sparseFillHex(target, true);
|
||||
|
||||
// use specified fill type
|
||||
switch (type) {
|
||||
case 'hex': sparseFillHex(target, true); break;
|
||||
case 'gyroid': sparseFillGyroid(target); break;
|
||||
}
|
||||
|
||||
// force emit of last line
|
||||
target.newline();
|
||||
|
||||
|
|
@ -1280,6 +1288,32 @@ var gs_kiri_slice = exports;
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ 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-line","geo-slope","geo-polygon","geo-polygons", "geo-gyroid",
|
||||
"kiri-slice","kiri-slicer","kiri-driver-fdm","kiri-driver-cam",
|
||||
"kiri-driver-laser","kiri-widget","kiri-pack","kiri-print","kiri-codec"
|
||||
].forEach(function(scr) {
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ self.kiri.license = exports.LICENSE;
|
|||
sliceFillOverlap: 1,
|
||||
sliceFillSpacing: 1,
|
||||
sliceFillSparse: 1,
|
||||
sliceFillGyroid: 1,
|
||||
sliceSupportEnable: 1,
|
||||
sliceSupportDensity: 1,
|
||||
sliceSupportOffset: 1,
|
||||
|
|
@ -270,6 +271,7 @@ self.kiri.license = exports.LICENSE;
|
|||
sliceFillOverlap: 0.3,
|
||||
sliceFillSpacing: 1.0,
|
||||
sliceFillSparse: 0.5,
|
||||
sliceFillGyroid: false,
|
||||
|
||||
sliceSupportEnable: false,
|
||||
sliceSupportDensity: 0.25,
|
||||
|
|
@ -2462,6 +2464,7 @@ self.kiri.license = exports.LICENSE;
|
|||
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}),
|
||||
sliceFillGyroid: UC.newBoolean("fill gyroid", onBooleanClick, {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}),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
var exports = {
|
||||
COPYRIGHT:"Copyright (C) 2014-2018 Stewart Allen <sa@grid.space> - All Rights Reserved",
|
||||
LICENSE:"See the license.md file included with the source distribution",
|
||||
VERSION:"1.2.5a"
|
||||
VERSION:"1.2.6"
|
||||
};
|
||||
if (!module) var module = {};
|
||||
module.exports = exports;
|
||||
|
|
|
|||
|
|
@ -743,6 +743,7 @@ var ver = require('../js/license.js'),
|
|||
"geo-bounds",
|
||||
"geo-polygon",
|
||||
"geo-polygons",
|
||||
"geo-gyroid",
|
||||
"moto-kv",
|
||||
"moto-ajax",
|
||||
"moto-ctrl",
|
||||
|
|
@ -795,6 +796,7 @@ var ver = require('../js/license.js'),
|
|||
"geo-bounds",
|
||||
"geo-polygon",
|
||||
"geo-polygons",
|
||||
"geo-gyroid",
|
||||
"kiri-slice",
|
||||
"kiri-slicer",
|
||||
"kiri-driver-fdm",
|
||||
|
|
|
|||
Loading…
Reference in a new issue