Calculating the 2D turn movement required given an incoming and outgoing direction - kotlin

Consider a 2D square tiled grid (chess board like) which contains conveyor belt like structures that can curve and move game pieces around.
I need to calculate the turn movement (TURN_LEFT, TURN_RIGHT or STAY), depending on
the direction from which a piece moves onto the field
the direction from which the underlying belt exits the field
Example:
1 2
1 |>X>|>v |
2 | | v |
The belt makes a RIGHT turn. As such, the result of calcTurn(LEFT, DOWN) should be TURN_RIGHT. Meaning the X game piece will be rotated 90° right when it moves over the curve at (1,2).
I already implemented a function but it only works on some of my test cases.
enum class Direction {
NONE,
UP,
RIGHT,
DOWN,
LEFT;
fun isOpposite(other: Direction) = this == UP && other == DOWN
|| this == DOWN && other == UP
|| this == LEFT && other == RIGHT
|| this == RIGHT && other == LEFT
}
data class Vec2(val x: Float, val y: Float)
fun Direction.toVec2() = when (this) {
Direction.NONE -> Vec2(0f, 0f)
Direction.UP -> Vec2(0f, 1f)
Direction.RIGHT -> Vec2(1f, 0f)
Direction.DOWN -> Vec2(0f, -1f)
Direction.LEFT -> Vec2(-1f, 0f)
}
fun getTurnMovement(incomingDirection: Direction, outgoingDirection: Direction): Movement {
if (incomingDirection.isOpposite(outgoingDirection) || incomingDirection == outgoingDirection) {
return Movement.STAY
}
val incVec = incomingDirection.toVec2()
val outVec = outgoingDirection.toVec2()
val angle = atan2(
incVec.x * outVec.x - incVec.y * outVec.y,
incVec.x * outVec.x + incVec.y * outVec.y
)
return when {
angle < 0 -> Movement.TURN_RIGHT
angle > 0 -> Movement.TURN_LEFT
else -> Movement.STAY
}
}
I can't quite figure out what's going wrong here, especially not because some test cases work (like DOWN+LEFT=TURN_LEFT) but others don't (like DOWN+RIGHT=STAY instead of TURN_LEFT)

You're trying to calculate the angle between two two-dimensional vectors, but are doing so incorrectly.
Mathematically, given two vectors (x1,y1) and (x2,y2), the angle between them is the angle of the second to the x-axis minus the angle of the first to the x-axis. In equation form: arctan(y2/x2) - arctan(y1/x1).
Translating that to Kotlin, you should instead use:
val angle = atan2(outVec.y, outVec.x) - atan2(incVec.y, incVec.x)
I'd note that you could achieve also your overall goal by just delineating the cases in a when statement as you only have a small number of possible directions, but perhaps you want a more general solution.

It's not answering your question of why your code isn't working, but here's another general approach you could use for wrapping ordered data like this:
enum class Direction {
UP, RIGHT, DOWN, LEFT;
companion object {
// storing thing means you only need to generate the array once
private val directions = values()
private fun getPositionWrapped(pos: Int) = directions[(pos).mod(directions.size)]
}
// using getters here as a general example
val toLeft get() = getPositionWrapped(ordinal - 1)
val toRight get() = getPositionWrapped(ordinal + 1)
val opposite get() = getPositionWrapped(ordinal + 2)
}
It's taking advantage of the fact enums are ordered, with an ordinal property to pull out the position of a particular constant. It also uses the (x).mod(y) trick where if x is negative, putting it in parentheses makes it wrap around
x| 6 5 4 3 2 1 0 -1 -2 -3 -4 -5
mod 4| 2 1 0 3 2 1 0 3 2 1 0 3
which makes it easy to grab the next or previous (or however far you want to jump) index, acting like a circular array.
Since you have a NONE value in your example (which obviously doesn't fit into this pattern) I'd probably represent that with a null Direction? instead, since it's more of a lack of a value than an actual type of direction. Depends what you're doing of course!

Related

Problem with simple rectangle collison in OpenGL

I try to implement collision detection and collision effects. This is my code for collision detection:
fun collisionDetection(objcect : Renderable?, object2 : Renderable?) : Boolean {
var collisionX : Boolean = objcect?.getPosition()?.x?.plus(quadSizeX)!! >= object2?.getPosition()?.x!! && object2?.getPosition()?.x?.plus(quadSizeX)!! >= objcect?.getPosition()?.x!!
var collisionY : Boolean = objcect?.getPosition()?.y?.plus(quadSizeY)!! >= object2?.getPosition()?.y!! && object2?.getPosition()?.y?.plus(quadSizeY)!! >= objcect?.getPosition()?.y!!
var collisionZ : Boolean = objcect?.getPosition()?.z?.plus(quadSizeZ)!! >= object2?.getPosition()?.z!! && object2?.getPosition()?.z?.plus(quadSizeZ)!! >= objcect?.getPosition()?.z!!
return collisionX && collisionY && collisionZ }
This seems to work, when used with overlapping objects set to overlapping values by transformations. This way
wall.translateLocal(Vector3f(0.0f, 1.0f, -8.0f))
and
wall2.translateLocal(Vector3f(0.0f, 2.0f, -8.0f))
collisionDetection is true.
When moving wall2 on the z-axis with a different starting point and speed, but x and y position are the same, the effect of the collision should be that wall2 stops moving, when z position of both objects is equal. I tried it this way in the main loop (and many other ways...), but it is still not working:
fun render(dt: Float, t: Float) {
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
staticShader.use()
camera.bind(staticShader)
wall.render(staticShader)
wall2.render(staticShader)
if (collisionDetection(wall, wall2) == true) {
wall2.getPosition()
}
else {wall2.translateLocal(Vector3f(0.0f, 0.0f, -t.toFloat()* 0.001f))}
}
I have no idea now, what is wrong and how to fix it. It seems like the collisionDetection function doesn't get information about the changing positions on the z-axis of the wall2. Or maybe I need a different approach.
Everything works fine now. Forgot to calculate QuadSize for the second object. This results in two objects on the same z.coordinate.Nothing has been wrong with the function for detecting collisions.

Floating Range with additional number in Kotlin

I make Wind direction enum and I want to make a range that catches all values between 2 numbers OR third one:
number between (0.0 and 45.0) OR (360.0)
enum class WindDirections(val description: String,val shortName : String, val range: ClosedFloatingPointRange<Double>,val image: Int) {
// North
NORTH("N North", "N" ,(0.0..45.0), R.drawable.wind_north_red50),
NORTH_EAST("NE North-East", "NE" ,45.1..90.0, R.drawable.wind_north_east_red),
(... East, South, West etc)
NOT_FOUND ("", "-",-1.1..-1.0,0); // error one, now 360* is inside that
(...)
With these functions, I want to get enum class - WindDirections with the right value, based on given angle windDirection: Double. For example:
The angle between 0 and 45 (0.0..45.0) returns WindDirections.North (Now want to make when its 360 to return same)
The angle between 45.1 and 90 (45.1..90.0) returns WindDirections.NORTH_EAST
companion object {
fun getDescriptionStringByDouble(windDirection: Double): String {
return getWindDirectionByDouble(windDirection).description
}
fun getShortDescriptionStringByDouble(windDirection: Double):String {
return getWindDirectionByDouble(windDirection).shortName
}
I want to get North for a range of 0..45 and also 360
fun getWindDirectionByDouble(windDirection: Double): WindDirections {
return when (windDirection) {
in NORTH.range -> NORTH // number between (0.0 and 45.0) OR (360.0 need to accomplish that one, now it isn't)
in NORTH_EAST.range -> NORTH_EAST // 45 and 90
(... East, South, West etc)
else -> NOT_FOUND // when angle is not between 0 and 359.9 (for now 360 is here)
Thank you in advance
EDIT:
The way to accomplish that is to make when catching my special case for 360 in function getWindDirectionByDouble() like that in bellow, but I ask, is it possible to add 360 in the range of NORTH somehow. "(0.0..45.0) and (360.0)"
fun getWindDirectionByDouble(windDirection: Double): WindDirections {
return when (windDirection) {
360.0 -> NORTH // Like that one, i will return right WindDirections.NORTH
in NORTH.range -> NORTH // number between (0.0 and 45.0) OR (360.0 need to accomplish that one, now it isn't)
in NORTH_EAST.range -> NORTH_EAST // 45 and 90
(... East, South, West etc)
else -> NOT_FOUND
There is a flaw in using a Double ClosedRange for this since it treats the start and end inclusively. You worked around that by using 45.1 for example, but then there are numbers like 45.05 that won't be in any of the ranges.
You could make your own type of range that treats the end as exclusive. And you could also design it to handle angles outside 0-360 by calculating the positive remainder (modulo).
data class AngleRange(val start: Double, val endExclusive: Double) {
operator fun contains(value: Double) =
((value % 360.0 + 360.0) % 360.0).let { it >= start && it < endExclusive }
}
Then you can fully cover the possible ranges using AngleRange(0.0, 45.0), AngleRange(45.0, 90.0), etc.

Find 10 nearest points from an array of coordinates for each coordinate in Kotlin

I have an array
var poses = arrayOf<Array<Double>>()
That I populate using a loop.
The output looks something like this:
poses.forEach {
println(Arrays.toString(it))
}
[-71.42510166478651, 106.43593221597114]
[104.46430594348055, 78.62761919208839]
[100.27031925094859, 79.65568893000942]
[311.2433803626159, 233.67219485640456]
[330.3015877764689, -114.9000129699181]
[34.76986782382592, -383.71914014833436]
[355.477931403836, -173.29388985868835]
[322.72821807215564, -45.99138725647516]
...
Is there an efficient way to find 10 nearest points from this list for each coordinate?
For example:
Find 10 nearest points for [-71.42510166478651, 106.43593221597114], then [104.46430594348055, 78.62761919208839] and so on.
I tried looking into numpy-like libraries for Kotlin but seeing as though I'm new to the language I couldn't figure out how to do it.
You can write a distance function with the Pythagorean theorem. (This GeeksforGeeks page might be helpful too.)
You could also use a data class for the points, instead of using an array with two double values. The code below uses the approach that Mateen Ulhaq suggested in his comment, with two modifications:
The addition of "point to" lets us create a map from a point to the ten nearest points (so we know which point the ten points are related to).
The call to ".drop(1)" before ".take(10)" keeps the point itself out of its list (since the distance to itself is 0).
This code uses a list of points, determines the nearest points and prints them for each point:
fun main() {
val poses = listOf(
Point(-71.42510166478651, 106.43593221597114),
Point(104.46430594348055, 78.62761919208839),
Point(100.27031925094859, 79.65568893000942),
Point(311.2433803626159, 233.67219485640456),
Point(330.3015877764689, -114.9000129699181),
Point(34.76986782382592, -383.71914014833436),
Point(355.477931403836, -173.29388985868835),
Point(322.72821807215564, -45.99138725647516)
)
val nearestPoints = poses.map {
point -> point to poses.sortedBy { point.distance(it) }.drop(1).take(10)
}
println("Nearest points:")
nearestPoints.forEach {
println("${it.first} is closest to ${it.second}")
}
}
data class Point(val x: Double, val y: Double) {
fun distance(that: Point): Double {
val distanceX = this.x - that.x
val distanceY = this.y - that.y
return sqrt(distanceX * distanceX + distanceY * distanceY)
}
}
If the points are evenly (or almost evenly) distributed in some area, I suggest dividing them into rectangular chunks with size area.size.x / poses.size * 10 by area.size.y / poses.size * 10.
Then to find the nearest points for any point, you only need to check neighboring chunks. Since points are evenly distributed, you can find the nearest points for all points in O(kn) where n is a number of points and k = 10.
If the points are not guaranteed to be evenly (or almost evenly) distributed, you have to divide the area into several chunks and then recursively repeat the same process for each chunk until all the sub-chunks contain at most x points. (It's hard to tell what is optimal x and optimal count of sub-chunks per chunk, you need to do some research to find it out).
Then you can find the nearest points for any point, just as you did with evenly distributed points.
A few tricks to improve performance:
Use distanceSquared instead of distance. Here is how you can implement distanceSquared:
fun Point.distanceSquared(other: Point) = (x - other.x).squared() + (y - other.y).squared()
typealias Point = Array<Double>
val Point.x get() = this[0]
val Point.y get() = this[1]
fun Double.squared() = this * this
Use PriorityQueue<Point>(10, compareBy { -it.distanceSquared(destination) }) to store nearest points, and offer(point, 10) to add points to it:
fun <E : Any> PriorityQueue<E>.offer(element: E, maxSize: Int) {
if (size < maxSize) offer(element)
else if (compare(element, peek()) > 0) {
poll()
offer(element)
}
}
// if `comparator()` returns `null` queue uses naturalOrder and `E` is `Comparable<E>`
#Suppress("UNCHECKED_CAST")
fun <E : Any> PriorityQueue<E>.compare(o1: E, o2: E) =
comparator()?.compare(o1, o2) ?: (o1 as Comparable<E>).compareTo(o2)
Divide your points into several groups and run the calculation for each group in a separate thread. It will let your program to use all available cores.

Collision response for rectangles

I've been working on a physics engine for about a week now, being stuck for several days trying to work out how to resolve collisions.
My problem is that if there's a box stuck in the middle of 2 other boxes, or between a box and a wall, my application will get stuck in a while loop. It wont resolve the collisions.
This is my code (note: if collision is right side, it means that object A is colliding against object B with its right side. Distance is negative because the objects are inside eachother, and it's in x or y axis depending on side of collision. If you need more code, for example the collision class, which is simply a container of the 2 objects, i can provide that.):
edit: Code edited with new way of dealing with collisions:
//Move colliding objects so they don't collide anymore.
while (getCollidingAmount(objectVector)){
for (int i = 0; i < objectVector.size(); i++){
PhysicsObject* A = objectVector[i];
if (objectVector[i]->getPhysicsType() != PhysicsType::staticT && A->_collision.size() > 0){
Collision collision = A->_collision[A->getDeepestPenetrationCollisionIndex(A->_collision)];
PhysicsObject* B = collision.getObject();
switch (collision.getSide()){
case SideOfCollision::left:
case SideOfCollision::top:
//Opposite velocity
if (A->_saveVelocity.x < 0 && B->_saveVelocity.x > 0){
long double percentageOfVelocity = std::min(abs(B->_saveVelocity.x), abs(A->_saveVelocity.x)) /
std::max(abs(B->_saveVelocity.x), abs(A->_saveVelocity.x));
A->_position.x -= percentageOfVelocity*collision.getVectorPenetration().x;
A->_position.y -= percentageOfVelocity*collision.getVectorPenetration().y;
}
else{
A->_position.x -= collision.getVectorPenetration().x;
A->_position.y -= collision.getVectorPenetration().y;
}
break;
case SideOfCollision::right:
case SideOfCollision::bottom:
//Opposite velocity
if (A->_saveVelocity.x > 0 && B->_saveVelocity.x < 0){
long double percentageOfVelocity = 1 - std::min(abs(B->_saveVelocity.x), abs(A->_saveVelocity.x)) /
std::max(abs(B->_saveVelocity.x), abs(A->_saveVelocity.x));
A->_position.x -= percentageOfVelocity*collision.getVectorPenetration().x;
A->_position.y -= percentageOfVelocity*collision.getVectorPenetration().y;
}
else{
A->_position.x -= collision.getVectorPenetration().x;
A->_position.y -= collision.getVectorPenetration().y;
}
break;
}
updateCollisions(objectVector);
}
}
}
Update
Something wrong with my trigonometry in bottom and top collisions:
sf::Vector2<long double> Collision::getVectorPenetration() const{
long double x;
long double y;
long double velX = _object->getVelocity().x;
long double velY = _object->getVelocity().y;
long double angle = atan2(velY, velX);
if (_side == SideOfCollision::left || _side == SideOfCollision::right){
x = getDistance();
y = x * tan(angle);
return sf::Vector2<long double>(x, y);
}
else if (_side == SideOfCollision::top || _side == SideOfCollision::bottom){
y = getDistance();
x = y / tan(angle);
return sf::Vector2<long double>(x, y);
}
}
Update 2
Thanks to Aiman, i solved my issue. Updated my collisionResponse code aswell to match my new way of dealing with collisions. I'm having another issue now where gravity makes it so i can't move in X direction when touching another object. If anyone familiar with this issue wants to give any tips to solve it, i appreciate it :).
Update 3
So it seems gravity is not actually the problem since i can swap gravity to the x axis, and then be able to slide boxes along the walls. There seems to still be something wrong with the trigonometry.
I can think of many ways to approach the problem.
1-**The more complicated one is to **introduce friction. Here is how I'd implement it, though this is untested and there is a chance I missed something in my train of thought.
Every shape gets a friction constant, and according to those your objects slide when they collide.
First, you need to get the angle that is perpendicular to your surface. To do this, you just get the arctan of the the surface's normal slope. The normal is simply -1/m, where m is the slope of your surface (which you is the ratio/quotient of how much the surface extends in y to/by how much it extends in x). Let's call this angle sNormal for "surface normal". We may also need sAngle-"surface angle" for later (you find that by arctan(m)). There remains some ambiguity in the angle that has to do with whether you're talking about the 'front' or the 'back' of the surface. You'll have to deal with that manually.
Next, you need the angle of the trajectory your object flies in, which you already know how to find (atan2(y,x)). We'll call this angle oAngle for "object's surface angle". Next, you calculate deltaAngle = sNormal - oAngle. This angle represents how much momentum was not blocked completely by the surface. A deltaAngle of 0 means all momentum is gone, and a value of PI/2 or 90 means the 2 surfaces are in parallel touching each other not blocking any momentum at all. Anything in between, we interpolate:
newSpeed = objectSpeed * deltaAngle/(PI/2);
newVelocity.x = cos(sAngle) * objectSpeed;
newVelocity.y = sin(sAngle) * objectSpeed;
Now this assumes 0 friction. If we let a friction of 1 be the maximum friction which doesn't allow the object to "slide", we modify the newSpeed before we apply the newVelocity values, like so: newSpeed *= (1-friction);.
And there we have it! Just give your platform a friction value of less than 1 and your box will be able to slide. If you're dealing with upright boxes, then the surface angle is PI for top wall, 0 for the bottom, PI/2 for the right and -PI/2 for the left wall.
2-The simpler option is to subtract gravity from the object's y-velocity in the solver's calculation.

Build a javascript sliding puzzle

I am attempting to build a game that follows the same mechanic as Dragon's Tail featured at the following link http://bit.ly/1CR0iha. I have built a very basic version of level one working using mouse clicks with one open space. I really want it to move on pressmove so that I can drag the pieces to move them rather than on click which will help when there are more than one open space as on level two.
How to confine the movement of the pieces so that they can ONLY move into the open space? My initial though was to place all the pieces into a grid and check for collisions between the various pieces on pressmove and only allow a move if there is no collision.
After some coding I need a sanity check because I am now second guessing my decision. I am asking for on a high level sanity check on my method any insights are most welcome ;);
Once I have set my x and y on mousedown I them do the following on pressmove. I am not limiting my direction which is what I am trying to achieve.
pressMove = function(event){
var pt = this.globalToLocal(event.stageX, event.stageY);
if(pt.x > this.startPosX){
this.dragDirection = 'right';
}
if(pt.x < this.startPosX){
this.dragDirection = 'left';
}
if(pt.y < this.startPosY){
this.dragDirection = 'up';
}
if(pt.y > this.startPosY){
this.dragDirection = 'down';
}
movePiece(event);
}
Moving the piece
movePiece = function(event){
var obj = event.target;
var pt = this.globalToLocal(event.stageX, event.stageY);
switch(this.dragDirection){
case 'up':
obj.y = pt.y;
break;
case 'down':
obj.y = pt.y;
break;
case 'left':
obj.x = pt.x;
break;
case 'right':
obj.x = pt.x;
break;
};
}
you don't need sofisticated collision detection - all you need to know is which are the possible moves (up, down, right, left) allowed for a clicked piece.
you can do this by having a 2d field representing blocked spaces 1 and free spaces 0 like so
A B C D
X 1 0 1 1
Y 1 1 1 1
Z 1 1 1 1
say you click the piece (Y,B) you can see by checking its four neighbouring entries that only the move up is available, similarly for XA and XC. for all other pieces there are no free spaces and thus no possible moves. after moving a piece, make sure its again in a grid position and update the 2d field. i.e. after moving YB up the field should look like this:
A B C D
X 1 1 1 1
Y 1 0 1 1
Z 1 1 1 1
you can make boundary handling easier by surrounding your actual playfield with blocked spaces
Edit: say you only move up/down and left/right. Every move starts at the center of a piece. Around the center there's a deadzone where it's not clear (yet) which direction the move will take. Then your code might look like this:
startDrag(x, y) {
// remember clicked piece p
p <- ...
// remember center positions for p (cX, cY)
(cX, cy) <- ...
// remember offset of cursor to center (oX, oY)
(oX, oY) <- (x - cX, y - cY)
}
continueDrag(x, y) {
// select move direction
if distance between (cX, cY) and (x - oX, y - oY) is within the deadzone
select move direction (up, down, left, right) with least projection error
else
select last move direction
end
// get constrained move direction
switch selected move direction
case up: perform move to (cX, y - oY)
case down: perform move to (cX, y - oY)
case left: perform move to (x - oX, cY)
case right: perform move to (y - oX, cY)
end
}
you can see all of this in action (and a bit more robust) here: http://js.do/code/sliding-puzzle