add Mesh:Tool SVG import options for extrusion depth and boolean repair
This commit is contained in:
parent
4754ee9909
commit
5df0262d8f
4 changed files with 90 additions and 15 deletions
|
|
@ -4,6 +4,7 @@ Full docs @ https://docs.grid.space/projects/kiri-moto
|
|||
|
||||
# Release 3.7
|
||||
|
||||
* add Mesh:Tool SVG import options for extrusion depth and boolean repair
|
||||
* replace jscad/modeling with Manifold project for faster mesh boolean
|
||||
* various CAM gcode, preview, animation fixes (3 axis)
|
||||
* various mobile touch, file load fixes
|
||||
|
|
|
|||
|
|
@ -16,19 +16,19 @@ if (load.File) return;
|
|||
gapp.register('load.file', []);
|
||||
|
||||
const types = {
|
||||
stl(data, file, resolve, reject) {
|
||||
stl(data, file, resolve, reject, opt = {}) {
|
||||
resolve([{
|
||||
mesh: new load.STL().parse(data), file
|
||||
}]);
|
||||
},
|
||||
|
||||
obj(data, file, resolve, reject) {
|
||||
obj(data, file, resolve, reject, opt = {}) {
|
||||
resolve(load.OBJ.parse(data).map(m => {
|
||||
return { mesh: m.toFloat32(), file: nameOf(file, m.name, 1) }
|
||||
}));
|
||||
},
|
||||
|
||||
"3mf"(data, file, resolve, reject) {
|
||||
"3mf"(data, file, resolve, reject, opt = {}) {
|
||||
let i = 1;
|
||||
load.TMF.parseAsync(data).then((meshes) => {
|
||||
resolve(meshes.map(m => {
|
||||
|
|
@ -37,12 +37,13 @@ const types = {
|
|||
});
|
||||
},
|
||||
|
||||
svg(data, file, resolve, reject) {
|
||||
resolve(load.SVG.parse(data).map(m => { return {mesh: m.toFloat32(), file} }));
|
||||
svg(data, file, resolve, reject, opt = {}) {
|
||||
resolve(load.SVG.parse(data, opt).map(m => { return {mesh: m.toFloat32(), file} }));
|
||||
},
|
||||
|
||||
png(data, file, resolve, reject) {
|
||||
png(data, file, resolve, reject, opt = {}) {
|
||||
load.PNG.parse(data, {
|
||||
...opt,
|
||||
done(data) {
|
||||
resolve({ mesh: data, file });
|
||||
}
|
||||
|
|
@ -72,7 +73,7 @@ function load_data(data, file, ext, opt = {}) {
|
|||
return new Promise((resolve, reject) => {
|
||||
let fn = types[ext];
|
||||
if (fn) {
|
||||
fn(data, file, resolve, reject);
|
||||
fn(data, file, resolve, reject, opt);
|
||||
} else {
|
||||
reject(`unknown file type: "${ext}" from ${file}`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,17 +17,29 @@ function parseAsync(text, opt) {
|
|||
}
|
||||
|
||||
function parse(text, opt = {}) {
|
||||
let objs = [];
|
||||
let data = new THREE.SVGLoader().parse(text);
|
||||
let paths = data.paths;
|
||||
let xmlat = data.xml.attributes;
|
||||
let depth = opt.depth || xmlat['data-km-extrude']
|
||||
const fromSoup = opt.soup || false;
|
||||
const objs = [];
|
||||
const data = new THREE.SVGLoader().parse(text);
|
||||
const paths = data.paths;
|
||||
const xmlat = data.xml.attributes;
|
||||
const polys = fromSoup ? [] : undefined;
|
||||
const depth = opt.depth || xmlat['data-km-extrude']
|
||||
|| xmlat['extrude']
|
||||
|| {value: 5};
|
||||
|
||||
|
||||
for (let i = 0; i < paths.length; i++) {
|
||||
let path = paths[i];
|
||||
let shapes = path.toShapes(true);
|
||||
if (fromSoup) {
|
||||
for (let node of shapes) {
|
||||
let { shape, holes } = node.extractPoints();
|
||||
for (let path of [ shape, ...holes ]) {
|
||||
polys.push(base.newPolygon().addPoints(path.map(p => base.newPoint(p.x, p.y, 0))));
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let geom = new THREE.ExtrudeGeometry(shapes, {
|
||||
steps: 1,
|
||||
depth: parseFloat(depth.value),
|
||||
|
|
@ -49,6 +61,41 @@ function parse(text, opt = {}) {
|
|||
objs.push([ ...array ]);
|
||||
}
|
||||
|
||||
if (fromSoup) {
|
||||
const nest = base.polygons.nest(polys.filter(p => {
|
||||
// filter duplicates
|
||||
for (let pc of polys) {
|
||||
if (pc === p) {
|
||||
return true;
|
||||
} else {
|
||||
return !pc.isEquivalent(p);
|
||||
}
|
||||
}
|
||||
}));
|
||||
let z = parseFloat(depth.value);
|
||||
for (let poly of nest) {
|
||||
let obj = [];
|
||||
let earcut = poly.earcut();
|
||||
for (let poly of earcut) {
|
||||
for (let point of poly.points) {
|
||||
obj.push(point.x, point.y, z);
|
||||
}
|
||||
for (let point of poly.points.reverse()) {
|
||||
obj.push(point.x, point.y, 0);
|
||||
}
|
||||
}
|
||||
obj.appendAll(poly.extrude(z));
|
||||
for (let inner of poly.inner || []) {
|
||||
obj.appendAll(inner.extrude(z));
|
||||
}
|
||||
objs.push(obj);
|
||||
// invert y
|
||||
for (let i=1, l=obj.length; i<l; i += 3) {
|
||||
obj[i] = -obj[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return objs;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -479,12 +479,38 @@ function load_files(files) {
|
|||
mesh.api.log.emit(`loading file...`);
|
||||
let api = mesh.api;
|
||||
let has_image = false;
|
||||
let has_svg = false;
|
||||
for (let file of files) {
|
||||
has_image = has_image || file.type === 'image/png';
|
||||
has_svg = has_svg || file.name.toLowerCase().indexOf(".svg") > 0;
|
||||
}
|
||||
if (has_image) {
|
||||
if (has_svg) {
|
||||
api.modal.dialog({
|
||||
title: `image options`,
|
||||
title: `svg import`,
|
||||
body: [ h.div({ class: "image-import" }, [
|
||||
h.div([
|
||||
h.label("extrude"),
|
||||
h.input({ id: "extrude_height", value: 5, size: 4 })
|
||||
]),
|
||||
h.div([
|
||||
h.label("repair"),
|
||||
h.input({ id: "svg_repair", type: "checkbox", checked: true })
|
||||
]),
|
||||
h.div([
|
||||
h.button({ _: "import", onclick() {
|
||||
let { svg_repair, extrude_height } = api.modal.bound;
|
||||
load_files_opt(files, {
|
||||
soup: svg_repair.checked,
|
||||
depth: extrude_height
|
||||
});
|
||||
api.modal.hide();
|
||||
} }),
|
||||
])
|
||||
]) ]
|
||||
});
|
||||
} else if (has_image) {
|
||||
api.modal.dialog({
|
||||
title: `image import`,
|
||||
body: [ h.div({ class: "image-import" }, [
|
||||
h.div([
|
||||
h.label("invert pixels"),
|
||||
|
|
@ -507,7 +533,7 @@ function load_files(files) {
|
|||
h.input({ id: "img_base", value: 0, size: 4 })
|
||||
]),
|
||||
h.div([
|
||||
h.button({ _: "next", onclick() {
|
||||
h.button({ _: "import", onclick() {
|
||||
let { inv_image, inv_alpha, img_border, img_blur, img_base } = api.modal.bound;
|
||||
load_files_opt(files, {
|
||||
inv_image: inv_image.checked,
|
||||
|
|
|
|||
Loading…
Reference in a new issue