generate: fix PruneRequests() to actually prune requests#1587
generate: fix PruneRequests() to actually prune requests#1587ruevs merged 3 commits intosolvespace:masterfrom
PruneRequests() to actually prune requests#1587Conversation
Commit 86f20cc (merged in solvespace#428) was marked NFC, even though it didn't just convert loops to ranged-for, but also introduced a functional change in the request pruning logic to prune entities instead. This doesn't make much sense, since the requests will stay, and their entities will simply be regenerated, leading to an infinite recursion and resulting in a crash with stack overflow. I'm not sure how it slipped past code review, but this was the cause of solvespace#628 and is the reason for the additional workplane deletion code that whitequark added in 586b047, so this commit also reverts that code as well.
9e94b40 to
9fe43d5
Compare
|
@iscgar I'm still testing things and thinking about this. The first problem is a real problem - pruning requests should actually prune requests, not entities. The entities need to go too, but I'm assuming that was working prior to the bug. The second piece of code that tags and removes entities in a workplane that has been tagged for deletion should stay - I think. If a workplane is going to be deleted, any requests living in that workplane will need to be deleted as well. Most of the time when we sketch in a workplane it is by doing NewGroup->Sketch in New Workplane. Which means deleting the workplane will delete the group, and hence all entities in the group/workplane. However, you can do Sketch in 3D, then create a workplane (just Sketch->Workplane), select that workplane and then Sketch->In Workplane and start drawing. Nothing in this scenario creates a new group, and you get a bunch of entities in a workplane that is just another part of the current group. If that plane is deleted we need to delete all the requests and entities belonging to it. Are we sure this is handled somewhere else? |
Both pieces of code are doing the exact same thing after my changes. In general, all entities are created by requests. Group entities ( During regeneration all of the entities are thrown away and regenerated from the existing requets and groups. So when a request is removed, all of its entities go away automatically and there's no need to prune individual entities. Requests generate all of their entitites on their own, and don't generally share entities, so when a request is removed, in theory we don't need to worry about other requests at all. However, the only exception to that rule is a workplane entity, which is shared by the request that created it and all of the entitities that are sketched in that workplane, and that's exactly why This PR restores the logic of The other piece is now redundant, since it implements the same logic, and was only needed because the changes in 86f20cc broke that logic by pruning entities instead of requests, so that's why I reverted it. |
For extrusions, remap create entities from prior entities. The new ones are not AFICT associated with any request. Also when linking files remap is used to assign new handles to entities in the other file, and these are considered to have the group as their parent. Similarly, when linking STL or IDF files, entities are created using remap from nothing they are not even descended from a request. The remap table allows re-created entities to have the same handle so that subsequent constraints can be preserved. BTW In the future I'd prefer if remapped entities are not deleted and recreated if possible so that we may assign other attribute to them that get preserved ( I'm thinking fillet or chamfer, but right now we can't even assign a different style). I guess I'm arguing to preserve the code that whitequark added. OTOH if you're sure its removing exactly the same entities then the code can be removed. But one function is removing group-specific entities and the other is removing anything that's been tagged from the global sketch. Or is the one being called after tagging by the previous one? |
Right, sorry. What I meant to highlight is that the entities themselves are thrown away and regenerated during In any case, there's code to handle pruning of both group-generated entities and request-generated entities, and pruning the entities themselves is useless because they are always regenerated.
The best way to do that would be to extend the remap table to contain a mini version of request that stores metadata as well. That way, during regeneration the metadata could be attached to the regenerated entities too.
It's indeed removing exactly the same entities. I'm not sure I follow your question though. Neither piece of code is removing group-specific entities. In fact, there's no code to do that, only a workplace can be depended upon by other entities, so when a workplane request is removed, we only need to remove specific requests that depend on it, or an entire group if it depends on it for its workplane (group pruning is done recursively using |
src/generate.cpp
Outdated
| continue; | ||
| } | ||
|
|
||
| if(!e.h.isFromRequest()) { |
There was a problem hiding this comment.
@iscgar Why did you decide to continue instead of:
ssassert(e->h.isFromRequest(), "Only explicitly created entities can be pruned");
like the old code did?
There was a problem hiding this comment.
@ruevs You can't assert since not all entities are from requests. It's searching for ones that are from requests, so we don't want to assert if they are not.
There was a problem hiding this comment.
The original code asserted, and it was right to do that, because the assertion is done only after we established that the current entity's workplane is gone, and the current group should not have group-generated entities whose workplane doesn't exist (such groups are pruned in PruneGroups() which is called on the current group in the loop before regeneration even takes place). However, it is confusing and requires readers of the code to keep track of the order of operations in GenerateAll(), and I don't think enforcing this justifies the added confusion, so that's why I changed it to a continue instead.
|
And while on the topic. I think the change in And the two @rpavlik are we missing something or did we miss it at the time? |
Oh that's bad. |
|
Could this be the cause for #1239? |
The original for loop also returned after removing a single request, so the conversion to
No, the original code behaved that way for |
Unlikely, since that message is shown only in the code path that doesn't prune any objects (i.e. done regenerating), while the combination of 86f20cc and 586b047 doesn't change that. It just caused |
OK I read more carefully (not from the phone) this time and you are right about everything.
|
|
@ruevs So do you want to push the green button? ;-) I'm satisfied. |
As I said, I already have that ready in a private branch. I can just add it to this PR if you prefer to tackle them both here.
As long as things get pruned, we simply recurse into |
…tems ...instead of just the first "prunable" one. The same was done for `PruneRequests` in the previous commit. The change in `PruneConstraints` avoids some of the recursive `GenerateAll` calls. The one caused by `PruneGroups` remains. The change in `PruneOrphans` is a NOP.
@iscgar ooops I committed a change to But yes - let us do it all at once in this PR. |
Reading even more carefully :-) you are right again - all it does is |
No worries, your code is almost exactly what I had in my branch. I added another minor optimisation from my branch, but feel free to drop it if you think that it doesn't belong in this PR. |
For request pruning, iterate over requests rather than over entities, and for constraint pruning look up in the requests list where possible, as it should be much shorter. While at it, combine them into a single operation. This saves us one potential recursion for every group, since there's no reason to wait for the next recursion to do constraint pruning when requests were pruned.
de7fe32 to
d6288d6
Compare
|
Thank you @iscgar . |
Commit 86f20cc (merged in #428) was marked NFC, even though it didn't just convert loops to ranged-for, but also introduced a functional change in the request pruning logic to prune entities instead.
This doesn't make much sense, since the requests will stay, and their entities will simply be regenerated, leading to an infinite recursion and resulting in a crash with stack overflow. I'm not sure how it slipped past code review, but this was the cause of #628 and is the reason for the additional workplane deletion code that whitequark added in 586b047, so this PR also reverts that code as well.