1
0
Fork 0
mirror of https://github.com/k4zmu2a/SpaceCadetPinball.git synced 2024-06-26 07:42:10 +02:00
SpaceCadetPinball/SpaceCadetPinball/maths.cpp
toxie 57af3af800
Increase precision of mode_countdown_ handling (#52)
* fix harmless warnings and properly try/catch allocations via new

otherwise the error handling will never be triggered

* increase precision of mode_countdown_ handling

potentially there could be modes running a bit too long, depending on passed in ms (which were implicitly truncated before when passing in)

also fix some harmless warnings

* document warnings that i cannot handle on my own

* revert changes to have a new cleaner PR after review/cherry picks

* increase precision of mode_countdown_ handling

potentially there could be modes running a bit too long, depending on passed in ms (which were implicitly truncated before when passing in)

also fix some harmless warnings and add comments where original code is 'correct' but weird
2021-10-23 07:33:04 +03:00

464 lines
12 KiB
C++

#include "pch.h"
#include "maths.h"
#include "TBall.h"
#include "TFlipperEdge.h"
void maths::enclosing_box(rectangle_type* rect1, rectangle_type* rect2, rectangle_type* dstRect)
{
int xPos1 = rect1->XPosition;
int yPos1 = rect1->YPosition;
int width1 = rect1->Width;
int height1 = rect1->Height;
int xPos2 = rect2->XPosition;
bool rect2XPosLessRect1 = rect2->XPosition < rect1->XPosition;
int yPos2 = rect2->YPosition;
int width2 = rect2->Width;
int height2 = rect2->Height;
int xPos2_2 = rect2->XPosition;
if (rect2XPosLessRect1)
{
width1 += xPos1 - xPos2;
xPos1 = xPos2;
}
if (yPos2 < yPos1)
{
height1 += yPos1 - yPos2;
yPos1 = yPos2;
}
if (width2 + xPos2 > xPos1 + width1)
width1 = xPos2_2 + width2 - xPos1;
int height1_2 = height1;
if (height2 + yPos2 > height1 + yPos1)
height1_2 = yPos2 + height2 - yPos1;
dstRect->YPosition = yPos1;
dstRect->Height = height1_2;
dstRect->XPosition = xPos1;
dstRect->Width = width1;
}
int maths::rectangle_clip(rectangle_type* rect1, rectangle_type* rect2, rectangle_type* dstRect)
{
int xPos1 = rect1->XPosition;
int yPos1 = rect1->YPosition;
int height1 = rect1->Height;
int xRight2 = rect2->XPosition + rect2->Width;
int width1 = rect1->Width;
int yRight2 = rect2->YPosition + rect2->Height;
if (xPos1 + width1 < rect2->XPosition)
return 0;
if (xPos1 >= xRight2)
return 0;
int yPos2 = yPos1;
if (yPos1 + height1 < rect2->YPosition || yPos1 >= yRight2)
return 0;
if (xPos1 < rect2->XPosition)
{
width1 += xPos1 - rect2->XPosition;
xPos1 = rect2->XPosition;
}
if (xPos1 + width1 > xRight2)
width1 = xRight2 - xPos1;
int height2 = height1;
if (yPos1 < rect2->YPosition)
{
height2 = yPos1 - rect2->YPosition + height1;
yPos2 = rect2->YPosition;
}
if (height2 + yPos2 > yRight2)
height2 = yRight2 - yPos2;
if (!width1 || !height2)
return 0;
if (dstRect)
{
dstRect->XPosition = xPos1;
dstRect->YPosition = yPos2;
dstRect->Width = width1;
dstRect->Height = height2;
}
return 1;
}
int maths::overlapping_box(rectangle_type* rect1, rectangle_type* rect2, rectangle_type* dstRect)
{
int v3;
int v4;
int v6;
int v7;
if (rect1->XPosition >= rect2->XPosition)
{
dstRect->XPosition = rect2->XPosition;
v3 = rect1->Width - rect2->XPosition;
v4 = rect1->XPosition;
}
else
{
dstRect->XPosition = rect1->XPosition;
v3 = rect2->Width - rect1->XPosition;
v4 = rect2->XPosition;
}
dstRect->Width = v3 + v4 + 1;
int v5 = rect1->YPosition;
if (v5 >= rect2->YPosition)
{
dstRect->YPosition = rect2->YPosition;
v6 = rect1->Height - rect2->YPosition;
v7 = rect1->YPosition;
}
else
{
dstRect->YPosition = v5;
v6 = rect2->Height - rect1->YPosition;
v7 = rect2->YPosition;
}
dstRect->Height = v6 + v7 + 1;
return dstRect->Width <= rect2->Width + rect1->Width && dstRect->Height <= rect2->Height + rect1->Height;
}
float maths::ray_intersect_circle(ray_type* ray, circle_type* circle)
{
// O - ray origin
// D - ray direction
// C - circle center
// R - circle radius
// L, C - O, vector between O and C
float Lx = circle->Center.X - ray->Origin.X;
float Ly = circle->Center.Y - ray->Origin.Y;
// Tca, L dot D, projection of L on D
float Tca = Ly * ray->Direction.Y + Lx * ray->Direction.X;
if (Tca < 0.0f) // No intersection if Tca is negative
return 1000000000.0f;
// L dot L, distance from ray origin to circle center
float LMagSq = Ly * Ly + Lx * Lx;
// If ray origin is inside of the circle
// T0 = Tca - Sqrt(rad^2 - d^2). d = sqrt(L dot L - Tca dot Tca)
if (LMagSq < circle->RadiusSq)
return Tca - sqrt(circle->RadiusSq - LMagSq + Tca * Tca);
// Thc^2 = rad^2 - d = rad^2 - L dot L + Tca dot Tca
float ThcSq = circle->RadiusSq - LMagSq + Tca * Tca;
if (ThcSq < 0.0f) // No intersection if Thc is negative
return 1000000000.0f;
// T0 = Tca - Thc, distance from origin to first intersection
float T0 = Tca - sqrt(ThcSq);
if (T0 < 0.0f || T0 > ray->MaxDistance)
return 1000000000.0f;
return T0;
}
float maths::normalize_2d(vector_type* vec)
{
float mag = sqrt(vec->X * vec->X + vec->Y * vec->Y);
if (mag != 0.0f)
{
vec->X = 1.0f / mag * vec->X;
vec->Y = 1.0f / mag * vec->Y;
}
return mag;
}
void maths::line_init(line_type* line, float x0, float y0, float x1, float y1)
{
float v9;
bool lineDirection;
float v11;
line->Direction.X = x1 - x0;
line->Direction.Y = y1 - y0;
normalize_2d(&line->Direction);
line->PerpendicularL.X = line->Direction.Y;
line->PerpendicularL.Y = -line->Direction.X;
line->PreComp1 = -(line->Direction.Y * x0) + line->Direction.X * y0;
if (line->Direction.X >= 0.000000001f || line->Direction.X <= -0.000000001f)
{
v9 = x1;
lineDirection = x0 >= x1;
v11 = x0;
}
else
{
line->Direction.X = 0.0;
v9 = y1;
lineDirection = y0 >= y1;
v11 = y0;
}
if (lineDirection)
{
line->OriginX = v9;
line->OriginY = v11;
}
else
{
line->OriginY = v9;
line->OriginX = v11;
}
}
float maths::ray_intersect_line(ray_type* ray, line_type* line)
{
bool v5;
bool v6;
float perpDot = line->PerpendicularL.Y * ray->Direction.Y + ray->Direction.X * line->PerpendicularL.X;
if (perpDot < 0.0f)
{
float result = -((ray->Origin.X * line->PerpendicularL.X + ray->Origin.Y * line->PerpendicularL.Y + line->
PreComp1)
/ perpDot);
if (result >= -ray->MinDistance && result <= ray->MaxDistance)
{
line->RayIntersect.X = result * ray->Direction.X + ray->Origin.X;
float v4 = result * ray->Direction.Y + ray->Origin.Y;
line->RayIntersect.Y = v4;
if (line->Direction.X == 0.0f)
{
if (v4 >= line->OriginX)
{
v5 = v4 < line->OriginY;
v6 = v4 == line->OriginY;
if (v5 || v6)
return result;
return 1000000000.0;
}
}
else if (line->OriginX <= line->RayIntersect.X)
{
float v7 = line->RayIntersect.X;
v5 = v7 < line->OriginY;
v6 = v7 == line->OriginY;
if (v5 || v6)
return result;
return 1000000000.0;
}
}
}
return 1000000000.0;
}
void maths::cross(vector_type* vec1, vector_type* vec2, vector_type* dstVec)
{
dstVec->X = vec2->Z * vec1->Y - vec2->Y * vec1->Z;
dstVec->Y = vec2->X * vec1->Z - vec1->X * vec2->Z;
dstVec->Z = vec1->X * vec2->Y - vec2->X * vec1->Y;
}
float maths::magnitude(vector_type* vec)
{
float result;
auto magSq = vec->X * vec->X + vec->Y * vec->Y + vec->Z * vec->Z;
if (magSq == 0.0f)
result = 0.0;
else
result = sqrt(magSq);
return result;
}
void maths::vector_add(vector_type* vec1Dst, vector_type* vec2)
{
vec1Dst->X += vec2->X;
vec1Dst->Y += vec2->Y;
}
float maths::basic_collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float elasticity, float smoothness,
float threshold, float boost)
{
ball->Position.X = nextPosition->X;
ball->Position.Y = nextPosition->Y;
float proj = -(direction->Y * ball->Acceleration.Y + direction->X * ball->Acceleration.X);
if (proj < 0)
{
proj = -proj;
}
else
{
float dx1 = proj * direction->X;
float dy1 = proj * direction->Y;
ball->Acceleration.X = (dx1 + ball->Acceleration.X) * smoothness + dx1 * elasticity;
ball->Acceleration.Y = (dy1 + ball->Acceleration.Y) * smoothness + dy1 * elasticity;
normalize_2d(&ball->Acceleration);
}
float projSpeed = proj * ball->Speed;
float newSpeed = ball->Speed - (1.0f - elasticity) * projSpeed;
ball->Speed = newSpeed;
if (projSpeed >= threshold)
{
ball->Acceleration.X = newSpeed * ball->Acceleration.X + direction->X * boost;
ball->Acceleration.Y = newSpeed * ball->Acceleration.Y + direction->Y * boost;
ball->Speed = normalize_2d(&ball->Acceleration);
}
return projSpeed;
}
float maths::Distance_Squared(vector_type& vec1, vector_type& vec2)
{
return (vec1.Y - vec2.Y) * (vec1.Y - vec2.Y) + (vec1.X - vec2.X) * (vec1.X - vec2.X);
}
float maths::DotProduct(vector_type* vec1, vector_type* vec2)
{
return vec1->Y * vec2->Y + vec1->X * vec2->X;
}
void maths::vswap(vector_type* vec1, vector_type* vec2)
{
vector_type tmp = *vec1;
*vec1 = *vec2;
*vec2 = tmp;
}
float maths::Distance(vector_type* vec1, vector_type* vec2)
{
auto dx = vec1->X - vec2->X;
auto dy = vec1->Y - vec2->Y;
return sqrt(dy * dy + dx * dx);
}
void maths::SinCos(float angle, float* sinOut, float* cosOut)
{
*sinOut = sin(angle);
*cosOut = cos(angle);
}
void maths::RotatePt(vector_type* point, float sin, float cos, vector_type* origin)
{
auto dirX = point->X - origin->X;
auto dirY = point->Y - origin->Y;
point->X = dirX * cos - dirY * sin + origin->X;
point->Y = dirX * sin + dirY * cos + origin->Y;
}
float maths::distance_to_flipper(ray_type* ray1, ray_type* ray2)
{
auto distance = 1000000000.0f;
auto distanceType = -1;
auto newDistance = ray_intersect_line(ray1, &TFlipperEdge::lineA);
if (newDistance < 1000000000.0f)
{
distance = newDistance;
distanceType = 0;
}
newDistance = ray_intersect_circle(ray1, &TFlipperEdge::circlebase);
if (newDistance < distance)
{
distance = newDistance;
distanceType = 2;
}
newDistance = ray_intersect_circle(ray1, &TFlipperEdge::circleT1);
if (newDistance < distance)
{
distance = newDistance;
distanceType = 3;
}
newDistance = ray_intersect_line(ray1, &TFlipperEdge::lineB);
if (newDistance < distance)
{
distance = newDistance;
distanceType = 1;
}
if (!ray2 || distance >= 1000000000.0f)
return distance;
if (distanceType != -1)
{
vector_type* nextOrigin;
if (distanceType)
{
if (distanceType != 1)
{
float dirY;
ray2->Origin.X = distance * ray1->Direction.X + ray1->Origin.X;
ray2->Origin.Y = distance * ray1->Direction.Y + ray1->Origin.Y;
if (distanceType == 2)
{
ray2->Direction.X = ray2->Origin.X - TFlipperEdge::circlebase.Center.X;
dirY = ray2->Origin.Y - TFlipperEdge::circlebase.Center.Y;
}
else
{
ray2->Direction.X = ray2->Origin.X - TFlipperEdge::circleT1.Center.X;
dirY = ray2->Origin.Y - TFlipperEdge::circleT1.Center.Y;
}
ray2->Direction.Y = dirY;
normalize_2d(&ray2->Direction);
return distance;
}
ray2->Direction = TFlipperEdge::lineB.PerpendicularL;
nextOrigin = &TFlipperEdge::lineB.RayIntersect;
}
else
{
ray2->Direction = TFlipperEdge::lineA.PerpendicularL;
nextOrigin = &TFlipperEdge::lineA.RayIntersect;
}
ray2->Origin = *nextOrigin;
return distance;
}
return 1000000000.0;
}
void maths::RotateVector(vector_type* vec, float angle)
{
float s = sin(angle), c = cos(angle);
vec->X = c * vec->X - s * vec->Y;
vec->Y = s * vec->X + c * vec->Y;
/* Error in the original, should be:
* tmp = c * vec->X - s * vec->Y;
* vec->Y = s * vec->X + c * vec->Y;
* vec->X = tmp
*/
}
void maths::find_closest_edge(ramp_plane_type* plane, int planeCount, wall_point_type* wall, vector_type** lineEnd,
vector_type** lineStart)
{
vector_type wallEnd{}, wallStart{};
wallStart.X = wall->X0;
wallStart.Y = wall->Y0;
wallEnd.Y = wall->Y1;
wallEnd.X = wall->X1;
float maxDistance = 1000000000.0f;
ramp_plane_type* planePtr = plane;
for (auto index = 0; index < planeCount; index++)
{
auto vec1 = reinterpret_cast<vector_type*>(&planePtr->V1),
vec2 = reinterpret_cast<vector_type*>(&planePtr->V2),
vec3 = reinterpret_cast<vector_type*>(&planePtr->V3);
auto distance = Distance(&wallStart, vec1) + Distance(&wallEnd, vec2);
if (distance < maxDistance)
{
maxDistance = distance;
*lineEnd = vec1;
*lineStart = vec2;
}
distance = Distance(&wallStart, vec2) + Distance(&wallEnd, vec3);
if (distance < maxDistance)
{
maxDistance = distance;
*lineEnd = vec2;
*lineStart = vec3;
}
distance = Distance(&wallStart, vec3) + Distance(&wallEnd, vec1);
if (distance < maxDistance)
{
maxDistance = distance;
*lineEnd = vec3;
*lineStart = vec1;
}
++planePtr;
}
}