Godot/Gdscript rotate + translate from local to world space - transform

I have a RigidBody Node (3D), and I want to get, in global coordinates, the position of "target", given:
an angle "rot" (up is Y)
a Vector = 5 * Vector.FORWARD
It looks very easy.... first translate, then rotate, or if it's not working, first rotate, then rotate.
A to B -> If I only apply the translation, it works, no problem:
target = global_transform.translated(5 * Vector3.FORWARD).origin
But if I want B to C, it fails: it looks like it rotate around world origin
I tried many things during last few hours and read so many articles on Transform and now, I'm giving up. So... How would you do it?

We will define a position relative to your RigidBody(3D) by polar coordinates (namely a "rot" angle and a distance, I'll call it "d") on the XZ plane.
To be clear, we are going to define it in local space of the RigidBody(3D), and then move it to global coordinates.
We can do it using transforms. Which you would do if you need to preserve the rotation. Although it requires a little more work. Or, you can do it Using vectors only. Which is easier.
Below are the different approaches to do this.
Using Transforms
Local transform
We define combine the rotation and translation transformation like this:
var t = Transform.IDENTITY.rotated(Vector3.UP, rot).translated(d * Vector3.FORWARD)
By the way, the order does not matter when using these methods. The result is this either way:
var t = Transform.IDENTITY.rotated(Vector3.UP, rot) * Transform.IDENTITY.translated(d * Vector3.FORWARD)
Which gives a rotation around the origin at a given distance. However, if you do this:
var t = Transform.IDENTITY.translated(d * Vector3.FORWARD) * Transform.IDENTITY.rotated(Vector3.UP, rot)
You get a transform that is displaced forward, and then rotates in position.
Global transform
For the global transform, we can use the fact that this should be relative to the RidigBody(3D), and we have the global transform of it:
var t = Transform.IDENTITY.rotated(Vector3.UP, rot).translated(d * Vector3.FORWARD)
var global_t = global_transform * t
Be aware that this will apply rotation, scaling, and so on. If you only wanted the global translation, you can do it like this:
var t = Transform.IDENTITY.rotated(Vector3.UP, rot).translated(d * Vector3.FORWARD)
var global_t = Transform.IDENTITY.translated(global_transform.origin) * t
And no, t.translated(global_transform.origin) is not the same thing. When you use translated it is in the local coordinates of the transform on which you are calling it.
Local position
As you know, we can use the origin of the transform:
var t = Transform.IDENTITY.rotated(Vector3.UP, rot).translated(d * Vector3.FORWARD)
var v = t.origin
Global position
Either you can use to_global:
var t = Transform.IDENTITY.rotated(Vector3.UP, rot).translated(d * Vector3.FORWARD)
var v = t.origin
var global_v = to_global(v)
Or we can take origin from the global transform:
var t = Transform.IDENTITY.rotated(Vector3.UP, rot).translated(d * Vector3.FORWARD)
var global_t = global_transform * t
var global_v = global_t.origin
If you only want the global translation, you can add global_transform.origin:
var t = Transform.IDENTITY.rotated(Vector3.UP, rot).translated(d * Vector3.FORWARD)
var v = t.origin
var global_v = global_transform.origin + v
Using Vectors Only
Local position
If you only care about the position, we can do this without the transforms altogether:
var v = (d * Vector3.FORWARD).rotated(Vector3.UP, rot)
Global position
And, of course, you can use to_global:
var v = (d * Vector3.FORWARD).rotated(Vector3.UP, rot)
var global_v = to_global(v)
And again, if you only want the global translation, you can add global_transform.origin:
var v = (d * Vector3.FORWARD).rotated(Vector3.UP, rot)
var global_v = global_transform.origing + v

After 1 more hour, I finally found something, but it's kind of ugly:
target = to_global(to_local(transform.translated(3 * Vector3.FORWARD).origin).rotated(Vector3.UP, rot))
If you have something better, please add your answer!

Related

I tried to shoot an object towards cursor position, but it has a problem

I'm a beginner in Godot I want to shoot an object that is already in the game towards where the cursor's position is, and the method I created for it works fine but depending on the distance between the object and the cursor the speed changes. can anyone please help me make the speed constant?
I used this:
func _process(_delta):
if Input.is_action_just_released("tap"):
var mousepos = get_viewport().get_mouse_position()
var ballpos = self.get_position()
var x = mousepos.x - ballpos.x
var y = mousepos.y - ballpos.y
velocity = Vector2(x,y)
You can normalize your vector, which gives you a vector of unit length:
velocity = Vector2(x,y).normalize()
And then scale it by the speed you want:
velocity = Vector2(x,y).normalize() * speed
Where speed is a previously defined variable or constant. Something like this will do:
var speed := 100.0
You, of course, will want to tweak the value. So perhaps you want to export it so you can set it form the inspector:
export var speed := 100.0
By the way, you can rewrite the code you have to this:
var mousepos = get_viewport().get_mouse_position()
var ballpos = self.get_position()
velocity = mousepos - ballpos
Adding the changes suggested above we have:
var mousepos = get_viewport().get_mouse_position()
var ballpos = self.get_position()
velocity = (mousepos - ballpos).normalize() * speed
Which you can rewrite to this:
var mousepos = get_viewport().get_mouse_position()
var ballpos = self.get_position()
velocity = ballpos.direction_to(mousepos) * speed

THREE.js BufferGeometry points animate individually around sphere and control speed

So im going nuts lol this seems to be super easy and I feel like im so close but I cant seem to pin point where im going wrong here.
.
Im trying to create an animated group of THREE.js BufferGeometry Points, and animate them around a sphere pattern INDIVIDUALLY at different speeds and have them start at different positions. I want them to each animate in a circular motion around the sphere pattern, not shoot randomly around like i have them now. They can BEGIN in any random spot but where they start, they should begin on a straight, normal circular pattern around the sphere. Also, my issue is figuring out how to SLOW THEM DOWN.
From what I understand, theta, is the angle which needs to b increased to rotate a particle around a sphere. So im kinda lost how to do that properly. below is my code and codepens, any advice is greatly appreciated and try to dumb it down for me on the math terminology as im super new to vector math but have been studying to try and learn some cool sh!t
Primarily, there are two main parts. The initial loop which does the initial drawing/placement of the particles and the second loop which udpdates them and is meant to move them forward in their circular future path around the sphere pattern. If thats not the correct way to go about this, please lmk lol.
Initial placement of particles in random places all along the outside of a spherical pattern and my update function is the same as of now although im sure the update function is the one that needs to change:
const t = clock.getElapsedTime();
let theta = 0, phi = 0;
for (let i = 0; i < 1000; i++) {
theta += 2 * Math.PI * Math.random();
phi += Math.acos(2 * Math.random() - 1);
const x = radius * Math.cos(theta) * Math.sin(phi)
const y = radius * Math.sin(theta) * Math.sin(phi)
const z = radius * Math.cos(phi)
positions.push(x, y, z);
sizes.push(Math.random()*100)
const hex = colorList[Math.round(Math.random() * colorList.length)]
const rgb = new THREE.Color(hex)
colors.push(rgb.r, rgb.g, rgb.b)
}
jsfiddle
The Vue tab in the fiddle has all the code.
Ive tried the above with a time constant added to theta and without and all the particles move about randomly around the sphere, but I cant figure out how to get the particles to moe in a smooth, slower, circular pattern around the sphere. Again, the initial random positions are fine, but the way they update and scatter around randomly is wrong, i know it has to do with the theta variable i just cant figure out what to do to make it right.
Ok, after what seems likes months, I FINALLY figured out how to INDIVIDUALLY rotate three.js points around a sphere at different speeds, from random starting positions.
THERE ARE LOTS OF EXAMPLES FOR OLD THREE.JS VERSIONS THAT USE THREE.GEOMETRY, BUT THIS USES THE NEW BUFFERGEOMETRY WITH THE LATEST THREE.JS VERSION, NOT SOME ANCIENT R86 VERSIO LIKE THE OTHER EXAMPLES!!!
This first part does the initial plotting of the points
const radius = 1.5
const vectors = []
let theta = 0; let phi = 0
for (let i = 0; i < 5000; i++) {
theta = 2 * Math.PI * Math.random()
phi = Math.acos(2 * Math.random() - 1)
const px = radius * Math.cos(theta) * Math.sin(phi)
const py = radius * Math.sin(theta) * Math.sin(phi)
const pz = radius * Math.cos(phi)
const vertex = new THREE.Vector3(px, py, pz)
vertex.delay = Date.now() + (particlesDelay * i)
vertex.rotationAxis = new THREE.Vector3(0, Math.random() * 2 - 1, Math.random() * 2 - 1)
vertex.rotationAxis.normalize()
vertex.rotationSpeed = Math.random() * 0.1
vectors.push(vertex)
positions.push(vertex.x, vertex.y, vertex.z)
sizes.push(Math.random() * 0.1)
const hex = colorList[Math.round(Math.random() * colorList.length)]
const rgb = new THREE.Color(hex)
colors.push(rgb.r, rgb.g, rgb.b)
}
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3).setUsage(THREE.DynamicDrawUsage))
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3))
geometry.setAttribute('size', new THREE.Float32BufferAttribute(sizes, 1))
const particles = new THREE.Points(geometry, shaderMaterial)
scene.add(particles)
This is the magic that updates the points around the sphere
const posAttribute = particles.geometry.getAttribute('position')
const ps = posAttribute.array
const updateParticles = () => {
// loop over vectors and animate around sphere
for (let i = 0; i < vectors.length; i++) {
const vector = vectors[i]
vector.applyAxisAngle(vector.rotationAxis, vector.rotationSpeed)
ps[i * 3] = vector.x
ps[i * 3 + 1] = vector.y
ps[i * 3 + 2] = vector.z
}
particles.geometry.attributes.position.needsUpdate = true
}

(Godot Engine) Replicating Line2D's joint functionality on the first + last points

I have created the following method to outline an 2D polygon using an Line2D node (in favor of _drawing because of texturing and round jointing capabilities of the Line2D node):
func Set_Polygon_Outline(_polygon_node: Node2D, _width: int = 5, _color: Color = Color.black, _texture: Texture = null) -> void:
if _polygon_node is Polygon2D:
var _polygon: PoolVector2Array = (_polygon_node as Polygon2D).polygon
if _polygon.size() >= 3:
# Line2D node setup
var _line_node: Line2D = null
var _line_name: String = str(_polygon_node.name, "_Line")
if not _polygon_node.has_node(_line_name):
_line_node = Line2D.new() ; _line_node.name = _line_name ; _polygon_node.add_child(_line_node)
else: _line_node = _polygon_node.get_node(_line_name) as Line2D
# Line2D properties setup
if _line_node != null:
_line_node.width = _width ; _line_node.default_color = _color ; _line_node.joint_mode = Line2D.LINE_JOINT_ROUND
if _texture != null:
_line_node.texture = _texture ; _line_node.texture_mode = Line2D.LINE_TEXTURE_STRETCH
var _points: PoolVector2Array = _polygon ; _points.append(_polygon[0]) ; _line_node.points = _points
How would it be possible to replicate the round point jointing on point 0 in the same way as the other points?
The result meets expectations except on the closing points (from 4 to 0)
One approach I have tried is appending an additional point (point 1) to the _points Array. While the un-textured one looks as desired, the textured variant is slightly off on the additional line because of the two superimposing textures with alpha values making it look "bolder".
Another (very unorthodox approach) is creating two polygons: one black and one with an blur shader, using the following method:
func Set_Polygon_Shadow(_polygon_node: Node2D, _size: float = 10.0, _color: Color = Color.black) -> void:
if _polygon_node is Polygon2D:
var _polygon: PoolVector2Array = (_polygon_node as Polygon2D).polygon
if _polygon.size() >= 3:
# Shadow Polygon node setup
var _shadow_name: String = str(_polygon_node.name, "_Shadow")
if not _polygon_node.has_node(_shadow_name):
var _shadow_node: Polygon2D = Polygon2D.new()
_shadow_node.polygon = Geometry.offset_polygon_2d(_polygon, _size).front() ; _shadow_node.color = _color
_shadow_node.show_behind_parent = true ; _polygon_node.add_child(_shadow_node)
# Blur Polygon node setup
var _blur_node: Polygon2D = Polygon2D.new()
_blur_node.polygon = Geometry.offset_polygon_2d(_polygon, _size * 2.0).front()
_blur_node.material = ShaderMaterial.new()
_blur_node.material.shader = preload("res://shaders/Shader_Blur.shader")
_blur_node.material.set_shader_param("Strength", 2.0)
_blur_node.show_behind_parent = true ; _polygon_node.add_child(_blur_node)
The shader code:
shader_type canvas_item;
uniform float Strength : hint_range(0.0, 5.0);
void fragment() {COLOR = textureLod(SCREEN_TEXTURE, SCREEN_UV, Strength);}
The result looks good but I can't possibly imagine using this approach on a large number of polygons.
Thank you kindly,
Mike.
If using a Line2D is a viable solution, except for fixing the loop, then let us fix the loop.
For a Line2D to loop seamless, even with transparency, it must close in a straight segment (not a corner). And have no end caps.
Thus, I suggest to move the first point, half way between its original position and the second point. Then you add at the end the original position of the first point, following by a copy of its moved position...
Like this:
# Let o be a copy of the original first point
var o = _points[0]
# Let m be the middle of the straight segment between the first and second points
var m = o + (_points[1] - o) * 0.5
_points[0] = m # The line now starts in m, we are moving the first point forth
_points.append(o) # Since we moved the first point forth, add its original position back
_points.append(m) # Add m at the end, so it loops, in the middle of a straight segment
That should result in a Line2D that loops seamlessly.

Combining two Matrix Transformations under the same Transformation with SVG

My current task is attempting to combine objects with similar matrices under the same transformation matrix. The two matrices will always have the first 4 digits of it's transform be equal. I am having difficulty calculating the x="???" and y="???" for the second tspan. Any help towards the proper equation would be greatly appreciated.
Input
<svg>
<text transform="matrix(0 1 1 0 100 100)"><tspan x=0 y=0>foo</tspan></text>
<text transform="matrix(0 1 1 0 110 110)"><tspan x=0 y=0>bar</tspan></text>
</svg>
Output
<svg>
<text transform="matrix(0 1 1 0 100 100)">
<tspan x="0" y="0">foo</tspan>
<tspan x="???" y="???">bar</tspan>
</text>
</svg>
EDIT 1
I guess my question is more along the lines of given a point (x,y), how do I apply an existing matrix transformation to that point so that the position will not move, but the element will now be nested inside of another element.
EDIT 2
I have got this code to work for matrices with 0s in the (a,d) or (b,c) positions. Slanted/Skewed matrices I still have not got working. Any thoughts on this?
var aX = floatX[0];
var bX = floatX[1];
var cX = floatX[2];
var dX = floatX[3];
var eX = floatX[4];
var fX = floatX[5];
var aY = floatY[0];
var bY = floatY[1];
var cY = floatY[2];
var dY = floatY[3];
var eY = floatY[4];
var fY = floatY[5];
var xX = (eX * aX) + (fX * bX);
var xY = (eX * cX) + (fX * dX);
var yX = (eY * aY) + (fY * bY);
var yY = (eY * cY) + (fY * dY);
var c1 = cX - aX;
var c2 = dX + bX;
return new float[] { (yX - xX) / (c1 * c2), (yY - xY) / (c1 * c2) };
One thought that may work if my logic isn't flawed, is to find the transform for one element to the other, which can then be used to transform a point of 0,0 (as that's the original x,y) to a new location.
Once we know what the difference in transforms is (assuming that the first 4 figures in the matrix are the same as mentioned in the question, it won't work otherwise), we can figure what the difference in x,y is.
First, there's a bit of code as some browsers have removed this feature..
SVGElement.prototype.getTransformToElement = SVGElement.prototype.getTransformToElement || function(elem) {
return elem.getScreenCTM().inverse().multiply(this.getScreenCTM());
};
This is an svg method that some browsers support, but including as a polyfill in case yours doesn't (like Chrome). It finds the transform from one element to another.
We can then use this, to find the transform from the first to the second text element.
var text1 = document.querySelector('#myText1')
var text2 = document.querySelector('#myText2')
var transform = text2.getTransformToElement( text1 )
Or if you don't want the polyfill, this 'may' work (matrices aren't a strong point of mine!). getCTM() gets the current transformation matrix of an element.
var transform = text1.getCTM().inverse().multiply( text2.getCTM() )
Now we know what the transform between them was. We also know the original x,y was 0,0. So we can create an svg point 0,0 and then transform it with that matrix we've just figured, to find the new x,y.
var pt = document.querySelector('svg').createSVGPoint();
pt.x = 0; pt.y = 0;
var npt = pt.matrixTransform( transform );
Then just a delayed example to show it being moved. Set the tspan with the new x,y we've just figured from the previous transform.
setTimeout( function() {
alert('new x,y is ' + npt.x + ',' + npt.y)
tspan2.setAttribute('x', npt.x);
tspan2.setAttribute('y', npt.y);
},2000);
jsfiddle with polyfill
jsfiddle without polyfill

How we calculate distance between two co-ordinate. ArangoDB

I have a Users table that contains latitude and longitude attribute for every user. So I need to calculate the distance between two users in AQL Query.
I have done the same in Orientdb with the below query.
var laltitude = CURRENT_USER_laltitude;
var longitude = CURRENT_USER_longitude;
var query = "select distance(latitude, longitude,"+laltitude+","+longitude+") as distance from users";
First, create a js file distance.js (or whatever you want to name it) and put below code as below.
/* distance.js */
'use strict';
function gdistance(latitude1, longitude1, latitude2, longitude2, radius) {
if (!latitude1 || !longitude1 || !latitude2 || !longitude2) {
return null;
};
var lat1 = Number(latitude1), lon1 = Number(longitude1);
var lat2 = Number(latitude2), lon2 = Number(longitude2);
radius = (radius === undefined) ? 6371e3 : Number(radius);
var R = radius;
var φ1 = (lat1 * Math.PI / 180), λ1 = (lon1 * Math.PI / 180);
var φ2 = (lat2 * Math.PI / 180), λ2 = (lon2 * Math.PI / 180);
var Δφ = φ2 - φ1;
var Δλ = λ2 - λ1;
var a = Math.sin(Δφ/2) * Math.sin(Δφ/2)
+ Math.cos(φ1) * Math.cos(φ2)
* Math.sin(Δλ/2) * Math.sin(Δλ/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c; // Meters
var d2 = d / 1000; // Meters to KM
return d2;
}
module.exports = gdistance;
Now open Arango Console with arangosh. This will open with _system database by default. So if you have other than this database like me then use db._useDatabase("myDatabase") command to change database.
Now write below commands to add custom to your desired database.
Version 2.8
db._useDatabase("myDatabase");
var aqlfunctions = require("org/arangodb/aql/functions");
var f = require("/path/to/file/distance.js");
aqlfunctions.register("geo::gdistance", f, true)
Version 3.0+
db._useDatabase("myDatabase");
var aqlfunctions = require("#arangodb/aql/functions");
var f = require("/path/to/distance.js");
i.e.
var f = require("/home/ubuntu/distance.js");
var f = require("distance.js");
# If you want to remove this group's UDFs (User defined functions)
# aqlfunctions.unregisterGroup("geo");
aqlfunctions.register("geo::gdistance", f, true);
Now use in your AQL queries as below.
LET distance = geo::gdistance(attrbute_name.latitude, attrbute_name.longitude, #your_latitude, #your_longitude)
For more references with here.
Currently ArangoDB can only give you distances when you use the Geo index to return you the distance of your search start to the point matching your condition:
FOR doc IN WITHIN(##collection, #lat, #long, #radius, #distanceAttributeName)
RETURN doc
You could however use a user defined AQL function to extend AQL. User defined functions are implemented in Javascript, which is luckily used by Chris Veness to explain howto calculate distances
With ArangoDB 3.0 we most probably will support the arithmetic operations to calculate this in AQL.
I will edit this post with more details and examples soon.

Resources