The topic is part of the problem but not the whole story.
I’m making a strategy game with a map, and I found some code online which takes an image and create polygons from regions of the same colour to make an interactive map (https://www.youtube.com/watch?v=n3FvvmBDvBY is the youtube video for the code if that’s helpful). However, I have some regions which are fully surrounded by another, which causes the code to create two overlapping polygons, which I don’t want to happen, but when the code has finished generating all of the polygons (allowing me to check if there are any overlaps), they have already been scattered across many various nodes, and checking for collisions would require lots of looking across sibling or parent nodes, which has already caused me a headache before, so I was wondering if there was a faster solution that would allow me to fix the polygons?
Are you familiar with Geometry2D already? A great utility class to do geometric operations with.
You could also look into my plugin’s codebase to see if you find what you need there. Multiple hole handling operations are in its Geometry2DUtil class.
If you were to adapt the video’s code to make my my plugin’s ScalableVectorShape2D nodes in stead of Polgyon2D directly you could select them in editor and assign them as clip paths for other shapes visually without looking around the scene tree.
More details on that:
A video to see how I first intended it.
(Edit): my second answer suggests a more hands-on solution (you could call it ‘low level’ if you want)
You could try to detect whether a generated polygon is a hole by using Geometry2D.is_polygon_clockwise, but that would have to happen during the generation, I think.
Although probably the check still works when you read out a Polygon2D’s polygon property.
Maybe you could try that:
# pseudo code
for poly in get_all_the_polygon_2d_nodes():
if Geometry2D.is_polygon_clockwise(poly.polygon):
print(poly, " - is a hole")
Again, not a guarantee.
If you have found a hole, the surrounding Polygon2D can mimic having a hole inside by using point indices in its polygons array to have it draw multiple polygons.
But do you still have the information what is the surrounding polygon? If not, you could find that back as well using Geometry2D.is_point_in_polygon.
After slicing it in 2 you need to clip both resulting halves with the polygon that was the original hole.
This whole approach would be easiest when you simply improve the original author’s algorithm to detect holes and apply the appropriate slices from the get go.
Also. If one polygon has 2 or more holes, you will need to slice it again for each hole, or try to make once slice through as many holes as possible (maybe an unnecessary optimization).
…but you were looking for a simpler way…
I don’t know if my first answer sounds simpler. But this second one is your alternative
Ok after a bit of coding I have been able to find the holes that are needed to be cut out of the larger shape, but I am still very confused as to how I should cut the holes out.
I am fairly new to godot and looking at the “slicing in two” code from the second comment gave me a headache to look at, and I’m not sure where the relevant code is in the github, so I was wondering if you could tell me which bits of code (from either source) I’d actually need considering:
Before I try and answer: a screenshot would help. One containing a bit of your scene tree with (a) polygon(s) that should be a hole selected as well as the polygon that contains the holes… as well as what they look like visually.
I have made a fork and will try it out with a small test. This could take a while:
It’s a bit hard to get a screenshot as the polygons are generated, but the below image contains the remote tree and the map which is being modified. A polygon is being created for each blob of colour, however the light blue sea blob needs holes cut out for the slightly greyed out islands, which should be the same colour as the dark blue blob for the netherlands except it’s currently overlapping with the sea polygon.
The expanded Territory_Belgium node’s contents are similar to the other Area2D nodes, except a few have extra polygons, such as the netherlands having polygons for the islands
All polygons have the same shape as described/visible, so hopefully you know what you need to know.
Let me know if you need to see anything else, and thanks for your help!
I’ve checked out the code from the original demo and I’m sorry to say that I now understand why your holes are not being detected. It looks like the function to convert bitmap to polygons that is being used does not produce information about holes, like the Geometry2D functions I was referring to:
I fixed it (although I did kinda create a lake in their Norway to test with ).
It may be a little slower, but it only has to run once at startup, right? [1]
As you can see, I did need almost a whole copy of the Geometry2DUtil class to do the correct build-up of polygons from pixels. You may want to prune the dead code, but it’s good utility stuff either way.
Let me know if you need any more help getting it up and started.
Would you also be so kind as to leave a nice review in the godot asset store for my plugin?
Also, @Venidici , if you’re still looking for a pixel to polygon converter, you may also be interested in this fork from the grand strategy map thing (just as a starting point, but it’s a good proof of concept, my offer to work it out still stands, especially now I know more about how simple it could be).
[1]: You may want to convert the Main.gd script to a @tool, or save the resulting scene to a .tscn file containing all the polygons so you never have to run it again
I was able to transfer the code you made to make my game work, thank you! I would leave a review but I don’t have an account to leave one with.
Also I was wondering how exactly to turn Main.gd into a tool, as the map I’ll actually be using is incredibly large and the new script takes much longer to load, even for a smaller file (from <1 second to 30-60 for my test image which is only slightly bigger than the one you used), so I’d appreciate if you could let me know how to do it with this specific script, as the documentation has a lot of scary warnings.
var scene = PackedScene.new()
var result = scene.pack(root_of_scene)
if result == OK:
var error = ResourceSaver.save(scene, "res://path/name.tscn") # Or "user://..."
if error != OK:
push_error("An error occurred while saving the scene to disk.")
This video illustrates how you can use it and how it was made:
Making a new map
You’ll be wanting to make your own map, in order to make the script work out the box you need to keep one thing in mind:
The SVG file has to have one <g> (group node) per country and the groups should not be nested. Also the group node’s label is used to apply the name.
This you can see best do in the inkscape editor. The end of the youtube video I shared in this reply shows you how.
You can also delete the entire addon after doing the import. Because the scene contains all the polygons as well, you can even delete the svg image file from the assets you ship in your game.
Thank you so much! I will have to learn how to use inkscape so it’ll be a little while until I get back to you with how the code works, but it looks amazing!
Thanks! Beware, though. I tried it with a CC0 licensed map from Wikipedia with many many points. It works, but the editor in edit mode will be very very slow.
I think combining medium detail svg with a nice looking raster may look best
Does each country have to be filled in with a colour or can it be an outline? If so, does it have to be the same colour or can they be different colours?
I’m also assuming that each individual country in a group has to be a self contained shape, even if that means having bordering countries having overlapping lines, is that correct?
Also is there anything I have to do about layers? If I shouldn’t have any how do I delete them?
Just an outline should work, but then the example project will not highlight anything, because it treats fill color as Polygon2D. You may have to adapt it to change the Line2D color.
Different colors is fine, but the example script overrides it so you may have to delete that bit of code from it. I can adapt it too but at some point you’ll have to be in charge of your own code. I’m not building your game for you either, right?
Correct. Like wooden puzzle pieces.
I don’t think it matters. The example code expect groups to be no more than exactly one level deep. I haven’t even looked at layers myself. Just ignore it and test if it works first.