TheDuckCow / godot-road-generator

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

System for auto placement and snapping #118

Closed TheDuckCow closed 3 months ago

TheDuckCow commented 1 year ago

One of the final steps to make an intersection system work is to be able to move segments around, and have auto placements work.

The problem:

We now have a mixture of auto generated road segments between RoadPoints, as well as prefab scenes which could be either real intersections, or even non-intersection road components / pieces / chunks with custom mesh geometry. If a user is placing only custom hand-made elements into the scene, they need to be able to "snap" them together with ease.

The solution:

Two pronged:

Open questions:

bdog2112 commented 7 months ago

Initially, it looked like this issue might require significant effort because we wanted to give users the ability to add RoadPoints to a 3D model of an Intersection. Then, hook up roads to it.

Well, we can absolutely pour additional effort into this. But, I have identified a novel workaround that might just allow us to move forward with what we already have in place.

So, we're talking about creating Intersection template scenes consisting of a 3D model and, maybe, 2-4 RoadPoints positioned where we want to make external connections. Unfortunately, as you know, that doesn't work.

The existing connection tool (aka the RoadPoint's "Add Mode") doesn't allow us to connect to loose RoadPoints that aren't connected to anything. HOWEVER, IT DOES ALLOW US TO CONNECT TO ROADPOINTS THAT HAVE VALID CONNECTIONS! Thus, a simple workaround is to add a 2x2 Road for each connection you desire in your template scene.

So, in the example below, we see a road connecting to a 3-way Intersection. Each connection point, essentially, consists of a 2x2 Road rotated and positioned to where we want to make the connection. There is a good chance that this could facilitate many/most use cases. scene1 scene2 scene3

It's also worth noting that a typical intersection might have a crosswalk. Hence, the small connective Road sections would provide a great place to utilize a crosswalk material.

In conclusion: There is a simple workaround that may facilitate our current needs and it also provides a great place to utilize materials for crosswalks. Please let me know if you still wish to implement additional functionality.

bdog2112 commented 7 months ago

Hey @TheDuckCow, I proposed a simple Intersection template solution, above. But, going back through your requirements, it sounds like you really want more than just the mere ability to facilitate templates.

The "auto-connection" functionality is the main request. So, I will continue working on that.

Having explored the aforementioned workaround, I found that the connection tool utilizes an extraneous RoadPoint for each connection between two RoadContainers. Thus, the extraneous RoadPoints were not physically connected to the destination RoadContainers.

If the destination RoadContainer was moved, then the extraneous RoadPoint was left behind and the connection didn't follow. This could be problematic for someone who is auto-connecting things. Still contemplating how it might be resolved...

UPDATE: Upon further exploration, determined that the "Refresh Roads" menu option caused the disjoint sibling RoadPoints to sync up.

bdog2112 commented 7 months ago

For initial placement, if you have one element selected such as a RoadPoint or another intersection point and from the create menu, you add another, it will simply connect it to a free/random slot with a random orientation.

So, that's how I'm approaching this portion of the request.

Update: You know, it might be useful to simply place the new RoadPoint in the position and rotation of the selected item since Godot doesn't have a handy "cursor" like Blender to help you do that. At any rate, for the moment, I'm sticking to the original plan.

bdog2112 commented 7 months ago

I'm looking at using edge_rp_locals in nested scenes to identify RoadPoints available for connections. This will be problematic for multiple reasons:

By giving RoadPoint a "Terminated" property, we can evaluate it on edge RoadPoints to see if we can use them. Users would need to set a RoadPoint as Terminated if they didn't want it to be used for connections.

In order to tell if a nested RoadPoint has a sibling in the main scene, 2 options come to mind.

  1. If main scene RoadPoint has the same exact position as the nested scene RoadPoint, then assume they are siblings.
  2. Add a "Sibling" property to RoadPoint. It would contain a NodePath pointing to the sibling RoadPoint. This property would only be applied to RoadPoints in the main scene. Not the nested scene.
bdog2112 commented 7 months ago
  • For initial placement, if you have one element selected such as a RoadPoint or another intersection point

Hey @TheDuckCow, what is meant by "intersection point"?

In theory, "intersections" are "nested scenes". Hence, one can select a nested scene. But, they cannot directly select the RoadPoints within them. That is, unless they are directly editing the nested scene and at that point, the selection is just a regular RoadPoint.

What is an "intersection point" and what do we want to do with it?

TheDuckCow commented 7 months ago

Hey @bdog2112 sorry been recovering, still getting there. But seems like you're largely on the right path with what I had in mind. But fair questions have been raised. Couple comments:

That being said, your other comments are still relevant for how we consider intersections in the short term:

So, there's a few ways we could still take this. Some more work now, others more work or rework later. Open to your thoughts. I have a feeling the RoadContainer extended into RoadIntersection might be the fastest though, and conceptually help user separate components with "edge road points" being the intermediates.

In theory, "intersections" are "nested scenes". Hence, one can select a nested scene. But, they cannot directly select the RoadPoints within them. That is, unless they are directly editing the nested scene and at that point, the selection is just a regular RoadPoint.

Correct, so they should be treated as static from the perspective of outsiders (until we get to some magical procedural point in the further future, not now though).

bdog2112 commented 7 months ago

Your vision of a more robust solution sounds great! Based on your last response, my sense is that you wish to have RoadIntersection inherit from RoadContainer. Then, use RoadIntersection to house nested scenes.

I already tested the scenario where RoadContainer housed the nested scene. Also, each 'intersection point' (to coin a phrase ;-) was part of a RoadSegment, which was made up of two RoadPoints. Each RoadSegment had one RoadPoint that was marked as Terminated. (It was basically the 3-way intersection depicted in images earlier in this thread.)

The RoadContainer's "Edge RoadPoint" logic was able to be harnessed to get the edges from the nested scene and the "Terminated" property was harnessed to filter out the unwanted Edge RoadPoints. Some additional logic was needed to target external RoadContainers and their RoadPoints. At any rate, it was then possible to select a nested scene and add a connection without targeting a specific RoadPoint inside or outside of the nested scene.

But, based on your response, I will:

I also recognize that "snapping" is a key goal of this effort. Although, the RoadIntersection update is a foundational element before implementing snapping. But, the end goal will be to implement snapping as described in the original post.

As an aside, keep in mind that the existing "connection tool" worked fine with the nested RoadContainer approach when RoadSegments (paired RoadPoints) were used. But, it will need to be updated if we're using standalone RoadPoints in the nested scenes.

bdog2112 commented 7 months ago

Oh, and hope you feel better soon. :-)

TheDuckCow commented 7 months ago

Hey @bdog2112 here's a little video where I thought through this some more. I got stuck while trying to sleep thinking about this last night. Curious to hear your thoughts!

https://youtu.be/3ORrU4YYl7g

So to summarize, there's these options:

  1. Have RoadIntersection extend RoadContainer (might not be very intuitive for users, but we get all the "edge" computation for free), and then child nodes who point to the intersection via prior/next pt path are treated as directly part of said intersection
    • Has the niceness for forcing breaking up of road containers. I always liked the analogy of naming a road container after the name of the street itself; an intersection could conceivably be named too, not that it really matters.
    • But that also means we have a lot of the export var settings of containers that won't necessarily make sense for road intersections
  2. Make RoadIntersections be their own class not extending anything (as it is right now), and let RoadPoints connect to them; one or more intersections can still be contained inside a RoadContainer, and that way roadContainers are still the only thing that "contain" other things.
    • This way, similar to how one RoadPoint (typically) contains one mesh child, one RoadIntersection also contains one mesh child, which is the logic to procedurally create, or the manually instanced, intersection mesh.
    • My intuition is that this will feel more natural to users. I'm also thinking ahead to when we'd want to draw a new road into an existing roadpoint to convert it into a roadIntersection; it would just be changing the one roadpoint's type to be intersection, but keeping the same hierarchy and position
  3. At the end of the video I threw this idea up, but not sure I like it: similar to option 2, but make the "connecting" intersection roadpoints children directly. I don't like this though as someone dynamically creating intersections will thus require reparenting which gets messy. So let's forget about that option

I'm a bit torn between 1 and 2; 1 is more strict and also means meshes being placed at inconsistent levels (mixing the concept of containers and intersections, while 2 feels more consistent with the hierarchy structure we have today, but doesn't force users to break up their very long roads with separate road containers (which will be nice, since I eventually want to have bulk tools like add/subtract lanes for all roadpoints in a container; would be nice if the roadpoints between two road containers were forced to be within at least one road container, so you could easily do operations over that entire container).

Given that, I'm still leaning towards approach 1, but keen to hear your thoughts.

Sorry for the back and forth here, I keep thinking it's settled in my mind but then think some more on it. Best is to choose a direction in the end and just mitigate the downsides.

bdog2112 commented 7 months ago

Both solutions are attractive. It really comes down to preference and time.

Option2 opens up a can of worms due to greater uncertainty. But, it could still totally work. On the other hand, Option1 is right there with a spotlight shining down on it saying, "Pick me, pick me." :-) So, it's a tough decision.

bdog2112 commented 7 months ago

Intersections have multiple inputs and outputs, which means, if they're inline, then they do away with the simplicity of always having only 2 end points on a section of road. That could be a good starting point for deciding the fate of Option2.

bdog2112 commented 7 months ago

Thinking about this some more: There is nothing preventing users from creating a single RoadContainer with multiple roads. So, we don't necessarily have the benefit of 2 end points per RoadContainer after all.

My so called "3-Way Intersection" nested scene effectively had 3 separate roads within a single RoadContainer. Existing logic was able to detect the edges of each separate road. Although, I'm not sure if it knew which edges shared the same sections of road.

It's important to weigh all the needs and goals in order to help in the decision making process.

bdog2112 commented 7 months ago

Here's an approach: Start with a simple use case and use that to weigh your options:

Do we have the functionality we need? Is anything missing? Does this necessitate Option1 or Option2?

If you want to layer in more complexity, then add an overpass.

A good use case can help to clear up what is needed in order to move forward.

TheDuckCow commented 7 months ago

(hang tight, did an exploration, uploading a video demo going through it)

TheDuckCow commented 7 months ago

Hey @bdog2112 hopefully we have our synthesis now:

Here's an approach: Start with a simple use case and use that to weigh your options:

A fair exercise. Check out the results - excuse my coughing, and I tried to cut out the rambling parts and where I was running into some odd bugs that were throwing me off.

https://youtu.be/AsfCVe9NGds

Thinking about this some more: There is nothing preventing users from creating a single RoadContainer with multiple roads. So, we don't necessarily have the benefit of 2 end points per RoadContainer after all.

Correct, the original thinking is that current day RoadContainers are so close to what we need since nothing stops you from having an arbitrary number of open connections. The main need is for something to connect the "intersection" side of those roadpoints two, so that only the other outwards-facing side appears as open connections.

We don't have to do the whole long term scope right now. I'm going to say, let's go with option 2, I don't think it's necessarily that much extra effort. And this way, each of the classes continue to stand on their own and are intuitive. So I'd propose:

bdog2112 commented 7 months ago

SUMMARY: Just putting this out there: The term "Intersection" could get confusing if we treat a RoadContainer Scene as an Intersection and we also have an "Intersection" object.

We need to establish some intuitive and consistent terminology. Maybe we could call the Intersection object a "Terminator".

Based on the video, it appears that we will use a RoadContainer to house the group of objects that could collectively be called an "Intersection".

So, if I understand your written post, we will use the actual Intersection object as kind of a terminator for RoadPoints. But, overall an "Intersection" is more of a scene where we combine a RoadContainer, RoadPoints, meshes, and a literal Intersection object.

Observation: Intersection RoadPoints stored in a pre-fab scene will only be able to refer to the Intersection object. They won't be able to refer to RoadPoints outside of the scene.

Another observation is that the actual Intersection object doesn't need to handle snapping while dragging because it sits side by side in a RoadContainer with a bunch of RoadPoints.

One of my impressions from earlier in this thread is that the Intersection class will handle the snapping and other duties. But, again, it seems like that will fall to the RoadContainer. Although, it may just fall to the plugin and its "mode" logic, which should perform snapping on the desired objects when in the desired mode.

Regarding your question about lane checks when snapping, I suppose some validation is in order. The number of lanes should auto adjust in order to accommodate the source and destination and it's possible there could be unacceptable combinations that would have to be prevented.

I'm not sure I understood what was said about the Connection Tool adding extra RoadPoints. Yes, it adds extraneous RoadPoints when connecting between RoadContainers, which seems to make sense. But, as far as I know, it doesn't add them when connecting RoadPoints within the same RoadContainer, which is also what I'd expect. So, I think it's working properly.

QUESTIONS:

That's all I've got for now.

TheDuckCow commented 7 months ago

Whew, this was a wordy day! Here we go:

It's a good point to keep consistent terminology, a very fair callout. This is how I see it, but let me know if you disagree:

But, overall an "Intersection" is more of a scene where we combine a RoadContainer, RoadPoints, meshes, and a literal Intersection object.

Yes, but I'd more reframe it that any reusable scene is a container that as a whole block can be connected to other containers, like large lego base plates. Just to reiterate that it's not specific to containers which house intersections, that's just one example use case.

Observation: Intersection RoadPoints stored in a pre-fab scene will only be able to refer to the Intersection object. They won't be able to refer to RoadPoints outside of the scene.

Correct, though we have utilities for that to reference along a chain by going up to the road container, checking its edge vars/connected edges, and going back into others. So you're always able to navigate along any path via linked points/intersections

One of my impressions from earlier in this thread is that the Intersection class will handle the snapping and other duties. But, again, it seems like that will fall to the RoadContainer. Although, it may just fall to the plugin and its "mode" logic, which should perform snapping on the desired objects when in the desired mode.

Sorry for that confusion. Agreed about the takeaway, that's also why originally I was thinking this snapping activity as being somewhat independent of the intersection conversation. In the end though, it was important that we aligned on RoadIntersections being full contained inside RoadContainers as that's what allows this to be true.

This snapping functionality will be useful for any situation someone wants to reuse a prefab chunk multiple times. To me, probably makes sense to have a Container.snap_to_container() func which gets called by the plugin when it detects the container just being moved or stopped moving, and takes an input of another container and probably iterates over the number of (open) edge roadpoints in each to see if any edges are within the snapping distance. The smallest distance for snapping is the one that takes over.

The number of lanes should auto adjust in order to accommodate the source and destination and it's possible there could be unacceptable combinations that would have to be prevented.

I'm thinking for now the snapping shouldn't change anything, since these could be prefab scenes where custom geo is used. So, it should just fail to snap/be skipped as if the given edge roadpoint isn't there, if a given pair doesn't have a matching "signature" (will need to take care that the orientations can be flipped, so really there's two directions to check). In the future, maybe we introduce an option to make it so we can dynamically update the lane counts if it's a procedural end, but let's worry about that sometime later.

But, as far as I know, it doesn't add them when connecting RoadPoints within the same RoadContainer, which is also what I'd expect. So, I think it's working properly.

Yup agreed - safe to ignore the connection tool part as for now, snapping is just an operation which will happen automatically after a road container has been transformed (and during transform, to keep pre-existing snapped things still snapped to it)

Should we call the "Intersection" object something else such as "Terminator"?

........ I'm revising my response while typing this out. Maybe you're right, maybe renaming the RoadIntersection class to RoadEnd could work...? (I like the more succinct name). But then in practice it might still seem weird to users they are attaching multiple road points to a single RoadEnd. I could be convinced either way.

Should we use "RoadContainer Scenes" as our de facto "Intersections"?

We'll end up having saved road container scenes which are both straight away sections (with some special hand crafted geometry placement perhaps), and also for segments which contain intersections. Maybe if anything, we call those Contained Intersections, how about that?

By the way, I keep calling it RoadIntersection so that every node in our plugin has the prefix "road", much easier for searching in the node add menu.

Should we re-consider having the Intersection object inherit from RoadContainer and just use that in place of RoadContainers?

I'd vote no. I like the parallel of having geo attached to the RoadIntersection in the same way that roadsegment geo is a child (and thus inheriting transforms etc) of a RoadPoint. And this way, we don't confuse or overlap RoadContainer and leave them be general purpose.

Do we ever need to have multiple Intersections in a single RoadContainer?

Per above, I think there's nothing that we could do to prevent this. It probably wouldn't make too much sense to do so though, but in my mind it doesn't hurt anything by having this flexibility. Unless you think otherwise.

Do we explicitly need snapping to be performed by the Intersection object or is it enough to say that snapping should work similarly to the "connection tool"?

Nope, in my mind the snapping is really a function performed/triggered by the "editor" essentially to correct the placement and connections for a moved/added RoadContainer (whether it contains intersections or not). Per above, the actual function imo makes sense to be a function on roadcontainer, but probably needs some kind of caller in plugin.gd so we can properly do/undo it as a result of a transform move (might be tricky, but let's take it one step at a time, we can think about the undo steps later).

Hopefully that clears things up at least enough to focus on just the snapping part on its own! We can do the intersection/road end outside of this (ie #118) task.

bdog2112 commented 7 months ago

Hopefully that clears things up at least enough to focus on just the snapping part on its own! We can do the intersection/road end outside of this (ie #118) task.

"Intersections" are a critical component needed for auto-connect and snapping to work.

I've worked around this by adding a "Terminator" property to the RoadPoint. However, if we're going to use the Intersection as the terminator, then we have to implement that as part of #118.

TheDuckCow commented 7 months ago

I worry we are not quite on the same page then.

Containers can only connect to other containers through the 5 parallel export var node path arrays. The snapping should thus just result in a transform on the road container in question + a change to both rose containers export var references (using the already defined "connect container" functions in the road container class). Not sure why a "road end" flag is relevant here, unless you just mean for filtering out available road points for snapping which are to be treated as the intersection - (in which case if that's true, fine if that approach unblocks you for now, but it would be rendered unnecessary once we start using the actual RoadIntersection or RoadEnd class or however we name it, since those won't be derived from Road point)

Let me know if that clears things up, or if you'd like me to do some rough speduo code of what I'm thinking this task will look like.

TheDuckCow commented 7 months ago

Ok maybe this extra tidbit is the actual source of confusion here: I'm suggesting we just focus on RoadContainer snapping only first. So that means one entire road container snapping to another entire road container. I wasn't thinking we'd be creating snapping for all kinds of elements such as RoadPoints, since typically the overlapping of RoadPoints should only happen during connections between road containers. Maybe that is the source of confusion? And hopefully also reduces the complexity here. Because our "intersection/end" spatials should never actually be the edge RPs exposed at the barrier of a RoadContainer.

bdog2112 commented 7 months ago
  • For initial placement, if you have one element selected such as a RoadPoint or another intersection point and from the create menu, you add another, it will simply connect it to a free/random slot with a random orientation. Might be wrong, but let them live with it for the quick ease of placement!

  • Then to adjust or move or reconnected, use this analogy. We could do this using none other that LoZ: Tears of the Kingdom as a reference. When moving a prefab piece around, have it show dotted lines (similar to our proposed connection tool) indicating what will be connected after placement. Essentially, two RoadPoints (either from nested or current scenes) should show indicators that they will snap together if they are close enough.

There are two high level tasks: 1 Add RoadPoint from Selected. 2 Snapping with Preview.

Add RoadPoint from Selected requires some RoadPoints in RoadContainers and Scenes to be terminated in order to work properly. To put this another way: if a RoadPoint is not terminated, then it will be considered a valid Edge and available for connection.

Snapping will most likely also require RoadPoints to be terminated. But, it shouldn't require extraneous RoadPoints to be created in the manner of the "connection tool" because the RoadPoints will already exist.

RoadIntersections may require some work in order to fulfill these requests. But, there are probably also nice to have features that are still out of scope. Hence, it seems fair to say that RoadIntersections should be in-scope with the caveat that we don't need to fully flesh them out.

TheDuckCow commented 7 months ago

Thanks for reminding me of my own description at the top.

Add RoadPoint from Selected requires some RoadPoints in RoadContainers and Scenes to be terminated in order to work properly. To put this another way: if a RoadPoint is not terminated, then it will be considered a valid Edge and available for connection.

I'm wondering how we'll really be able to do the first bullet point (adding roadpoint from selected), since we also intentionally can't select points within a roadcontainer (without making instance children into editable anyways).

I might suggest we focus on just the snapping on movement part for now given that. If you want to create a terminator property for now, I'm ok with that, but just to be aware I'm mostly expecting to revert that part as this terminating problem won't be there once we have an actual intersection object for roadpoints to point their next/prior point to (because then those roadpoints won't appear to be edges, as they are fully connected; and roadintersections won't be returned by the container's edge list by design).

Snapping will most likely also require RoadPoints to be terminated.

Snapping should only be relevant for edge RoadPoints that get identified by the RoadContainer.update_edges function and thus are exposed through the export var. If intersections for some reason interfere with this, if anything, I'd say it should be a follow on task to change the update_edges to make sure they are skipped.

In the example template you have now where you're trying to "simulate" an intersection by having a few RoadPoints, yes, the interior ones will appear to be open edges since we haven't done the RoadIntersection work yet. I'm ok with that. But, if you really want to add the terminator property as a short term stopgap, Im ok with it.

TheDuckCow commented 7 months ago

Thanks for reminding me of my own description at the top.

Reading what I wrote again, it sounded very passive aggressive, not what I meant! I more meant that I had somewhat forgotten/lost track what I originally wrote at the top! So the confusion has been entirely fair

bdog2112 commented 7 months ago

TLDR: I will cease working on bullet 1 and focus on bullet 2. The overlap between them should still net some gains if we do 2 first. Personally, I'm excited about some aspects of number 2 anyway.

Read on if you want more detes.

I'm wondering how we'll really be able to do the first bullet point (adding roadpoint from selected), since we also intentionally can't select points within a roadcontainer (without making instance children into editable anyways).

I don't see a problem with completing the first bullet. Adding from a selected RoadPoint is like selecting a RoadPoint and clicking +Next RoadPoint on the Inspector Panel. Easy peasy.

Adding to a RoadContainer or Nested Scene is very much like using the Connection Tool with additional steps. User selects RoadContainer/Scene and clicks Roads>RoadPoint. Program automatically picks random Edge RoadPoint in the RoadContainer or Nested Scene. Then, creates an extraneous RoadPoint at that same position outside the RoadContainer or Scene. Finally, it extends that RoadPoint just like you would using the Inspector panel. This is practically the reverse of using the Connection Tool to connect an outside RoadPoint to a RoadContainer or Nested Scene.

I might suggest we focus on just the snapping on movement part for now given that. If you want to create a terminator property for now, I'm ok with that, but just to be aware I'm mostly expecting to revert that part as this terminating problem won't be there once we have an actual intersection object for roadpoints to point their next/prior point to (because then those roadpoints won't appear to be edges, as they are fully connected; and roadintersections won't be returned by the container's edge list by design).

Bullet 1 and bullet 2 have some overlap. Bullet 1 was simply listed first and it was easier. Hence, I started there. But, I'm happy to skip to bullet 2. Hopefully, we'll gain something we can use to complete bullet 1.

So far, the quickest way to achieve success with these connections to RoadContainers and Nested Scenes is to simply use complete RoadSegments (2 connected RoadPoints) in the Nested Scene and then flag one RoadPoint with a "Terminated" property so you can ignore it in the list of Edges.

I was reassessing everything, in order to figure out best how to incorporate the "RoadIntersection" object in the manner you demonstrated in your videos.

I see the long-term vision in using the RoadIntersection. It's just a matter of getting it to work in conjunction with all the RoadPoint validation logic and the road Edge identification logic and whatever else needs to be updated.

At any rate, at your behest, I will cease working on bullet 1 and focus on bullet 2. The overlap between them should still net some gains if we do 2 first. Personally, I'm excited about some aspects of number 2 anyway.

TheDuckCow commented 7 months ago

Ok great I think we're really aligned now then! Once we get through point 2, I'm sure we can sync and see what you have in mind for how point 1 can work.

Program automatically picks random Edge RoadPoint in the RoadContainer or Nested Scene.

One of the things I'm curious about, and something I see as pretty central to having this system work well, is giving the user some way to "pick" their pre-saved scenes. Either by listing them in a sub menu or in some kind of browser. We'd have to filter for only scenes where a RoadContainer is the root. I'm guessing there's a way, but I think it'd be a whole can of worms. Maybe you already have ideas there, would love to hear them. Knowing this problem is part of why I also was thinking point 1 was less useful to do first since we'd still need a way to actually list the scenes to add via the dropdown menu.

Anyhow, totally happy for you to carry on at this point - we can sync more on Sunday as needed!

btw if you want something fun to test, the open PR here https://github.com/TheDuckCow/godot-road-generator/pull/162 is getting there, aside from annoying Inspector width wiggle on toggling shift. More pointing out as the idea of doing different things based on modifiers pressed I think will come in handy. Likely out of scope of the first iteration of this placement/snapping system, but I could see some follow on work like holding or pressing some key while moving a scene to "rip" it away (severing connections), as opposed to to moving connected things with it (ie what you're working on now). Only food for thought

bdog2112 commented 7 months ago

We've had some brief discussion on differences between line and connector scaling on different monitors. I did a little research and here's a code example for establishing a consistent scale across different screen resolutions.

Notice the use of "OS.get_screen_size()" and the creation of a scale factor that gets applied to the dimensions of each of the drawing elements.

A 1920x1080 resolution was used to evaluate the initial size of the screen elements. Hence, that is the resolution used to set the scaling factor. When the screen resolution is 1920x1080, it results in a scale factor of 1. But, if the resolution is increased to 3840x2160, then it results in a scale factor of 2.

Assuming the physical dimensions of the display remain constant, the screen elements appear to be the same size at both resolutions.

It will take a little time to implement and test. Hence, I will forego doing that unless you expressly request it. But, it's food for thought. :-)

func draw_over_viewport(overlay):
    var screen_size = OS.get_screen_size()
    var screen_width = screen_size[0]
    var scale_factor: float = screen_width / 1920
    var margin := 3 * scale_factor
    var width := 10 * scale_factor
    var rad_size := 20.0 * scale_factor
    var white_col = Color(1, 1, 1, 0.9)
    var col = Color.coral

    # White margin background
    overlay.draw_circle(_overlay_hovering_pos, rad_size + margin, white_col)
    overlay.draw_circle(_overlay_hovering_from, rad_size + margin, white_col)
    overlay.draw_line(
        _overlay_hovering_from,
        _overlay_hovering_pos,
        white_col,
        width+(margin*2),
        true)

    # Now color based on operation
    overlay.draw_circle(_overlay_hovering_pos, rad_size, col)
    overlay.draw_circle(_overlay_hovering_from, rad_size, col)
    overlay.draw_line(
        _overlay_hovering_from,
        _overlay_hovering_pos,
        col,
        width,
        true)
TheDuckCow commented 3 months ago

Closing this now as we have it merged finally! Took awhile, but it's feeling quite nice now. One caveat listed in the PR around disconnect undo steps potentially leaving users in a less than ideal state.

https://github.com/user-attachments/assets/ca6d2f5c-7490-435a-9b0a-b27458b541d9