How to instance a scene and have it face only an x or z direction? - game-development

I'm trying to create a "Block" input that summons a wall at the position the camera is looking at and have it face the camera's direction. It seems to be using the global coordinates which doesn't make sense to me, because I use the same code to spawn a bullet with no problem. Here's my code:
if Input.is_action_just_pressed("light_attack"):
var b = bullet.instance()
muzzle.add_child(b)
b.look_at(aimcast.get_collision_point(), Vector3.UP)
b.shoot = true
print(aimcast.get_collision_point())
if Input.is_action_just_pressed("block"):
var w = wall.instance()
w.look_at(aimcast.get_collision_point(),Vector3.UP)
muzzle.add_child(w)
w.summon = true
The light attack input is the code used to summon and position the bullet. muzzle is the spawn location (just a spatial node at the end of the gun) and aimcast is a raycast from the center of the camera. All of this is run in a get_input() function. The wall spawns fine, I just can't orient it. I also need to prevent any rotation on the y-axis. This question is kinda hard to ask, so I couldn't google it. If you need any clarification please let me know.

New Answer
The asked comment made me realize there is a simpler way. In the old answer I was defining a xz_aim_transform, which could be done like this:
func xz_aim_transform(pos:Vector3, target:Vector3) -> Transform:
var alt_target := Vector3(target.x, pos.y, target.z)
return Transform.IDENTITY.translated(pos).looking_at(alt_target, Vector3.UP)
That is: make a fake target at the same y value, so that the rotation is always on the same plane.
It accomplishes the same thing as the approach in the old answer. However, it is shorter and easier to grasp. Regardless, I generalized the approach in the old answer, and the explanation still has value, so I'm keeping it.
Old Answer
If I understand correctly, you want something like look_at except it only works on the xz plane.
Before we do that, let us establish that look_at is equivalent to this:
func my_look_at(target:Vector3, up:Vector3):
global_transform = global_transform.looking_at(target, up)
The take away of that is that it sets the global_transform. We don't need to delve any deeper in how look_at works. Instead, let us work on our new version.
We know that we want the xz plane. Sticking to that will make it simpler. And it also means we don't need/it makes no sense to keep the up vector. So, let us get rid of that.
func my_look_at(target:Vector3):
# global_transform = ?
pass
The plan is to create a new global transform, except it is rotated by the correct angle around the y axis. We will figure out the rotation later. For now, let us focus on the angle.
Figuring out the angle will be easy in 2D. Let us build some Vector2:
func my_look_at(target:Vector3):
var position := global_transform.origin
var position_2D := Vector2(position.x, position.z)
var target_2D := Vector2(target.x, target.z)
var angle:float # = ?
# global_transform = ?
That part would not have been as easy with an arbitrary up vector.
Notice that we are using the 2D y for the 3D z values.
Now, we compute the angle:
func my_look_at(target:Vector3):
var position := global_transform.origin
var position_2D := Vector2(position.x, position.z)
var target_2D := Vector2(target.x, target.z)
var angle := (target_2D - position_2D).angle_to(Vector2(0.0, -1.0))
# global_transform = ?
Since we are using the 2D y for the 3D z values, Vector2(0.0, -1.0) (which is the same as Vector2.UP, by the way) is representing Vector3(0.0, 0.0, -1.0) (Which is Vector3.FORWARD). So, we are computing the angle to the 3D forward vector, on the xz plane.
Now, to create the new global transform, we will first create a new basis from that rotation, and use it to create the transform:
func my_look_at(target:Vector3):
var position := global_transform.origin
var position_2D := Vector2(position.x, position.z)
var target_2D := Vector2(target.x, target.z)
var angle := (target_2D - position_2D).angle_to(Vector2.UP)
var basis := Basis(Vector3.UP, angle)
global_transform = Transform(basis, position)
You might wonder why we don't use global_transform.rotated, the reason is that using that multiple times would accumulate the rotation. It might be ok if you only call this once per object, but I rather do it right.
There is one caveat to the method above. We are losing any scaling. This is how we fix that:
func my_look_at(target:Vector3):
var position := global_transform.origin
var position_2D := Vector2(position.x, position.z)
var target_2D := Vector2(target.x, target.z)
var angle := (target_2D - position_2D).angle_to(Vector2.UP)
var basis := Basis(Vector3.UP, angle).scaled(global_transform.basis.get_scale())
global_transform = Transform(basis, position)
And there you go. That is a custom "look at" function that works on the xz plane.
Oh, and yes, as you have seen, your code works with global coordinates. In fact, get_collision_point is in global coordinates.
Thus, I advice not adding your projectiles as children. Remember that when the parent moves, the children move with it, because they are placed relative to it.
Instead give them the same global_transform, and then add them to the scene tree. If you add them to the scene before giving them their position, they might trigger a collision.
You could, for example, add them directly as children to the root (or have a node dedicated to holding projectiles, another common option is to add them to owner).
That way you are doing everything on global coordinates, and there should be no trouble.
Well, since you are going to set the global_transform anyway, how about this:
func xz_aim_transform(position:Vector3, target:Vector3) -> Transform:
var position_2D := Vector2(position.x, position.z)
var target_2D := Vector2(target.x, target.z)
var angle := (target_2D - position_2D).angle_to(Vector2.UP)
var basis := Basis(Vector3.UP, angle)
return Transform(basis, position)
Then you can do this:
var x = whatever.instance()
var position := muzzle.global_transform.origin
var target := aimcast.get_collision_point()
x.global_transform = xz_aim_transform(position, target)
get_tree().get_root().add_child(x)
x.something = true
print(target)
By the way, this would be the counterpart of xz_aim_transform not constrained to the xz plane:
func aim_transform(position:Vector3, target:Vector3, up:Vector3) -> Transform:
return Transform.IDENTITY.translated(position).looking_at(target, up)
It took me some ingenuity, but here is the version constrained to an arbitrary plane (kind of, as you can see it does not handle all cases):
func plane_aim_transform(position:Vector3, target:Vector3, normal:Vector3) -> Transform:
normal = normal.normalized()
var forward_on_plane := Vector3.FORWARD - Vector3.FORWARD.project(normal)
if forward_on_plane.length() == 0:
return Transform.IDENTITY
var position_on_plane := position - position.project(normal)
var target_on_plane := target - target.project(normal)
var v := forward_on_plane.normalized()
var u := v.rotated(normal, TAU/4.0)
var forward_2D := Vector2(0.0, forward_on_plane.length())
var position_2D := Vector2(position_on_plane.project(u).dot(u), position_on_plane.project(v).dot(v))
var target_2D := Vector2(target_on_plane.project(u).dot(u), target_on_plane.project(v).dot(v))
var angle := (target_2D - position_2D).angle_to(forward_2D)
var basis := Basis(normal, angle)
return Transform(basis, position)
Here w - w.project(normal) gives you a vector perpendicular to the normal. And w.project(u).dot(u) gives you how many times u fit in w, signed. So we use that build our 2D vectors.

Related

GODOT 4 | Navigation path gets lost

For some reason, the enemy, when following the player, can change the path for a second to some corner or somewhere else. Because of this, he sometimes twitches and goes back. What could be the problem? Here is my code
extends CharacterBody2D
class_name Ghosts
#onready var region_id: RID = NavigationServer2D.region_create()
var path:Array = []
var nav:NavigationRegion2D = null
var player = null
func _ready():
await get_tree()
var tree = get_tree()
if tree.has_group("LevelNavigation"):
nav = tree.get_nodes_in_group("LevelNavigation")[0]
NavigationServer2D.region_set_map(region_id, get_world_2d().navigation_map)
NavigationServer2D.region_ser_navpoly(region_id, nav.navpoly)
if tree.has_group("Player"):
player = tree.get_nodes_in_group("Player")[0]
func attack(speed:int):
if path.size() > 0:
velocity = global_position.direction_to(path[1]) * speed
if global_position == path[0]:
path.pop_front()
move_and_slide()
func generate_path(line2D: Line2D):
if nav != null and player != null:
path = NavigationServer2D.map_get_path(get_world_2d().navigation_map, position
line2D.points = path
I would like to get the correct path. Maybe it's the features of Godot 4
Noop
First this is nothing:
await get_tree()
The method get_tree is not asynchronous, and you are discarding the result anyway. Perhaps you wanted to await for the Node to enter the scene tree? That would be await self.tree_entered… Except you have the line inside _ready and when Godot calls _ready the Node is inside the tree (which also means it will not enter, so waiting for it to enter won't work).
Just remove that line.
Following the path
Second this won't work well:
if path.size() > 0:
velocity = global_position.direction_to(path[1]) * speed
if global_position == path[0]:
path.pop_front()
move_and_slide()
You are moving towards path[1] and not path[0]
Even if you were, nothing is preventing you from overshooting.
Even if you were preventing overshooting, the equality comparison won't work well due to floating point errors.
So the plan:
If there are no points in the path, we are done.
If there are points in the path, pick the first point.
If the current position is close enough to the point, remove it.
Otherwise move towards the point.
Let us do it:
if path.empty():
return
var point := path[0]
if global_position.distance_to(point) <= threshold:
path.pop_front()
return
velocity = global_position.direction_to(point) * speed
move_and_slide()
Ok, caveats:
We need to prevent overshooting.
How much is the threshold?
When we remove we don't move.
Let us fix that. We need to figure out, beforehand, much we have to move:
var distance_to_move := speed * delta
But we should not advance more than the distance to the point!
if path.empty():
return
var point:Vector2 = path[0]
var distance_to_point := global_position.distance_to(point)
var distance_to_move := min(speed * delta, distance_to_point)
And the velocity will be that to move that in this frame:
if path.empty():
return
var point:Vector2 = path[0]
var distance_to_point := global_position.distance_to(point)
var direction_to_point := global_position.direction_to(point)
var distance_to_move := minf(speed * delta, distance_to_point)
if distance_to_point <= distance_to_move:
path.pop_front()
velocity = direction_to_point * (distance_to_move / delta)
move_and_slide()
This way we don't overshoot, we move even when we remove, and we have a clear threshold for removing.
Caveats:
We don't always move the full distance.
For that we need a loop:
var total_distance_to_move := speed * delta
while total_distance_to_move > 0.0 and not path.empty():
var point:Vector2 = path[0]
var distance_to_point := global_position.distance_to(point)
var direction_to_point := global_position.direction_to(point)
var distance_to_move := minf(total_distance_to_move, distance_to_point)
if distance_to_point <= distance_to_move:
path.pop_front()
velocity = direction_to_point * (distance_to_move / delta)
move_and_slide()
total_distance_to_move -= distance_to_move
Generating the path
The code is cut off where you use map_get_path, however I can see you are using position.
Work with the global_position instead:
path = Navigation2DServer.map_get_path(
get_world_2d().get_navigation_map(),
global_position,
target_pos,
true
)
I don't know how you were getting the target position, but I'll trust you can figure out how to make it with global coordinates.
And you are setting this to a Line2D. That wants its own local coordinantes. So you do this:
line2d.points = line2d.global_transform.affine_inverse() * path

Model Shakes when moved long distance - Godot

I am making a space game in Godot and whenever my ship is a big distance away from (0,0,0) every time I move the camera or the ship, it shakes violently. Here is my code for moving the ship:
extends KinematicBody
export var default_speed = 500000
export var max_speed = 5000
export var acceleration = 100
export var pitch_speed = 1.5
export var roll_speed = 1.9
export var yaw_speed = 1.25
export var input_response = 8.0
var velocity = Vector3.ZERO
var forward_speed = 0
var vert_speed = 0
var pitch_input = 0
var roll_input = 0
var yaw_input = 0
var alt_input = 0
var system = "System1"
func _ready():
look_at(get_parent().get_node("Star").translation, Vector3.UP)
func get_input(delta):
if Input.is_action_pressed("boost"):
max_speed = 299792458
acceleration = 100
else:
max_speed = default_speed
acceleration = 100
if Input.is_action_pressed("throttle_up"):
forward_speed = lerp(forward_speed, max_speed, acceleration * delta)
if Input.is_action_pressed("throttle_down"):
forward_speed = lerp(forward_speed, 0, acceleration * delta)
pitch_input = lerp(pitch_input, Input.get_action_strength("pitch_up") - Input.get_action_strength("pitch_down"), input_response * delta)
roll_input = lerp(roll_input, Input.get_action_strength("roll_left") - Input.get_action_strength("roll_right"), input_response * delta)
yaw_input = lerp(yaw_input, Input.get_action_strength("yaw_left") - Input.get_action_strength("yaw_right"), input_response * delta)
func _physics_process(delta):
get_input(delta)
transform.basis = transform.basis.rotated(transform.basis.z, roll_input * roll_speed * delta)
transform.basis = transform.basis.rotated(transform.basis.x, pitch_input * pitch_speed * delta)
transform.basis = transform.basis.rotated(transform.basis.y, yaw_input * yaw_speed * delta)
transform.basis = transform.basis.orthonormalized()
velocity = -transform.basis.z * forward_speed * delta
move_and_collide(velocity * delta)
func _on_System1_area_entered(area):
print(area, area.name)
system = "E"
func _on_System2_area_entered(area):
print(area, area.name)
system = "System1"
Is there any way to prevent this from happening?
First of all, I want to point out, that this is not a problem unique to Godot. Although other engines have automatic fixes for it.
This happens because the precision of floating point numbers decreases as it goes away form the origin. In other words, the gap between one floating number and the next becomes wider.
The issue is covered in more detail over the game development sister site:
Why loss of floating point precision makes rendered objects vibrate?
Why does the resolution of floating point numbers decrease further from an origin?
What's the largest "relative" level I can make using float?
Why would a bigger game move the gameworld around the Player instead of just moving a player within a gameworld?
Moving player inside of moving spaceship?
Spatial Jitter problem in large unity project
Godot uses single precision. Support for double precision has been added in Godot 4, but that just reduces the problem, it does not eliminate it.
The general solution is to warp everything, in such way that the player is near the origin again. So, let us do that.
We will need a reference to the node we want to keep near the origin. I'm going to assume which node it is does not change during game play.
export var target_node_path:NodePath
onready var _target_node:Spatial = get_node(target_node_path)
And we will need a reference to the world we need to move. I'm also assuming it does not change. Furthermore, I'm also assuming the node we want to keep near the origin is a child of it, directly or indirectly.
export var world_node_path:NodePath
onready var _world_node:Node = get_node(target_node_path)
And we need a maximum distance at which we perform the shift:
export var max_distance_from_origin:float = 10000.0
We will not move the world itself, but its children.
func _process() -> void:
var target_origin := _target_node.global_transform.origin
if target_origin.length() < max_distance_from_origin:
return
for child in _world_node.get_children():
var spatial := child as Spatial
if spatial != null:
spatial.global_translate(-target_origin)
Now, something I have not seen discussed is what happens with physics objects. The concern is that The physics server might be trying to move them in the old position (in practice this is only a problem with RigidBody), and it will overwrite what we did.
So, if that is a problem… We can handle physic objects with a teleport. For example:
func _process() -> void:
var target_origin := _target_node.global_transform.origin
if target_origin.length() < max_distance_from_origin:
return
for child in _world_node.get_children():
var spatial := child as Spatial
if spatial != null:
var body_transform := physics_body.global_transform
var new_transform := Transform(
body_transform.basis,
body_transform.origin - target_origin
)
spatial.global_transform = new_transform
var physics_body := spatial as PhysicsBody # Check for RigidBody instead?
if physics_body != null:
PhysicsServer.body_set_state(
physics_body.get_rid(),
PhysicsServer.BODY_STATE_TRANSFORM,
new_transform
)
But be aware that the above code does not consider any physics objects deeper in the scene tree.

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

How to get the height and width of the visible browser area

How can I retrieve the height and width of the browser area?
I want to show one form always in landscape mode, regardless whether the mobile phone has rotated the browser view.
if the browser is in portrait postion I want to rotate the form (TForm.Angel := 90). In order to force landscape mode.
Update:
Here is a picture, how the result should look like:
I found a solution, but I am not so really happy with it. It's easy to rotate the view, but the origin is not in the center, so I have to manually correct it, and I don't understand why this transformation is neccessary.
Here is the code:
procedure TForm1.ForceLandscape(aEnabled: Boolean);
var
browserWidth, browserHeight: Integer;
isLandscapeMode: Boolean;
begin
browserHeight := Application.Display.ClientHeight; //BrowserAPI.Window.innerHeight;
browserWidth := Application.Display.ClientWidth; //BrowserAPI.Window.innerWidth;
isLandscapeMode := browserHeight > browserWidth;
if aEnabled and isLandscapeMode then
begin
Angle := 90;
Height := browserWidth;
Width := browserHeight;
end
else
begin
Angle := 0;
Height := browserHeight;
Width := browserWidth;
end;
end;
procedure TForm1.InitializeForm;
begin
inherited;
// this is a good place to initialize components
//Need to put a transform.orign for form rotation (Angle)
var x := trunc(Application.Display.ClientWidth / 2);
var myStyle := TInteger.ToPxStr(x) + ' ' + TInteger.ToPxStr(x);
w3_setStyle(Handle, w3_CSSPrefix('TransformOrigin'), myStyle);
end;
The simplest way would be to use the main form's With and Height.
Create a new "Visual project" and add an EditBox to the form.
Put this code into the "Resize" method:
Edit1.Text := Format('%d x %d', [Self.Width, Self.Height]);
If you, however, want to keep the main-form at a fixed size, you would need to read some other properties.
Self.Width := 250;
Self.Height := 250;
There are several ways to get these dimensions. Both the "window" and "document" DOM-element have some properties you can use:
window.innerWidth
document.documentElement.clientWidth
document.body.clientWidth
In Smart you can access these from the Application object:
(Add two more edit-boxes...)
W3EditBox2.Text := Format('%d x %d', [Application.Document.ClientWidth, Application.Document.ClientHeight]);
W3EditBox3.Text := Format('%d x %d', [Application.Display.ClientWidth, Application.Display.ClientHeight]);
It seems to be a bug in Application.Document.ClientHeight as it always returns 0...
Remember that the running code is JavaScript. You can find lots of useful information on the web.
It's very easy to transform these JS-snippets into Smart Pascal via an asm section.
Eg:
var
w,h: Integer;
begin
//Pure JavaScript below.
//The #-prefix maps the variable to a SmartPascal declared variable
asm
#w = window.innerWidth;
#h = window.innerHeight;
end;
W3EditBox4.Text := Format('%d x %d', [w, h]);
end;
Another trick would be to declare a Variant variable.
A Variant variable can hold a whole JavaScript object, and enable "late binding"-ish access to the members of the object:
var
v: Variant;
begin
//Grab the whole "document" element from the DOM
asm
#v = document;
end;
//Access all "document" members directly via the "v"-variable
W3EditBox5.Text := Format('%d x %d', [v.documentElement.clientWidth, v.documentElement.clientHeight]);
end;
Screenshot of the code-snippets above:

Polygon from collection of Latitudes & Longitudes

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 !