jscad / OpenJSCAD.org

JSCAD is an open source set of modular, browser and command line tools for creating parametric 2D and 3D designs with JavaScript code. It provides a quick, precise and reproducible method for generating 3D models, and is especially useful for 3D printing applications.
https://openjscad.xyz/
MIT License
2.58k stars 505 forks source link

feat(modeling): unify expand into offset #1270

Closed platypii closed 11 months ago

platypii commented 11 months ago

This PR attempts to unify expand and offset operations into a single unified offset operation for V3. The expand operation is removed.

I went with offset because that is the common term in the literature around polygon offsetting. It is also consistent with OpenSCAD.

I studied all the cases where expand is different than offset in V2, to make sure existing designs would be possible to port to V3:

Expanding holes

One question is whether holes in a geometry should "expand" or "shrink". The traditional definition of offset would have the holes get smaller when the object is offset. In JSCADv2, expand will increase the size of both "solids" and "holes", while offset does the more traditional offset operation:

offsetHole

If you have JSCADv2 code that looks like:

expand({ delta: 1 }, geometry)
offset({ delta: 1 }, geometry)

To get the same behavior with this PR you would do:

offset({ delta: 1, expandHoles: true }, geometry)
offset({ delta: 1 }, geometry)

Expanding paths

The other special case is the expanding of path2 geometries. In JSCADv2 expand of a path2 will create a solid geom2 representing the "stroke" of the path. In JSCADv2 doing an offset of a path2 will create another path2 which is "offset" to the left or right.

offsetPath

If you have JSCADv2 code that looks like:

const newGeom2 = expand({ delta: 1 }, path)
const newPath2 = offset({ delta: 1 }, path)

To get the same behavior with this PR you would do:

const newGeom2 = offset({ delta: 1 }, path)
const newPath2 = path2.create(offsetFromPoints({ delta: 1 }, path2.toPoints(path)))

This is a little more awkward, but I think the scenario where a user wants to "offset" a path to the left or right is not a common scenario. Exporting offsetFromPoints makes it possible for users to migrate code from V2, while letting us simplify the offset and expand apis. An alternative name for offsetFromPoints could be parallelCurve see wikipedia.

Minor differences

Minor difference offsetGeom2 vs expandGeom2 had different default segments, 0 vs 16. I went with a default of 16.

Tests are technically all the same test cases as before, but renamed and reorganized since some were in expand and moved to offset.

All Submissions:

z3dev commented 11 months ago

@platypii Nice! I was wondering what you were working on...

In general, the V3 API can change. And I don't see anything unreasonable in the the above proposal.

For the path2 expand, I would just remove it. It was kind of a 'hack' which wasn't really appreciated when introduced. If this needs to continue then maybe a new function can be created for 'tracing' paths or geom2 outlines.

Also, I think keeping JSCAD similar to other 2D 'standards' would be wise. I didn't find anything in the SVG world for creating offsets from paths. But Inkscape (the defacto SVG app) has offset. Maybe Autocad has something as well.

https://logosbynick.com/offset-a-path-in-inkscape/

hrgdavor commented 11 months ago

The offset should be more like other cad. The expand for path looks like something useful in context of svg maybe where path has stroke thickness, and that stroke in graphics has some attributes like caps etc.

platypii commented 11 months ago

@z3dev I would prefer to export the offsetFromPoints function, since there are some rare cases it would be hard to do without it. But I am open to suggestions on naming.

Related question: should the sub-package name be changed from expansions to offsets?

import * as jscad from "@jscad/modeling"
const { offset } = jscad.offsets

@hrgdavor if I understand your comment correctly... yes that's what I did. In V3 the offset function generates the stroke path like in an svg, with cap options.

z3dev commented 11 months ago

Related question: should the sub-package name be changed from expansions to offsets?

Sure. I cannot think of any addition offset functionality.

z3dev commented 11 months ago

I would prefer to export the offsetFromPoints function, since there are some rare cases it would be hard to do without it. But I am open to suggestions on naming.

Sounds reasonable but documenting the use case may be difficult.

z3dev commented 11 months ago

@hrgdavor if I understand your comment correctly... yes that's what I did. In V3 the offset function generates the stroke path like in an svg, with cap options.

Actually, I think that @hrgdavor and I are thinking the same. There's a piece of functionality missing for creating 'strokes' along paths or geom2 outlines. But maybe this is another PR.

platypii commented 11 months ago

Sounds reasonable but documenting the use case may be difficult.

I don't think it's hard to find examples where it might be useful to do a "path offset". Suppose you have a path that represents the centerline of a road. If you wanted to compute where the lane markings are, or the start of the sidewalk, you would want to offsetByPoints from the centerline.

platypii commented 11 months ago

I pushed another commit which renames folder expansions to offsets

platypii commented 11 months ago

@z3dev I just pushed a change to require segments > 0. I also added a bunch of tests against the offset options.