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

Introduce new notion of object ID that survives updates #1904

Open
hannobraun opened this issue Jun 20, 2023 · 4 comments
Open

Introduce new notion of object ID that survives updates #1904

hannobraun opened this issue Jun 20, 2023 · 4 comments
Labels
topic: core Issues relating to core geometry, operations, algorithms type: feature New features and improvements to existing features

Comments

@hannobraun
Copy link
Owner

Objects, the elements that make up shapes in fj-core, are immutable. When an object is updated, a new version of it is created.

Objects are identified by an ID. This ID doesn't survive such an update, as from a technical perspective, the new object is an entirely new thing. This can be highly inconvenient. Check out this code, for example:

let abc = Face::triangle([a, b, c], services);
let bad = Face::triangle([b, a, d], services).update_region(|region| {
region
.update_exterior(|cycle| {
cycle
.join_to(
abc.face.region().exterior(),
0..=0,
0..=0,
services,
)
.insert(services)
})
.insert(services)
});
let dac = Face::triangle([d, a, c], services).update_region(|region| {
region
.update_exterior(|cycle| {
cycle
.join_to(
abc.face.region().exterior(),
1..=1,
2..=2,
services,
)
.join_to(
bad.face.region().exterior(),
0..=0,
1..=1,
services,
)
.insert(services)
})
.insert(services)
});
let cbd = Face::triangle([c, b, d], services).update_region(|region| {
region
.update_exterior(|cycle| {
cycle
.join_to(
abc.face.region().exterior(),
0..=0,
1..=1,
services,
)
.join_to(
bad.face.region().exterior(),
1..=1,
2..=2,
services,
)
.join_to(
dac.face.region().exterior(),
2..=2,
2..=2,
services,
)
.insert(services)
})
.insert(services)
});

This is part of the code that builds a tetrahedron from triangles. Where it joins the triangles together, it needs to do so using indices to refer to HalfEdges within Cycles. This was inconvenient to write, and now it's hard to read. It would be much nicer, if Handles to already existing HalfEdges could be used here instead, but this is not possible: The HalfEdges are being updated, so the Handles lose their meaning in the later steps of that code.

I have an idea on how to solve this: In addition to the ID, have some some additional identifying characteristic that survives object updates. Maybe name it "family" or "ancestry" or something like that. For a completely new object, ID and "family" would be identical. For each object created by updating an existing one, the "family" is kept.

Then APIs like this "join" API could use the "family" instead of the ID to identify an object.

@hannobraun hannobraun added type: feature New features and improvements to existing features topic: core Issues relating to core geometry, operations, algorithms labels Jun 20, 2023
@hannobraun
Copy link
Owner Author

I'm looking into this now.

@hannobraun hannobraun self-assigned this Jul 3, 2023
@hannobraun
Copy link
Owner Author

I didn't update this issue! Sorry!

I haven't been working on this in a while, as I've been sidetracked by other work. I'm un-assigning myself for now, but I expect to pick this up later.


In the meantime I've had an idea for how to specifically use this family/ancestry (I've started to call it "lineage") ID. One way would be to just check the lineage instead of the ID for all comparisons within update operations. While I can't think of a specific reason not to do it, I don't like it. I think this will have unintended consequences sooner or later.

So instead, I have another idea that makes this more explicit: Instead of accepting a bare Handle as an argument of update methods, accept an enum (or rather, an impl Into<this new enum> that can either represent the ID or the lineage. A Handle could convert into the ID variant, preserving the current behavior. Maybe Handle could get a lineage method that returns a type representing the lineage, and that could convert into the lineage variant of the new type.

Not quite sure about all the specifics yet, but this idea has the advantage of preserving the stricter "compare by ID" behavior within update methods, while enabling the looser "compare by lineage" behavior to be enabled explicitly.

@hannobraun hannobraun removed their assignment Sep 25, 2023
@hannobraun
Copy link
Owner Author

Since I last updated this issue, a new concept has emerged: That of "layers", which enrich the object graph with additional attributes, without the object graph having to know about that (thus sectioning the complexity, keeping the object graph light and simple). See #2116 and #2117 for more information on that.

This relates to this issue, as lineage information could be kept in a separate layer, thus enabling the functionality I've described here, without adding more complexity to Handle or the central object storage.

@hannobraun
Copy link
Owner Author

It looks like I almost implemented this accidentally, while I worked on #2117.

There's a derive operation now, which encodes exactly the information that is needed to track lineage. So far, this is only used to update the presentation layer, but it should be fairly trivial now to add a "lineage" layer that tracks this information and provides support for queries like ("do objects A and B share the same lineage").

Then there's #2164, which tracks the introduction of better selector infrastructure. It might be best to wait until that issue is implemented, then take another look at this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: core Issues relating to core geometry, operations, algorithms type: feature New features and improvements to existing features
Projects
None yet
Development

No branches or pull requests

1 participant