TheDuckCow / godot-road-generator

A godot plugin for creating 3D highways and streets.
MIT License
313 stars 16 forks source link

Identify, disconnect, and clean up badly connected RoadPoints #78

Closed TheDuckCow closed 1 year ago

TheDuckCow commented 1 year ago

If we duplicate a roadpoint in the middle of a RoadNetwork, we get a lot of weird overlapping geometry. This is because one roadpoint points to another, but that other one doesn't point bad. See the video showing up this awkwardness of having to manually toggle refresh (there are a number of scenarios where this becomes necessary):

https://user-images.githubusercontent.com/2958461/226811922-847c676d-a39c-4dfd-952c-41bfd9afb367.mp4

Ideally, the RoadNetwork can be an authority to determine an "illegally connected" RoadPoint, and auto clear up any bad pointer references - and just as critically, clean up any of that invalid geometry left around so that we don't have to manually refresh the network auto update, which is a bit of a pain.

TheDuckCow commented 1 year ago

Some aspect of this was actually resolved in the recent release, though more could be done.

bdog2112 commented 1 year ago

Questions:

In the given example, the user should have used the "+Next/Prev RoadPoint" buttons to add the RoadPoint rather than duplicating. In fact, I would go so far as to say users should never duplicate RoadPoints because it inherently creates an invalid junction.

Nevertheless, we could enforce certain rules. For instance, the given example showed two RoadPoints using the same third RoadPoint as their end point. We could say this is not allowed and that a RoadPoint can only be used as the end point for one RoadPoint. But, how do we enforce this and when do we check for the condition?

To enforce this, we could validate whenever the user changes the end point. We would iterate the entire network of RoadPoints and make sure that RoadPoint was only used once. If it was already used, then we would revert the user's change and set the end point to null.

In the case where the user duplicates a RoadPoint, there's no way of knowing when this has happened. We could always run validation when a RoadPoint is selected.

Alternatively, we could implement a tool button that says "Validate RoadPoint" and/or "Validate RoadNetwork". It follows that the validation routine would verify that a given RoadPoint is used no more than once as a start or end point.

If duplication was detected, then we would need to provide options for remediation. These might include aribitrarily keeping the first start/end point assignment and clearing all of the others or, perhaps, just printing a list in the console and letting the user do the cleanup.

Another approach would be to display a modal popup where the user selects the correct RoadPoint from a list of connected RoadPoints. Then, we would automatically disconnect all other RoadPoints from the start/end point. There would be a bit of a learning curve as we figure out how to do this. But, it would probably be the most convenient and intuitive solution for users.

TheDuckCow commented 1 year ago

Thanks for the detailed questioning and thinking through. I think the core original topic in question was brought up indeed around duplication of nodes, but then I generalized the title to be about really any situation with a bad situation. You outline the two main scenarios:

1) Duplication of a RoadPoint 2) Otherwise creating bad connections (where as you say, a roadpoint gets used more than once)

What is the issue? Is it the fact that the user was allowed to create an invalid setup?

Yes, we want to prevent invalid setups where two road segments are coming out of each other. It creates weird geometry that will never be desirable. We should have cleanup code the prevents this from persisting, so that other features like intersections can depend on the validity of the network (where roadpoints only connect to max 2 other roadpoints).

When do we do validation? a) When RoadPoint selected, b) When start/end point changed, c) When a new (to be created) "Validate RoadPoint" button is pressed, d) Other: (explain here)

Think more downstream: at the time mesh geometry updates are happening, when we're about to potentially create new RoadSegmetns. This should really be a pre-step which corrects any bad connections. This way, this code gets triggered whenever a transform / updated connection is done (whether on a single node, or if the whole network is updated), no different to how it's already running. So it shouldn't be a new lifecycle hook.

Do we arbitrarily correct a user's changes based on our assumptions or simply tell them what's wrong and let them make the corrections? (i.e. Which RoadPoint do we alter when making corrections? How do we know which is the right one? Does only the user know?)

The modal popup is an interesting idea. While indeed it gives the most control, most users would not expect a popup right after duplicating a node. Plus, whenever an invalid situation occurs, there will be a single action or moment that makes it invalid. So, it should be self-resolved at that point.

To give direction: How do we do this, "get the last action"?

Thinking about this more, we shouldn't even need to. We have bidirectional linked lists essentially right now. If we have an invalid setup, we could check whether the bidirectional point connections agree or not - and for the one which disagrees, disconnect what it is falsely claims being connected to.

Maybe I'm oversimplifying, but it seems to me like this could be a solid approach. Thinking it through:

Scenario A: The duplicate scenario

Scenario B: A manually created invalid setup by changing pt inits only.

Some additional thoughts on this approach:

Let me know if you think I'm missing any considerations @bdog2112. Of course once we implement this, any scenes saved to disk may be updated immediately on next regeneration, but I'd argue that's working as intended as it sanitizes the network (maybe that's even what we call this function.