Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(modeling): input array structure #1299

Merged
merged 7 commits into from
Dec 21, 2023
Merged

Conversation

platypii
Copy link
Contributor

@platypii platypii commented Sep 19, 2023

This PR contains my suggestions for JSCAD v3 handling of input arrays. Some of these are breaking changes from V2. I tried to follow a couple principles:

  1. Avoid throws unless the operation would be clearly invalid
  2. Preserve input structure when possible, don't flatten unless we need to. This is nice for grouping objects, and also preserves information when composing operations.
  3. Accept nested arrays anywhere a single geometry is accepted. So anywhere a user could pass a geometry, should also accept nested arrays of geometries.
  4. Make choices that give users more control. If there's two choices for how we define an API, but one way gives users more flexibility, then do that. See align below.

Before this PR:

returns Input arrays Mix 2d 3d Empty input null/undefined Invalid geom
Booleans, extrusions, hulls, offsets Geometry flatten throw [] filtered throw*
modifiers Geometry[] flatten apply to each [] throw throw*
transforms Geometry[] flatten apply to each return input copy to output throw*

*throws for obviously incorrect geometries, but will incorrectly flatten and return some things like [[1,2],[3,4]]

After this PR:

returns Input arrays Mix 2d 3d Empty input null/undefined Invalid geom
Booleans, extrusions, hulls, offsets Geometry flatten throw [] filtered throw
modifiers RecursiveArray Preserve input structure apply to each return input copy to output throw
transforms RecursiveArray Preserve input structure apply to each return input copy to output throw

Nested Arrays as Groups

I suggest that we preserve nested arrays where it makes sense to do so. This information can be treated like “groups” in svg and other file formats. For operations which return a single geometry, like the booleans, we should still flatten before we apply the operation, but if it makes sense to do so, I propose that we preserve the recursive structure as much as possible.

null/undefined

I suggest that we filter out null-ish values when we do a flatten operation for booleans, and most operations. To make working with nested arrays easier, I added a new util function coalesce which combines flatten and filtering out nullish values.

export function coalesce<T>(arr: RecursiveArray<T>): Array<T>

Empty arrays

What if a user calls a boolean operation with an empty array, or an array which is empty when flattened? We have three options 1) throw error, but I would prefer not to do this 2) return geom2.create() or geom3.create(), but the problem is that we don’t actually know if it’s intended to be 2D or 3D. I propose that in this case we return undefined, which can be filtered out by other operations using coalesce.

Mix 2D/3D

If you call a boolean between a 2D and 3D geometry, it’s not clear what to return in that case, so we should throw if not all types are the same. However for transforms, and modifiers, it’s okay to mix 2D and 3D. I propose that we apply functions where it makes sense. If an object is nullish, or not a geometry, I suggest we simply map the object to itself and return it in the output. This makes the operation robust against unexpected inputs.

Alignment

One other suggestion is that I believe that align and center operations should respect groupings, and use them when aligning objects rather than flattening. See suggestion from @hjfreyer in #1249. The reason I like this change is that it gives more flexibility to users. In V2, inputs will be flattened regardless of the user's intent. Whereas with this change, users can align groups of objects. The previous behavior can be accessed by setting options.grouped = true.

Examples

const g3 = h3 = cube()
const g2 = h2 = square()

generalize({}, g3) => out3
generalize({}, g2, g3) => [out2, out3]
generalize({}, [[g2], g3]) => [[out2], out3]
generalize({}, []) => []

offset({}, g3) => out3
offset({}, g2, g3) => [out2, out3]
offset({}, [[g2], g3]) => [[out2], out3]
offset({}, []) => []

union(g3) => g3
union(g2) => g2
union(g2, null) => g2
union(g2, undefined) => g2
union([g2, undefined, null]) => g2
union(g3, h3) => out3
union(g2, h2) => out2
union([[g2], h2]) => out2
union(g2, g3) => throw “union arguments must be the same geometry type”
union([]) => undefined

scission(g3, h3) => [[g3a, g3b], h3]

All Submissions:

  • Have you followed the guidelines in our Contributing document?
  • Have you checked to ensure there aren't other open Pull Requests for the same update/change?
  • Does your submission pass tests?

1. Avoid throws unless the operation would be clearly invalid

2. Preserve input structure when possible, don't flatten unless we need to

3. Accept nested arrays anywhere a geometry is accepted
Don't re-call flatten from internal helper functions.
@z3dev
Copy link
Member

z3dev commented Sep 28, 2023

@platypii sorry for the lack of a review. hopefully, i can play with the new style this weekend.

@platypii
Copy link
Contributor Author

Just to add a bit more justification for this PR:

One of my goals is to improve things like SVG serialize/deserialize. I want to be able to convert SVG groups into nested sub-arrays of geometries. And then it would be really nice if information was not lost along the way. So for example: 1) Load geometry from SVG, 2) Apply transform, or extrude, or offset, or align, etc to the nested geometry, 3) Re-export to SVG while still preserving the nested group structure.

Currently that information is lost when any operation is applied to a design because arrays get flattened. This PR attempts to preserve that structure where possible.

Copy link
Member

@z3dev z3dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, the changes look good. Maintaining the hierarchy of an array looks like a nice improvement for the transforms. But I had a few questions on the hulls and the boolean functions, which combine geometries. Maybe some additional tests would help proof the changes.

I also had a few small comments.

@z3dev
Copy link
Member

z3dev commented Dec 4, 2023

@platypii Can you finish these changes? It would be nice to get these into V3.

@platypii
Copy link
Contributor Author

platypii commented Dec 4, 2023

@z3dev updated PR based on feedback, and added a few more tests to try and make things clearer. Thanks!

@platypii platypii requested a review from z3dev December 4, 2023 20:08
Copy link
Member

@z3dev z3dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes to handle arrays recursively was BIG.

Some small requests to change comments. And one question...

packages/modeling/src/operations/booleans/intersect.js Outdated Show resolved Hide resolved
packages/modeling/src/operations/booleans/subtract.js Outdated Show resolved Hide resolved
packages/modeling/src/operations/booleans/subtractGeom2.js Outdated Show resolved Hide resolved
packages/modeling/src/operations/booleans/unionGeom2.js Outdated Show resolved Hide resolved
packages/modeling/src/operations/booleans/unionGeom3.js Outdated Show resolved Hide resolved
packages/modeling/src/operations/hulls/hullGeom3.js Outdated Show resolved Hide resolved
Move up empty geometry checks

Use Geom3[] instead of ...Geom3
@z3dev z3dev self-requested a review December 21, 2023 00:33
Copy link
Member

@z3dev z3dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@platypii thanks again.

sorry that i haven't been active lately... it's part of the busy season.

@z3dev z3dev merged commit b8d8fb8 into jscad:V3 Dec 21, 2023
2 checks passed
@platypii platypii deleted the V3-input-arrays branch February 17, 2024 16:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants