Polygon from collection of Latitudes & Longitudes - vb.net

I have a collection of latitudes and longitudes and I'll be grabbing sets of these and want to draw a polygon based on them.
The datasets won't be the outline so will need an algorithm to establish which ones make up the outline of a polygon containing all the latitudes and longitudes supplied. This polygon needs to be flexible so the polygon can be concave if the points dictate that.
Any help would be appreciated.
** UPDATE **
Sorry, should have put more detail.
My code below produces a horrible looking polygon. As explain in my first post I want to create a nice concave or convex polygon based on the latlng's provided.
Just need a way of plotting the outer latlngs.
Apologies if this is still asking too much but thought it was worth one last try.
function initialize() {
var myLatLng = new google.maps.LatLng(51.407431, -0.727142);
var myOptions = {
zoom: 12,
center: myLatLng,
mapTypeId: google.maps.MapTypeId.TERRAIN
};
var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
var bermudaTriangle;
var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
var triangleCoords = [
new google.maps.LatLng(51.392692, -0.740358),
new google.maps.LatLng(51.400618, -0.742469),
new google.maps.LatLng(51.40072, -0.72418),
new google.maps.LatLng(51.400732, -0.743817),
new google.maps.LatLng(51.401258, -0.743386),
new google.maps.LatLng(51.401264, -0.741445),
new google.maps.LatLng(51.401443, -0.725555),
new google.maps.LatLng(51.401463, -0.744042),
new google.maps.LatLng(51.402281, -0.739059)
];
var minX = triangleCoords[0].lat();
var maxX = triangleCoords[0].lat();
var minY = triangleCoords[0].lng();
var maxY = triangleCoords[0].lng();
for (var i = 1; i < triangleCoords.length; i++) {
if (triangleCoords[i].lat() < minX) minX = triangleCoords[i].lat();
if (triangleCoords[i].lat() > maxX) maxX = triangleCoords[i].lat();
if (triangleCoords[i].lng() < minY) minY = triangleCoords[i].lng();
if (triangleCoords[i].lng() > maxY) maxY = triangleCoords[i].lng();
}
// Construct the polygon
bermudaTriangle = new google.maps.Polygon({
paths: triangleCoords,
strokeColor: "#FF0000",
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: "#FF0000",
fillOpacity: 0.35
});
bermudaTriangle.setMap(map);
}

Your problem is not enough defined : with a given set of points, you may end up with many different polygons if you do not add a constraint other than 'create a nice concave or convex polygon'.
And even a simple example shows that :
imagine a triangle ABC, and let D be the center of this triangle, what output will you expect for {A,B,C,D} set of points ?
ABC, since D is inside ?
or ADBCA polygon ?
or ABDCA polygon ?
or ABCDA polygon ?
now if you say 'well D is in the center, it's obvious we should discard D', let D be closer and closer from, say, the AB segment. When do you decide the best output is ABC or ADBCA ?
So you have to add constraints to be able to build an algorithm, since if you cannot not decide by yourself for the above {A,B,C,D} example, how could a computer do ? :-) For example if you call AvgD the average distance beetween points, you could add the constraint that no segment of your outer polygon should be longer than 1.2*AvgD (or, better, Alpha*AvgD, and you try your algorithm with different alpha).
To solve your issue, i would use first a classical hull algorithm to get the outer convex polygon (which is deterministic), then break down the segments that are 'too' long (with the constraint(s) you want) putting more and more inner points into the outlining until all constraints are ok. Something like 'digging holes' into the convex polygon.
'Breaking down' a too long segment is also a thing you can do in quite different maners. One may be to search for the nearest not-in-the-outline point from the middle point of the segment. Another would be to choose the point having lowest radius with current segment... Now that you have your new point, break the segment in two, update your list of too-loong segment, and do it again until you're done (or until you reach a 'satisfactory' average length for too long segments, or ...)
Good luck !

Related

How do I set a variable to contain rotational degrees?

I'm trying to implement a leaning mechanic in a game that I'm building. To do that I want to set one variable to act as the default number of rotation degrees (ideally x0, y0, and z0), and one for the rotation degrees of a character that is leaning to the right (ideally x0.6, y0, and z0).
Here's my code (for context, this script is attached to a Spatial node called UpperBody):
extends Spatial
const LEAN_LERP = 5
export var default_degrees : Vector3
export var leaning_degrees : Vector3
func _process(delta):
if Input.is_action_pressed("LeanRight"):
transform.origin = transform.origin.linear_interpolate(leaning_degrees, LEAN_LERP * delta)
else:
transform.origin = transform.origin.linear_interpolate(default_degrees, LEAN_LERP * delta)
if Input.is_action_pressed("LeanLeft"):
transform.origin = transform.origin.linear_interpolate(-leaning_degrees, LEAN_LERP * delta)
else:
transform.origin = transform.origin.linear_interpolate(default_degrees, LEAN_LERP * delta)
As you can see, I have both default_degrees and leaning_degrees' types set to Vector3 instead of the (currently unknown) equivalent for rotational degrees.
My question is this: how do I set a variable to contain rotational degrees?
Thanks.
There is no dedicated type for Euler angles. Instead you would use … drum roll … Vector3.
In fact, if you see the rotation_degrees property, you will find it is defined as a Vector3.
That, of course, isn't the only way to represent rotations/orientations. Ultimately, the Transform has two parts:
A Vector3 called origin which represents the translation.
A Basis called basis which represent the rest of the transformation (rotation, scaling and reflection, and shear or skewing).
A Basis can be thought of a trio of Vector3 each representing one of the axis of the coordinate system. Another way to think of Basis is as a 3 by 3 matrix.
Thus whatever you use to represent rotations or orientations will ultimately be converted to a Basis (and then either replace or be composed with the Basis of the transform).
Now, you want to interpolate the rotations, right? Euler angles aren't good for interpolation. Instead you could interpolate:
Transformations (Transform using interpolate_with Transform.interpolate_with).
Basis (Basis using Basis.slerp).
Quaternions (Quat using Quat.slerp).
On the other hand, Euler angles are good for input. In this particular case that means it is relative easy to wrap your head around what the numbers mean compared to writing any of these in the inspector.
Thus, we have two avenues:
Convert Euler angles to either Transform, Basis or Quat.
Find an easy way to input a Transform, Basis or Quat.
Euler angle to Quat
The Quat has a constructor that takes a vector for Euler angles. The catch is that it is Euler angles in radians. So we need to convert degrees to radians (which we can do with deg2rad). Like this:
var target_quat := Quat(
Vector3(
deg2rad(degrees.x),
deg2rad(degrees.y),
deg2rad(degrees.z)
)
)
Alternatively, you could do this:
var target_quat := Quat(degrees * PI / 180.0)
We also need to get the current quaternion from the transform:
var current_quat := transform.basis.get_rotation_quat()
Interpolate them:
var new_quat := current_quat.slerp(target_quat, LEAN_LERP * delta)
And replace the quat:
transform = Transform(
Basis(new_quat).scaled(transform.basis.get_scale()),
transform.origin
)
The above line assumes the transformation is only rotation, scaling, and translation. If we want to keep skewing, we can do this:
transform = Transform(
Basis(new_quat) * Basis(current_quat).inverse() * transform.basis,
transform.origin
)
The explanation for that is in the below section.
Notice we ended up converting the Quat to a Basis. So perhaps we are better off avoiding quaternions entirely.
Euler angle to Basis
The Basis class also has a constructor that works like the one we found in Quat. So we can do this:
var target_basis := Basis(degrees * PI / 180.0)
The catch this time is that Basis does not only represent rotation. So if we do that, we are losing scaling (and any other transformation the Basis has). We can preserve the scaling like this:
target_basis = target_basis.scaled(transform.basis.get_scale())
Ah, of course, the current Basis is this:
var current_basis := transform.basis
We interpolate like this:
var new_basis := current_basis.slerp(target_basis, LEAN_LERP * delta)
And we replace the Basis like this:
transform.basis = new_basis
To be honest, I'm not happy with the above approach. I'll show you a way to have the Basis you interpolate be only for rotation (so it can preserve any skewing the original Basis had, not only its scale), but it is a little more involved. Let us start here again:
var target_rotation := Basis(degrees * PI / 180.0)
And we will not scale that, instead we want to get a Basis that is only the rotation of the current one. We can do that by going from Basis to Quat and back:
var current_rotation := Basis(transform.basis.get_rotation_quat())
We interpolate the same way as before:
var new_rotation := current_rotation.slerp(target_rotation, LEAN_LERP * delta)
But to replace the Basis we want to keep everything about the old Basis that wasn't the rotation. In other words we are going to:
Take the Basis:
transform.basis
Remove its rotation (i.e. compose it with the inverse of its rotation):
Basis(transform.basis.get_rotation_quat()).inverse() * transform.basis
Which is the same as:
current_rotation.inverse() * transform.basis
And apply the new rotation:
new_rotation * current_rotation.inverse() * transform.basis
And that is what we set:
transform.basis = new_rotation * current_rotation.inverse() * transform.basis
I have tested to make sure the composition order is correct. And, yes, code for preserving skewing with Quat I showed above is based on this.
Euler angle to Transform
The way to create a Transform from Euler angles is via a Basis:
var target_transform := Transform(Basis(degrees * PI / 180.0), Vector3.ZERO)
We could preserve scale and translation with this approach:
var target_transform := Transform(
Basis(degrees * PI / 180.0).scaled(trasnform.basis.get_scale()),
transform.origin
)
If you want to interpolate translation at the same time, you can set your target position instead of transform.origin.
The current transform is, of course:
var current_transform := transform
We interpolate them like this:
var new_transform = current_transform.interpolate_with(target_transform, LEAN_LERP * delta)
And we can set that:
transform = new_trasnform
If we inline these variables, we have this:
transform = transform.interpolated_with(target_transform, LEAN_LERP * delta)
If you want to preserve skewing, use the Basis approach.
Alternative input to Euler angles
We have found out that interpolating transforms is actually very easy. Is there a way to easily input a Transform? Rhetorical question. We can add some Position3D to the scene. Position and rotate them (and even scale them, even though Position3D has no size), and then use the Transform from them.
We can make the Position3D children of your Spatial (which is somewhat odd, but don't think too hard about it), or as sibling. Regardless, the idea is that we are going to take the transform from these Position3D and use it to interpolate the transform of your Spatial. It is the same code as before:
transform = transform.interpolated_with(position.transform, LEAN_LERP * delta)
In fact, while we are at it, why not have three Position3D:
The lean left target.
The lean right target.
The default target.
Then you pick which target to use depending on input, and interpolate to that:
extends Spatial
const LEAN_LERP = 5
onready var left_target:Position3D = get_node(…)
onready var right_target:Position3D = get_node(…)
onready var default_target:Position3D = get_node(…)
func _process(delta):
var left := Input.is_action_pressed("LeanLeft")
var right := Input.is_action_pressed("LeanRight")
var target := default_target
if left and not right:
target = left_target
if right and not left:
target = right_target
transform = transform.interpolate_with(target, LEAN_LERP * delta)
Put the node paths where I left ....
Ok, Ok, here is one of the Euler angles versions:
extends Spatial
const LEAN_LERP = 5
export var default_degrees : Vector3
export var leaning_degrees : Vector3
func _process(delta):
var left := Input.is_action_pressed("LeanLeft")
var right := Input.is_action_pressed("LeanRight")
var degrees := default_degrees
if left and not right:
degrees = -leaning_degrees
if right and not left:
degrees = leaning_degrees
var target_rotation := Basis(degrees * PI / 180.0)
var current_rotation := Basis(transform.basis.get_rotation_quat())
var new_rotation := current_rotation.slerp(target_rotation, LEAN_LERP * delta)
transform.basis = new_rotation * current_rotation.inverse() * transform.basis

Godot Inversing selected rectangle area made up of two Vector2 objects

This seems like a really simple question but I've been at this for a couple of hours and need an outsiders perspective.
I'm migrating a start of a game to Godot from Unity.
I'm selecting an area of tiles (startDragPosition, endDragPosition, both Vector2 objects) from a TileMap and setting them to a certain tile. Currently the dragging only works if the direction is top->bottom and left->right, so if the ending x and y are larger than the starting x and y
In Unity(C#) I had a few simple lines to flip the rectangle values if it was dragged in reverse.
if (end_x < start_x) {
int tmp = end_x;
end_x = start_x;
start_x = tmp;
}
if (end_y < start_y) {
int tmp = end_y;
end_y = start_y;
start_y = tmp;
}
However in when I try a similar approach in Godot it is not working for some reason. I'm thinking that I'm messing up somewhere earlier and any help would be appreciated. If there is an easier way of doing this please tell me I'm fairly new to Godot itself.
Here is the function responsible for dragging in my Godot script(GD)
func Drag():
if(Input.is_action_just_pressed("click")):
startDragPosition=get_global_mouse_position()
if(Input.is_action_pressed("click")):
endDragPosition=get_global_mouse_position()
print("01 START: "+String(stepify(startDragPosition.x-8,16)/16)+"_"+String(stepify(startDragPosition.y-8,16)/16))
print("01 END: "+String(stepify(endDragPosition.x-8,16)/16)+"_"+String(stepify(endDragPosition.y-8,16)/16))
if(endDragPosition.x<startDragPosition.x):
var temp = endDragPosition.x
endDragPosition.x=startDragPosition.x
startDragPosition.x=temp
if(endDragPosition.y<startDragPosition.y):
var temp = endDragPosition.y
endDragPosition.y=startDragPosition.y
startDragPosition.y=temp
for x in range(startDragPosition.x,endDragPosition.x):
for y in range(startDragPosition.y,endDragPosition.y):
get_node("../DragPreview").set_cell((stepify(x-8,16))/16,(stepify(y-8,16))/16,0)
#get_node("../DragPreview").update_bitmask_area(Vector2((stepify(x-8,16))/16,(stepify(y-8,16))/16))
if(Input.is_action_just_released("click")):
print("START: "+String(stepify(startDragPosition.x-8,16)/16)+"_"+String(stepify(startDragPosition.y-8,16)/16))
print("END: "+String(stepify(endDragPosition.x-8,16)/16)+"_"+String(stepify(endDragPosition.y-8,16)/16))
startDragPosition=null
endDragPosition=null
When you drag, you always write to endDragPosition.
When you drag to the left or drag up, and you update endDragPosition, it will have smaller coordinates than it had before. Because of that you swap the coordinates with startDragPosition… And then you keep dragging left or up, and that updates endDragPosition again. The original startDragPosition is lost.
Either you work with a copy when you are deciding the start and end:
var start = startDragPosition
var end = endDragPosition
if(end.x<start.x):
var temp = end.x
end.x=start.x
start.x=temp
if(end.y<start.y):
var temp = end.y
end.y=start.y
start.y=temp
for x in range(start.x,end.x):
for y in range(start.y,end.y):
# whatever
pass
Or you forget this swapping shenanigans, and give the loops a step:
var start = startDragPosition
var end = endDragPosition
for x in range(start.x,end.x,sign(end.x-start.x)):
for y in range(start.y,end.y,sign(end.y-start.y)):
# whatever
pass

Generating country-shaped geometry on the surface of a sphere in Godot

I am currently working on a game in Godot which involves rendering countries on a globe. I have very little prior experience with Godot, but have experimented with it in the past.
I am using this data from Natural Earth for country borders, and have successfully gotten it to display on the globe using a line mesh. The data was originally in shapefile format, but I converted it to GeoJSON using mapshaper.org.
Picture
The data basically boils down to a list of points given in latitude and longitude, which I then converted into 3d points and created a mesh using SurfaceTool.
I am having trouble generating an actual surface for the mesh, however. Firstly, I am unable to find a built-in function to generate a triangle mesh from this data. I have looked into numerous solutions, including using the built-in Mesh.PRIMITIVE_TRIANGLE_FAN format, which doesn't work on concave shapes.
I have looked into triangulation algorithms such as delaunay triangulation, but have had little success implementing them.
My current plan is to generate a triangle mesh using the 2d data (x,y = longitude,latitude), and project it onto the surface of the sphere. In order to produce a curved surface, I will include the vertices of the sphere itself in the mesh (example).
I would like to know how to go about constructing a triangle mesh from this data. In essence, I need an algorithm that can do the following things:
Create a triangle mesh from a concave polygon (country border)
Connect the mesh to a series of points within this polygon
Allow for holes within the polygon (for lakes, etc.)
Here is an example of the result I am looking for.
Again, I am quite new to Godot and I am probably over-complicating things. If there is an easier way to go about rendering countries on a globe, please let me know.
This is my current code:
extends Node
export var radius = 1
export var path = "res://data/countries.json"
func coords(uv):
return (uv - Vector2(0.5, 0.5)) * 360
func uv(coords):
return (coords / 360) + Vector2(0.5, 0.5)
func sphere(coords, r):
var angles = coords / 180 * 3.14159
return Vector3(
r * cos(angles.y) * sin(angles.x),
r * sin(angles.y),
r * cos(angles.y) * cos(angles.x)
)
func generate_mesh(c):
var mesh = MeshInstance.new()
var material = SpatialMaterial.new()
material.albedo_color = Color(randf(), randf(), randf(), 1.0)
var st = SurfaceTool.new()
st.begin(Mesh.PRIMITIVE_LINE_STRIP)
for h in c:
var k = sphere(h, radius)
st.add_normal(k / radius)
st.add_uv(uv(h))
st.add_vertex(k)
st.index()
mesh.mesh = st.commit()
mesh.set_material_override(material)
return mesh
func load_data():
var file = File.new()
file.open(path, file.READ)
var data = JSON.parse(file.get_as_text()).result
file.close()
for feature in data.features:
var geometry = feature.geometry
var properties = feature.properties
if geometry.type == "Polygon":
for body in geometry.coordinates:
var coordinates = []
for coordinate in body:
coordinates.append(Vector2(coordinate[0], coordinate[1]))
add_child(generate_mesh(coordinates))
if geometry.type == "MultiPolygon":
for polygon in geometry.coordinates:
for body in polygon:
var coordinates = []
for coordinate in body:
coordinates.append(Vector2(coordinate[0], coordinate[1]))
add_child(generate_mesh(coordinates))
func _ready():
load_data()
What about using Geometry.triangulate_polygon() method to triangulate a polygon:

Custom Delaunay Refinement with CGAL Delaunay3D

I want to perform a custom refinement strategy in a tetrahedral mesh.My input is a point cloud and I have tetrahedralized it using Delaunay 3D routine available in CGAL. The points have scalar values associated with it. Now I want to refine the tetrahedral mesh with this following strategy:
1. Get the maximum value among the vertices of each tetrahedra.
2. Get the value at the point that is going to be inserted (May be barycentre, weighted centroid or circumcenter).
3. If the difference is large enough add this point.
Any idea how to do this effectively? Note: I do not require 0-1 dimensional feature preservation.
I have already tried the above strategy. Let me show what I have done so far.
// Assume T is Delaunay_3D triangulation CGAL mesh and I have an oracle f that tells me what is the value at the point that is going to be inserted if conditions are met.
bool updated = true;
int it = 0;
while (updated)
{
updated = false;
std::vector<std::pair<Point, unsigned> > point_to_be_inserted;
for (auto cit = T.finite_cells_begin(); cit != T.finite_cells_end(); cit++)
{
Cell_handle c = cit;
Point v = Maximum valued vertex
Point q = Point that is going to be inserted
double val_at_new_pt = oracle(q, &pts, var);
double ratio = std::abs(max_val - val_at_new_pt) / max_val;
if (ratio > threshold) {
point_to_be_inserted.emplace_back(std::make_pair(q, new_pt_ind));
updated = true;
}
}
if (updated)
{
std::cout << "Total pts inserted in it: " << it << " " << point_to_be_inserted.size() << std::endl;
T.insert(point_to_be_inserted.begin(), point_to_be_inserted.end())
}
}
The problem is it is quite slow (each time iterating through all the cells). I am not finding any effective strategy to do the refinement locally. I tried using a queue but the cell_handles are getting messed up after I perform one iteration of refinement. I cannot have a map that tells me whether the tetrahedra is refined or not because each time after insertion of new points cell_handles are getting created. Any help will be appreciated. Thanks in advance.

Move polygon points based on center location?

Would someone have an example using Turf moving all polygon points from one location to another based on a new center point?
For example, let’s say I have calculated a center based on where the points were originally created.
Then I need to move those points to a new location based on that calculated center.
As always any help would be great!
You need to use transformTranslate
var poly = turf.polygon([[[0,29], [3.5,29], [2.5,32], [0,29]]]);
//calculates the centroid of the polygon
var center = turf.centroid(poly);
var from = turf.point([center.geometry.coordinates[0], center.geometry.coordinates[1]]);
var to = turf.point([4, 35]);
//finds the bearing angle between the points
var bearing = turf.rhumbBearing(from, to);
//calculates the distance between the points
var distance = turf.rhumbDistance(from, to);
//moves the polygon to the distance on the direction angle.
var translatedPoly = turf.transformTranslate(poly, distance, bearing)