How do I rotate smoothly in Godot? - godot

Below is my code (ignore the input variable it is just for movement):
var input = Vector3()
if Input.is_action_pressed("move_forward") and Input.is_action_pressed("move_left"):
rotation_degrees.y = lerp_angle(rotation_degrees.y, atan2(1, -1), delta * 7)
input[2] = 1
input[0] = 1
elif Input.is_action_pressed("move_forward") and Input.is_action_pressed("move_right"):
rotation_degrees.y = lerp_angle(rotation_degrees.y, atan2(1, 1), delta * 7)
input[2] = 1
input[0] = -1
elif Input.is_action_pressed("move_backward") and Input.is_action_pressed("move_left"):
rotation_degrees.y = lerp_angle(rotation_degrees.y, atan2(-1, -1), delta * 7)
input[2] = -1
input[0] = 1
elif Input.is_action_pressed("move_backward") and Input.is_action_pressed("move_right"):
rotation_degrees.y = lerp_angle(rotation_degrees.y, atan2(-1, 1), delta * 7)
input[2] = -1
input[0] = -1
else:
if Input.is_action_pressed("move_forward"):
rotation_degrees.y = lerp_angle(rotation_degrees.y, atan2(1, 0), delta * 7)
input[2] = 1
if Input.is_action_pressed("move_backward"):
rotation_degrees.y = lerp_angle(rotation_degrees.y, atan2(-1, 0), delta * 7)
input[2] = -1
if Input.is_action_pressed("move_left"):
rotation_degrees.y = lerp_angle(rotation_degrees.y, atan2(0, -1), delta * 7)
input[0] = 1
if Input.is_action_pressed("move_right"):
rotation_degrees.y = lerp_angle(rotation_degrees.y, atan2(0, 1), delta * 7)
input[0] = -1
For some reason, the player is hardly moving at all. Before I just set rotation_degrees.y to the direction but that didn't make a smooth movement. Any help is appreciated!

Getting Input
We have a better way to get a vector from input, let us start there:
var input := Vector3(
Input.get_action_strength("move_left") - Input.get_action_strength("move_right"),
0,
Input.get_action_strength("move_forward") - Input.get_action_strength("move_backward")
)
Addendum: This is the order you had. However, notice that we usually thing of x growing to the right, and - in Godot - forward is negative z. So this is backwards, I don't know if that is intentional.
What is going on here is that get_action_strength gives us the "strength" of the input, which is a number between 0 and 1. It will be 0 or 1 for a digital input, but it could be a value in between for analog input.
Then to get a component of the vector I take the input that would make it positive and subtract the negative. That gives you a value in the range from -1 to 1. Which also means we don't get a unit vector, depending on what you are doing, you might or might not want to normalize it.
Next you want the angle on the xz plane. Well, if you just used a Vector2, that would be trivial. So, instead of the code above, let us use a Vector2:
var input := Vector2(
Input.get_action_strength("move_left") - Input.get_action_strength("move_right"),
Input.get_action_strength("move_forward") - Input.get_action_strength("move_backward")
)
By the way, in Godot 3.4+ and Godot 4.0+ you will be able to use Input.get_vector, which simplifies this further.
It should not be hard to get a Vector3 from that if you need it for something else: Vector3(input.x, 0, input.y)
And now get the angle:
var input_angle := input.angle()
Addendum: You may need to do an operation on the angle to make it usable in 3D. The reason being that the angle is measured from the x axis, but no rotation is looking down the z. I had overlooked that when I wrote this answer. Although I tested all the code here. Without motion I didn't notice the orientation didn't match the direction of motion.
Smooth Rotation
Now, we want to smoothly change rotation_degrees.y to input_angle. There are multiple ways to do it, here are a few.
Before any of them. You are going to need to declare the target angle as a field, so the rotation does not reset when there is no input.
var _target_angle:float
And we are going to store it, whenever there is input:
var input := Vector2(
Input.get_action_strength("ui_left") - Input.get_action_strength("ui_right"),
Input.get_action_strength("ui_up") - Input.get_action_strength("ui_down")
)
if input.length_squared() > 0:
_target_angle = input.angle()
Note: I'm checking length_squared instead of length to avoid a square root.
lerp_angle
Yes, you should be able to use lerp_angle. Even thought, I don't advocate that.
I'll go ahead and add that 7 magic constant you have:
const _rotation_amount:float = 7.0
Then you would do it something like this (which I admit is convenient and short code):
rotation.y = lerp_angle(rotation.y, _target_angle, delta * _rotation_amount)
Please notice I changed rotation_degrees to rotation, that is because you get the angle in radians. Which would have been a problem using atan2 too. You can use deg2rad and rad2deg to do the conversions if you prefer.
With this approach, you have very little control over the interpolation. You do not specify how fast it rotates, nor how much time the rotation should take.
Please notice that the weight of a linear interpolation is not time. You are not telling Godot "go from this value to this value in this amount of time" (you can do that with a Tween, see below).
Also notice that it will advance a more or less stable proportion of the difference of the values each frame. Thus it moves more at the start, and less at the end. That is, this is not linear at all. It has a deceleration effect.
Angular speed
If we are going to work with angular speed, it follows that we need a field to hold the angular speed:
const _angular_speed:float = TAU
Here TAU (which is PI * 2) represent one rotation per second (four rotations per second would be 4 * TAU, and a quarter rotation per second could be 0.25 * TAU, you can associate TAU with one Turn as mnemonic).
Now, we are going to figure out the angle difference (shortest path), to do that we can do this:
var angle_diff := wrapf(_target_angle - rotation.y, -PI, PI)
The wrapf function will "wrap" the value in the range (-PI to PI in this case), so that if the values goes beyond that range, it is as if it entered from the opposite edge (if you say wrapf(14, 0, 10) you get 4, and if you say wrapf(-4, 0, 10) you get 6). I hope that makes sense.
To apply the angular speed, in theory, we would only need the sign:
rotation.y += delta * _angular_speed * sign(angle_diff)
However, we don't want to overshoot. We can solve this with clamp:
rotation.y += clamp(delta * _angular_speed, 0, abs(angle_diff)) * sign(angle_diff)
Notice I have separated the sign from the magnitude of angle_diff. I'm clamping how much the angle would change according to angular speed to the magnitude of angle_diff (so it is never more than angle_diff, i.e. it does not overshoot). Once clamped, I apply the sign so it turns in the correct direction.
And, of course, if you really wanted to, you could work with angular acceleration and deceleration too.
Tween
Using a Tween node is the most powerful option sans using an AnimationPlayer. And it is also very easy to use.
We, of course, need to define a Tween object and add it as a child:
var _tween:Tween
func _ready() -> void:
_tween = Tween.new()
add_child(_tween)
This is because the Tween object will continue to gradually change the rotation each frame (so we don't have to worry about it). And to do that, it needs to persist and to get frame notifications, so it needs to be in the scene tree somewhere.
In Godot 4.0+, you will be able to do get_tree().create_tween() which returns a rooted Tween, so you don't have to worry about keeping a reference or adding it to the scene tree.
And then you can do this:
var input := Vector2(
Input.get_action_strength("ui_left") - Input.get_action_strength("ui_right"),
Input.get_action_strength("ui_up") - Input.get_action_strength("ui_down")
)
if input.length_squared() > 0:
var rotation_time := 0.2
_tween.interpolate_property(self, "rotation:y", rotation.y, input.angle(), rotation_time)
_tween.start()
Do not forget to call start. Well, In Godot 4.0+, all tween will automatically start by default.
As you can see, the code is telling the Tween to interpolate the y of the rotation from the value of rotation.y (its current value) to the value of input.angle(), and we specify a rotation time (which I made a variable just so I can give it a name). And that's it.
In Godot 4.0+, interpolate_property is gone in favor of the new tween_property API, with which you don't need to specify the start value.
Oh, wait, we can specify how do you want to ease these values:
_tween.interpolate_property(
self,
"rotation:y",
rotation.y,
input.angle(),
rotation_time,
Tween.TRANS_QUAD,
Tween.EASE_OUT
)
See this cheatsheet by wandomPewlin to pick your easing:
I believe Tween.TRANS_QUAD and Tween.EASE_OUT gives you something close to what you get from lerp_angle, except stable. And look, you have more options!
I'll also mention that you can ask Tween what interpolation it currently is doing, and also remove them, which is useful in some cases. Yet, you don't need to go into that for simple cases like this one. And, well, there is AnimationPlayer if you need something more complex. By the way, yes, AnimationPlayer can operate on a subproperty (such as rotation.y, I have an explanation elsewhere).

Related

Get Rigidbody2D collision velocity without the sliding factor

I have a flying car that I want to lose some HP when colliding with objects.
I connected car's RigidBody2D to this function to do that
func _on_Car_body_entered(body):
var force = linear_velocity.length()
var dmg = pow(force / 100, 2) - 0.25
if dmg <= 0: return
Health = Health - dmg
Now, since I don't have to be precise I'm just using current velocity as the force, though this is up for change.
After getting my 'force of impact', I put it into damage calculating formula and if damage is above 0, decrease HP by damage.
This works fine in most cases
BUT
I noticed that if car's going fast horizontally and just barely touch the ground (that's perfectly horizontal), car gets hit with a lot of damage, because I'm using the length of the velocity vector.
Ofcourse, this case can be managed by using just the Y component of the velocity vector, but then it removes any horizontal collisions, and vice versa, and it also leads me on to the path of programming vertical and horizontal collisions, and ofcourse those are not the only 2 directions of colisions I need.
Is there a way to remove the sliding factor from this equation?
You can get the sine of the angle between your velocity and the collision normal, and then take the absolute of that.
# 0 When sliding along the wall. 1 when hitting the wall head on
var slide_factor = abs(cos(vel_last_frame.angle_to(collision_normal)))
This will give you a value from 0 to 1. When you are just sliding along the wall, this value will be 0, and when you hit the wall straight on, it will be 1.
I am using the velocity from the last frame here so that it gets the velocity just before the collision. I get it by setting vel_last_frame to linear_velocity inside the _physics_process function.
You can only get the collision normal inside the _integrate_forces function using PhysicsDirectBodyState.get_local_contact_normal(), so you need to make a variable that can be accessed in this function and the _on_Car_body_entered function. Note that you need to set contact_monitor to true and contacts_reported to at least 1 for this function to work.
var collision_normal
func _integrate_forces(state):
# Check if there is a collision
if state.get_contact_count():
# contact_monitor must be true and contacts_reported must be at least 1 for this to work
collision_normal = state.get_contact_local_normal(0)
Now in the _on_Car_body_entered_function, you can multiply dmg by sliding_factor to scale it less depending on how much you are sliding against the wall.
func _on_Car_body_entered(body):
var force = linear_velocity.length()
# 0 When sliding along the wall. 1 when hitting the wall head on
var slide_factor = abs(cos(vel_last_frame.angle_to(collision_normal)))
var dmg = pow(force / 100, 2) - 0.25
# Reduce dmg depending on how much you are sliding against the wall
dmg *= slide_factor
if dmg <= 0: return
Health = Health - dmg
Found a solution for my problem here
This gives me a predictable force range to work with.
I copied all code for 2D collision, just added damage calculation
Range of forces my objects produce is <3000 for small collisions like scratches and bumps, ~10k for beginner friendly damage, and 20k+ for when I really slam the gas pedal, so I just convert that force to damage that I want.
Best part is that I don't have to use the body_entered from RigidBody2D, because now all my cars have this calculation in them, so when 2 of them collide they both get damaged.
extends RigidBody2D
var collision_force : Vector2 = Vector2.ZERO
var previous_linear_velocity : Vector2 = Vector2.ZERO
func _integrate_forces(state : Physics2DDirectBodyState)->void:
collision_force = Vector2.ZERO
if state.get_contact_count() > 0:
var dv : Vector2 = state.linear_velocity - previous_linear_velocity
collision_force = dv / (state.inverse_mass * state.step)
var dmg = collision_force.length() / 2000 - 2
if dmg > 0:
set_hp(Health - dmg)
emit_signal("Damaged")
previous_linear_velocity = state.linear_velocity
**OLD ANSWER**
RUBBER DUCK HERE
In script for car I added a new variable var last_linear_velocity = Vector2()
Then stored the last velocity in _process
func _process(delta):
last_linear_velocity = linear_velocity
Not in _integrate_forces because if I put it there then the new and last velocities are the same.
And just changed how force is calculated in the function mentioned above, so it looks like this
func _on_Car_body_entered(body):
var force = last_linear_velocity.length() - linear_velocity.length()
var dmg = pow(force / 100, 2) - 0.25
if dmg <= 0: return
Health = Health - dmg
Now I get a nice predicable range of values and can transform that to damage.
NOTE
I noticed that sometimes when collision occures the difference between the last and current velocity lengths is negative, as in - car is accelerating.
Anyway, this works for me for now.
If you find a better solutions do post it, as I couldn't find a solution to this problem online elswhere.

mutual visibility of nodes on a grid

I have a simple grid, and I need check two nodes for mutual visibility. All walls and nodes coordinations is known. I need check two nodes for mutual visibility.
I have tried use vectors, but I didn't get acceptable result. This algorithm works, but it bad fit in my program, because of this i must do transformations of data to get acceptable result.
I used this code for check nodes for mutual visibility:
def finding_vector_grid(start, goal):
distance = [start[0]-goal[0], start[1]-goal[1]]
norm = math.sqrt(distance[0] ** 2 + distance[1] ** 2)
if norm == 0: return [1, 1]
direction = [(distance[0]/norm), (distance[1]/norm)]
return direction
def finding_vector_path(start, goal):
path = [start]
direction = finding_vector_grid((start[0]*cell_width, start[1]*cell_height),
(goal[0]*cell_width, goal[1]*cell_height))
x, y = start[0]*cell_width, start[1]*cell_height
point = start
while True:
if point not in path and in_map(point):
path.append(point)
elif not in_map(point):
break
x -= direction[0]
y -= direction[1]
point = (x//cell_width, y//cell_height)
return path
def vector_obstacles_clean(path, obstacles):
result = []
for node in path:
if node in obstacles:
result.append(node)
break
result.append(node)
return result
for example:
path = finding_vector_path((0, 0), (0, 5))
path = vector_obstacles_clean(path, [(0, 3)])
in_map - check if point not abroad map frontiers;
start, goal - tuples width x and y coords;
cell_width, cell_height - int variables with node width and height in pixels (I use pygame for visualization graph).
I have not any problems with this method, but it works not with graphs, it works "by itself", it not quite the that I need to. I am not good at English, please forgive me :)
The code you posted seems perfectly nice,
and your question doesn't clarify what needs improving.
Rather than doing FP arithmetic on vectors,
you might prefer to increment an integer X or Y pointer
one pixel at a time.
Consider using Bresenham's line algorithm,
which enumerates pixels in the line of sight
between start and goal.
The key observation is that for a given slope
it notices whether X or Y will increment faster,
and loops on that index.

Is there an alternative way to convert points from the axial to the projected plane in VB6?

(Please note that I have also posted this to the Visual Basic 6 Programming list on Yahoo! Groups but that seems to have been dead since 2017 so I thought I would post here as well.)
I've recently taken over maintaining a VB6 program at work that has a routine to convert data points from an axial plane to the projected plane given the axial point, a lead measurement, projection angle, vector (initial angle, final angle, gradient) and the projected point. It is written as a function that returns a Boolean to indicate whether it was successful.
It has to calculate the rotation angle from the axial plane to do this and uses the Newton-Raphson iteration method to determine the value of this angle. It sets an upper limit of 30 iterations in which to do this, but for a certain set of parameters, it's unable to find a solution within those 30 iterations.
Does anyone know of an alternative method to perform this conversion please? This program is used to drive a CNC machine with the data points calculated and I believe that these parameters are valid it's just the method used that's preventing it from proceeding.
I have added logging output code to trace the problem and found that it is down to a conversion between the axial and projected planes with a specific upper value for the projection angle (it also uses +/- a margin on this value as part of the process).
The iteration code cannot find a solution that falls within the set tolerance within the set maximum number of tries (30).
I have tried increasing the maximum number of tries, reducing the increments, increasing the tolerance and also reducing the value of the margin applied, but none of what I have done so far works, or it duplicates the same error.
I expect the output to be an array of profile points that are on the projected plane and equivalent to the provided axial plane points using the provided lead measurement and projection angle but I am getting errors that indicate the iteration process failed to find a solution within the maximum number of 30 tries.
Edited to add:
I've written the following pseudocode based on the VB6 code to try and explain how the routine works:
'AxialPoint, ProjectedPoint: structures with ProfileRadius, ProfileWidth,
'NormalAngle, UnwrapAngle and Attribute fields
'Lead: lead measurement
'ProjectionAngle: projection angle onto the projected plane in degrees
'Vector(): provides initial value of Beta, Last Beta value found, Last
'gradient) - set by the calling routine as (0, 0, 0) in this case
'Beta is the angle of rotation from the axial plane
Function AxialToProjected(AxialPoint, Lead, ProjectionAngle, Vector(),
ProjectedPoint):
LeadAngle := Atn(Lead / (2 * PI * AxialPoint.ProfileRadius)) 'radians
SA := Sin(LeadAngle)
CA = Cos(LeadAngle)
CT := -CosD(AxialPoint.NormalAngle)
ST := SinD(AxialPoint.NormalAngle)
SG := SinD(ProjectionAngle)
CG := CosD(ProjectionAngle)
C1 := SA * CG * CT
C2 := CA * CG * ST
C3 := CA * SG * CT
Iteration := 0
FBeta := 0.0001
While (Abs(FBeta) >= 0.0001) And (Iteration < 9) Do
NewValue(Beta, OldBeta, FBeta, Increment:=0.1, Gradient, SVB(),
Iteration, Multiplier:=10)
SB := Sin(Beta)
CB := Cos(Beta)
OldFBeta := FBeta
FBeta := C1 * CB + C2 * SB - C3
If Iteration >= 2 Then
Gradient := (Beta - OldBeta) / (FBeta - OldFBeta)
End If
End While
If (Abs(FBeta) > 0.0001) Then
'The code reaches this point with the specific parameters
Report "Iteration failed to find a solution within 30 tries" error
Else
'The code doesn't reach this point
X3 := Beta * Lead / 2 * PI + AxialPoint.ProfileWidth
With ProjectedPoint Do
.ProfileWidth := X3 * CG - AxialPoint.ProfileRadius * SB * SG
.ProfileRadius := AxialPoint.ProfileRadius * CB
.NormalAngle := ATan2(CA * CB * CG * ST - SA * SB * CG * CT,
-CA * CT) * RadToDeg
.Attribute := AxialPoint.Attribute
.UnwrapAngle := AxialPoint.UnwrapAngle
End With
End If
End AxialToProjected()
SinD() and CosD() accept an angle in degrees and convert it to radians before passing that to the normal Sin() and Cos() functions.
The NewValue() routine calculates a new value for Beta from the value of OldBeta using FBeta, Increment, Gradient, SVB() and the Multiplier while incrementing the value of Iteration:
'Beta: current axial plane rotation angle estimate
'OldBeta: previous current axial plane rotation angle estimate
'FBeta: gradient of tangent to the normal
'Increment: increment size to adjust Beta by
'Gradient: gradient for the Newton method
NewValue(Beta, OldBeta, FBeta, Increment, Gradient, SVB(), Iteration,
Multiplier):
If Iteration > 2 Then
MaxChange := Abs(Increment * Multiplier)
Change := -FBeta * Gradient
If Abs(Change) > MaxChange Then
Beta := Beta + TransferSign(MaxChange, Change)
'TransferSign() will return -MaxChange if Change < 0 or
'MaxChange if Change >= 0
Else
Beta := Beta + Change
End If
ElseIf Iteration = 2 Then
If SVB(3) = 0 Then
Beta := SVB(1) + Increment
Else
Beta := -FBeta * SVB(3)
End If
Else
Beta := SVB(1) 'Set Beta to initial value
End If
End NewValue()
I'll get a diagram uploaded later as I need to work out how best to draw it from my scribblings :)
Just to add some extra context, the routine is being used to calculate points to machine ZA and ZN type worms using a CNC machine.
It's just 1 specific set of parameters for each type that cause the error to occur and I believe that it's being caused by the initial estimate being too far away from the root to be resolved within the 30 try limit but I'm not sure how to work out a better initial estimate.
Edited to add:
Apologies for not returning to the thread sooner as I've been looking into this issue from other angles, but I have managed to put together a simple diagram showing how these planes are related so I hope this helps:
Coordinate Planes
When I was looking into the calculation of FBeta in the code, I found something useful in my internet searches based on Pythagoras' Theorem:
|C3| \le \sqrt{C1^2+C2^2} - the Newton iteration loop will be able to find a solution
|C3| > \sqrt{C1^2+C2^2} - the Newton iteration loop will not be able to find a solution
I've modified the code to perform this check at the start and only enter the Newton loop if equation (1) holds true, otherwise, it currently reports an error, but I'd like to find an alternative method to find a solution.

How can I fix the wrong deceleration when moving to left?

I just started learning game development in pygame and I want the player object to have a deceleration when the player stops pressing the key.
This is what I have at the moment:
def update(self):
self.accel_x = 0
keys = pg.key.get_pressed()
if keys[pg.K_LEFT]:
self.accel_x = -0.2
if keys[pg.K_RIGHT]:
self.accel_x = 0.2
if abs(self.vx) >= max_speed:
self.vx = self.vx/abs(self.vx) * max_speed
if self.accel_x == 0:
self.vx *= 0.91
self.vx += self.accel_x
self.vy += self.accel_y
self.rect.x += self.vx
self.rect.y += self.vy
It's works fine while moving to right but the object doesn't stop on time while going to left. Instead it decelerates to a point and then keeps going with a really slow speed for some time, then stops.
First, let see the math behind the algorithm.
When the button is pressed, the speed and position change based on the acceleration a, at t (number of times the function run), initial values being v0 and x0
v = v0 + a * t
x = x0 + Σ(i=1 to t) i * a
or
x = x0 + (t2+t) * a/2
And when the button is released (accel is 0) the speed v decreases geometrically
v = v0 * 0.91t
after 10 calls, we have ~0.39 v, after 100 calls ~10-5 v. Meaning that, visually, the position x decelerates and stops, v being too small to make a difference after some time.
The math is consistent with what is seen in games.
The question is why that algorithm doesn't work left side.
While it should work the same, left and right.
The difference is, left side,
speed v more likely to be negative after LEFT was pressed
position x might become negative at some point (and has to be checked)
Since the code provided (probably) does not cover the part to be changed, some recommendations:
You could force the speed to 0 if abs(v) is less than, say, 10-5 or another small values from which the position doesn't change visually (less than a pixel).
Ensure x values are checked at the limit, especially for negative values.
Debug: display/log v and x values especially after LEFT is released. This way when the whole program is running you'll identify more easily when does the problem come from.
If that doesn't address your problem, you could edit your question and add more relevant code.

Finding the original position of a point on an image after rotation

I have the x, y co-ordinates of a point on a rotated image by certain angle. I want to find the co-ordinates of the same point in the original, non-rotated image.
Please check the first image which is simpler:
UPDATED image, SIMPLIFIED:
OLD image:
Let's say the first point is A, the second is B and the last is C. I assume you have the rotation matrice R (see Wikipedia Rotation Matrix if not) et the translation vector t, so that B = R*A and C = B+t.
It comes C = R*A + t, and so A = R^1*(C-t).
Edit: If you only need the non rotated new point, simply do D = R^-1*C.
First thing to do is defining the reference system (how "where the points lies with respect to each image" will be translated into numbers). I guess that you want to rely on a basic 2D reference system, given by a single point (a couple of X/Y values). For example: left/lower corner (min. X and min. Y).
The algorithm is pretty straightforward:
Getting the new defining reference point associated with the
rotated shape (min. X and min. Y), that is, determining RefX_new and
RefY_new.
Applying a basic conversion between reference systems:
X_old = X_new + (RefX_new - RefX_old)
Y_old = Y_new + (RefY_new -
RefY_old)
----------------- UPDATE TO RELATE FORMULAE TO NEW CAR PIC
RefX_old = min X value of the CarFrame before being rotated.
RefY_old = max Y value of the CarFrame before being rotated.
RefX_new = min X value of the CarFrame after being rotated.
RefY_new = max Y value of the CarFrame after being rotated.
X_new = X of the point with respect to the CarFrame after being rotated. For example: if RefX_new = 5 with respect to absolute frame (0,0) and X of the point with respect to this absolute frame is 8, X_new would be 3.
Y_new = Y of the point with respect to CarFrame after being rotated (equivalently to point above)
X_old_C = X_new_C(respect to CarFrame) + (RefX_new(CarFrame_C) - RefX_old(CarFrame_A))
Y_old_C = Y_new_C(respect to CarFrame) + (RefY_new(CarFrame_C) - RefY_old(CarFrame_A))
These coordinates are respect to the CarFrame and thus you might have to update them with respect to the absolute frame (0,0, I guess), as explained above, that is:
X_old_D_absolute_frame = X_old_C + (RefX_new(CarFrame_C) + RefX_global(i.e., 0))
Y_old_D_absolute_frame = Y_old_C + (RefY_new(CarFrame_C) + RefY_global(i.e., 0))
(Although you should do that once the CarFrame is in its "definitive position" with respect to the global frame, that is, on picture D (the point has the same coordinates with respect to the CarFrame in both picture C and D, but different ones with respect to the global frame).)
It might seem a bit complex put in this way; but it is really simple. You have just to think carefully about one case and create the algorithm performing all the actions. The idea is extremely simple: if I am on 8 inside something which starts in 5; I am on 3 with respect to the container.
------------ UPDATE IN THE METHODOLOGY
As said in the comment, these last pictures prove that the originally-proposed calculation of reference (max. Y/min. X) is not right: it shouldn't be the max./min. values of the carFrame but the minimum distances to the closer sides (= perpendicular line from the left/bottom side to the point).
------------ TRIGONOMETRIC CALCS FOR THE SPECIFIC EXAMPLE
The algorithm proposed is the one you should apply in any situation. Although in this specific case, the most difficult part is not moving from one reference system to the other, but defining the reference point in the rotated system. Once this is done, the application to the non-rotated case is immediate.
Here you have some calcs to perform this action (I have done it pretty quickly, thus better take it as an orientation and do it by your own); also I have only considered the case in the pictures, that is, rotation over the left/bottom point:
X_rotated = dx * Cos(alpha)
where dx = X_orig - (max_Y_CarFrame - Y_Orig) * Tan(alpha)
Y_rotated = dy * Cos(alpha)
where dy = Y_orig - X_orig * Tan(alpha)
NOTE: (max_Y_CarFrame - Y_Orig) in dx and X_orig in dy expect that the basic reference system is 0,0 (min. X and min. Y). If this is not the case, you would have to change this variables.
The X_rotated and Y_rotated give the perpendicular distance from the point to the closest side of the carFrame (respectively, left and bottom side). By applying these formulae (I insist: analyse them carefully), you get the X_old_D_absolute_frame/Y_old_D_absolute_frame that is, you have just to add the lef/bottom values from the carFrame (if it is located in 0,0, these would be the final values).

Resources