Arcs and line segments detection from collection of points - c#

I am preparing to create a program to detect arcs and line segments from the collection of points (x,y) which follow each other and generally, they create a closed polygon.
I just dont know where to start.
Maybe someone knows "ready to go" library (can be paid) which do such a think for me? Or maybe some propositions of algorithms that are relatively easy to implement? I am looking for any advices.

If your points don't precisely fall on the arcs and segments, but you're trying to find the closest fit, see: 'https://en.wikipedia.org/wiki/Curve_fitting'.
If your points follow exactly arcs or line segments, maybe this closed solution might work for you.
It assumes arcs are made out of at least 4 points. You can create an arc with any 3 points, so there is no way to tell if they are supposed to be an arc or line segment (unless you use an angle threshold). With 4 points you can compare if points (0,1,2) and (0,1,3) are part of the same arc.
When creating arc objects from 3 points, internally it calculates the radius and center to be able to compare them. To find a circle from 3 points: https://math.stackexchange.com/questions/213658/get-the-equation-of-a-circle-when-given-3-points
PolyCurve ArcsAndLines(List<Point3d> points)
{
var curve = new PolyCurve();
Arc current = Arc.Unset;
for (int i = 0; i < points.Count - 1; i++)
{
var areEqual = false;
if (i + 3 < points.Count)
{
var arcA = new Arc(points[i], points[i + 1], points[i + 2]);
var arcB = new Arc(points[i], points[i + 1], points[i + 3]);
areEqual = AreEqual(arcA, arcB);
}
if (areEqual)
{
var start = current == Arc.Unset ? points[i] : current.StartPoint;
current = new Arc(start, points[i + 1], points[i + 3]);
}
else
{
if (current != Arc.Unset)
{
curve.Append(current);
current = Arc.Unset;
i++;
}
else
{
curve.Append(new Line(points[i], points[i + 1]));
}
}
}
return curve;
}
bool AreEqual(Arc a, Arc b)
{
const double tol = 0.001;
bool sameRadius = Math.Abs(a.Radius - b.Radius) < tol;
if (!sameRadius) return false;
bool sameCenter = a.Center.DistanceTo(b.Center) < tol;
return sameCenter;
}
'curve' is a list containing line and arc segments.
It should work with open polylines and polygons (with end point being the same as start). If the start of a polygon lies in the middle of an arc, it will be split into two arcs.
A regular polygon (resulting in a circle) will not work properly since you'll be defining an arc with the same start and end points.

Related

How to detect if object collide with each other if all I have is a list of their positions and their size?

I have a lot of circles and I store their position in a list.
All circles have the same radius.
How can I detect if any circle collides with another circle?
What would be the fastest way?
Given the Pythagorean Theorem, a^2 + b^2 = c^2, if you take dx = circle[0].x - circle[1].x, dy = circle[0].y - circle[1].y, then you can get the hypotenuse which is the straight-line distance between centers by hypotenuse = Mathf.Sqrt(dx^2 + dy^2);.
However, you don't actually need the distance/hypotenuse, you just need to know if that value is less than 2*r, or two radii, because if the distance between the two circles is less than that then you know they're colliding.
This means that you don't need to take the square root, you can just leave it squared and compare it to the collision distance squared, or (2*r)^2. That is, you are colliding when:
(dx^2 + dy^2) <= (2*r)^2
In code, it looks like:
public class Circle
{
public float R { get; private set; }
public float X { get; private set; }
public float Y { get; private set; }
}
public static bool IsColliding(Circle c0, Circle c1)
{
var collisionDistance = (c0.R + c1.R)*(c0.R + c1.R);
var dx = c0.X - c1.X;
var dy = c0.Y - c1.Y;
return dx*dx + dy*dy <= collisionDistance;
}
If the radii are all the same then you can cache collisionDistance and not need to recalculate it on each step.
NOTE: My solution here assumes the radii aren't the same; the "2*r" term is replaced by c0.R + c1.R. If they're the same then it reduces to c0.R + c0.R or 2*c0.R.
:EDIT: To check collisions, if you have 4 circles, you'd need to check 1 against 2, 3, and 4. Then, because you already checked 1 against 2 you can skip 2 against 1. You just need to check 2 against 3 and 4, then just 3 against 4.
That is, if you have a List<Circle> circles, you would get:
var nCircles = circles.Count;
for(int i=0; i<nCircles-1; i++)
{
for(int j=i+1; j<nCircles; j++)
{
if(IsColliding(circles[i], circles[j]))
{
// Do something
}
}
}
This would check 0 against 1,2,3; then 1 against 2,3; then 2 against 3.
Assuming you have a lot of circles (e.g. at least 10,000) and you want to find all circle collisions, you can check each circle against all remaining circles, starting with the first. You can optimize the check by using the bounding box around each circle to filter in only nearby circles, and then test against a diamond inside to filter in close circles and finally test against the full circle to filter out corner circles. Since we know the radius is the same in all cases, we are filtering the centers against a circle of 2*r.
Testing only the circles inside the bounding box is about a 10% speedup, filtering in the circles inside the diamond is about another 1% speedup, and testing in the order given is fastest for collisions of 0.15% to 62% of the total list, but if your list is much smaller, extra tests may cost more time than just the simple test against the bounding circle.
Also if you only care for the first collision for each circle, you can add a break statement after the Add in the if to stop testing once a collision is found.
var r = 3.0;
var ans = new List<(int, int)>();
var r2 = 2 * r;
var r_sq = r2 * r2;
for (int j1 = 0; j1 < circles.Count - 1; ++j1) {
var c1 = circles[j1];
for (int j2 = j1 + 1; j2 < circles.Count; ++j2) {
var c2 = circles[j2];
var dx = Math.Abs(c1.x - c2.x);
var dy = Math.Abs(c1.y - c2.y);
if (dx <= r2 && dy <= r2 && ((dx + dy <= r2) || (dx * dx + dy * dy <= r_sq))) {
ans.Add((j1, j2));
}
}
}
If this isn't sufficient, and you can store your circles in a different structure, it is probably time to look at something like k-d trees.

How to find the "best" position using newtons method for n samples

I am trying to find the best position for n sphere intersections using newtons method.
Sphere equation: (x - a)2 + (y - b)2 + (z - c)2 = r2
where a, b, c = center of sphere
where x, y, z = sample location
where r2 = radius squared
for a point to lie on any sphere, it needs to satisfy this equation. So, for a point to lie on (or close) to multiple spheres, it needs to satisfy this equation for multiple spheres.
I think we can make a cost function like this:
sum((x - a_i)2 + (y - b_i)2 + (z - c_i)2 - (r_i)2) == 0;
where a_i, b_i, c_i = center of i sphere
where x, y, z = sample location
where r_i = radius of i sphere
since dot(pos - center) should equal radii squared, if dot(pos - center) - radii squared == 0, it is the same function.
Newtons method works by using taking samples at intervals, and then uses the derivative of a function in order to move it. This equation looks like this:
s2 = s1 - (f(s1) / f`(s1))
where (s2) = second sample location (target)
f(s1) = first sample location's equation
f`(s1) = derivative of the sample
I have written the following function in C#, which solves the problem when I input 2 spheres:
// For maxIterations:
for (int i = 0; i < maxIterations; i++)
{
float3 vDist = float3.zero;
float errorFunction = 0f;
float3 derivative = float3.zero;
// For each sphere
for (int j = 0; j < centers.Length; j++)
{
// Calculate the sqDistance between the sample point and the sphere, and subtract it with the squared radii in order to get an error function
vDist = sampleLocation - centers[j];
errorFunction += math.dot(vDist, vDist) - (radii[j] * radii[j]);
// The derivative of the sphere is needed in order to move the sample
derivative += new float3((sampleLocation.x - centers[j].x) * (sampleLocation.x - centers[j].x), (sampleLocation.y - centers[j].y) * (sampleLocation.y - centers[j].y), (sampleLocation.z - centers[j].z) * (sampleLocation.z - centers[j].z));
}
// If the errorFunction gets within a certain threshold, the position is found
if (errorFunction >= -threshold && errorFunction <= threshold)
{
print("Found position: " + sampleLocation + " in " + i + ", error: " + errorFunction);
break;
}
// Move the sample based on the error function and the derivative of the sphere
sampleLocation = sampleLocation - (errorFunction / derivative);
}
However, when I input 3 spheres, it does not work anymore, I think because the equation becomes non-linear?
I found a good reference on stackoverflow:
Math - 3d positioning/multilateration
but I don't quite understand the answer given there.
Could someone try to help me extend this function to work with multiple spheres?

Moving from one set of points in 3d space to another set of points with the shortest possible accumulative distance

We have 2 lists(black and red), each one contains multiple points in 3d space. We have to move each black point to a red point, and do this in such a way that the total distance to make the moves is the least it can be. The lists can be of different sizes.
Simple correct soltution in 2D space:
Incorrect solution:
If the sizes of the lists differ, then we have to either stack points on top of each other or split one point into multiple points.
Splitting example:
Stacking example:
Our best attempt at this problem follows these general steps:
If there are more red points than black points, pick the black point that's furthest from all of the red point and match it with a red point that is closest to its position and has not been matched yet.
Repeat step 1 until all of the black points are matched.
Iterate over the leftover red points and match each one to their respective closest black point, thus stacking them. The result will look something like this:
Note: if there is more black points than red points, then step one will look for the furthest red point and match it to its closest black point and proceed all the same with the colors swapped.
Some C# code:
private void SaveFrames(List<List<Vector3>> frameList) {
List<Dictionary<Vector3, List<Vector3>>> resultingPairs = new List<Dictionary<Vector3, List<Vector3>>>();
for (int iFrame = 0; iFrame < frameList.Count+1; iFrame++) {
List<Vector3> currentFrame = frameList[iFrame % frameList.Count];
List<Vector3> nextFrame = frameList[(iFrame + 1) % frameList.Count];
int maxIterations = Mathf.Min(currentFrame.Count, nextFrame.Count);
Dictionary<Vector3, List<Vector3>> pairs = new Dictionary<Vector3, List<Vector3>>();
HashSet<Vector3> takenRed = new HashSet<Vector3>();
HashSet<Vector3> takenBlack = new HashSet<Vector3>();
HashSet<Vector3> takenDestination = new HashSet<Vector3>();
bool moreRed = currentFrame.Count < nextFrame.Count;
if (moreRed) {
for (int i = 0; i < maxIterations; i++) {
// Find furthest black point from any red point
float distance = 0;
Vector3 furthestBlack = Vector3.zero;
foreach (Vector3 black in currentFrame) {
if (takenBlack.Contains(black)) continue;
foreach (var red in nextFrame) {
if (Vector3.Distance(black, red) > distance) {
distance = Vector3.Distance(black, red);
furthestBlack = black;
}
}
}
// Find the closest red point to the furthest black point
distance = float.MaxValue;
Vector3 closestRed = Vector3.zero;
foreach (var red in nextFrame) {
if (takenRed.Contains(red)) continue;
if (Vector3.Distance(furthestBlack, red) < distance) {
distance = Vector3.Distance(furthestBlack, red);
closestRed = red;
}
}
if (!pairs.ContainsKey(furthestBlack)) {
pairs[furthestBlack] = new List<Vector3>();
}
if (!takenDestination.Contains(closestRed)) {
pairs[furthestBlack].Add(closestRed);
takenBlack.Add(furthestBlack);
takenRed.Add(closestRed);
takenDestination.Add(closestRed);
}
// Debug.Log("Pair: " + furthestBlack.ToString() + " to " + closestRed.ToString());
}
} else {
for (int i = 0; i < maxIterations; i++) {
// Find furthest red point from any black point
float distance = 0;
Vector3 furthestRed = Vector3.zero;
foreach (Vector3 red in nextFrame) {
if (takenRed.Contains(red)) continue;
foreach (Vector3 black in currentFrame) {
if (Vector3.Distance(black, red) > distance) {
distance = Vector3.Distance(black, red);
furthestRed = red;
}
}
}
// Find the closest black point to the furthest red point
distance = float.MaxValue;
Vector3 closestBlack = Vector3.zero;
foreach (var black in currentFrame) {
if (takenBlack.Contains(black)) continue;
if (Vector3.Distance(furthestRed, black) < distance) {
distance = Vector3.Distance(furthestRed, black);
closestBlack = black;
}
}
if (!pairs.ContainsKey(closestBlack)) {
pairs[closestBlack] = new List<Vector3>();
}
if (!takenDestination.Contains(furthestRed)) {
pairs[closestBlack].Add(furthestRed);
takenBlack.Add(closestBlack);
takenRed.Add(furthestRed);
takenDestination.Add(furthestRed);
}
// Debug.Log("Pair: " + closestBlack.ToString() + " to " + furthestRed.ToString());
}
}
if (currentFrame.Count < nextFrame.Count) {
// For every nextFrame[i], find the closest black point and pair it.
for (int i = currentFrame.Count; i < nextFrame.Count; i++) {
float distance = float.MaxValue;
Vector3 closestBlack = Vector3.zero;
foreach (var black in currentFrame) {
if (Vector3.Distance(nextFrame[i], black) < distance) {
distance = Vector3.Distance(nextFrame[i], black);
closestBlack = black;
}
}
if (!pairs.ContainsKey(closestBlack)) {
pairs[closestBlack] = new List<Vector3>();
}
if (!takenDestination.Contains(nextFrame[i])) {
pairs[closestBlack].Add(nextFrame[i]);
takenDestination.Add(nextFrame[i]);
}
// Debug.Log("Pair: " + closestBlack.ToString() + " to " + nextFrame[i].ToString());
}
}
if (currentFrame.Count > nextFrame.Count) {
// For every currentFrame[i], find the closest red point and pair it.
for (int i = nextFrame.Count; i < currentFrame.Count; i++) {
float distance = float.MaxValue;
Vector3 closestRed = Vector3.zero;
foreach (var red in nextFrame) {
if (Vector3.Distance(currentFrame[i], red) < distance) {
distance = Vector3.Distance(currentFrame[i], red);
closestRed = red;
}
}
if (!pairs.ContainsKey(currentFrame[i])) {
pairs[currentFrame[i]] = new List<Vector3>();
}
if (!takenDestination.Contains(closestRed)) {
pairs[currentFrame[i]].Add(closestRed);
takenDestination.Add(closestRed);
}
// Debug.Log("Pair: " + currentFrame[i].ToString() + " to " + closestRed.ToString());
}
}
resultingPairs.Add(pairs);
}
}
This method works for simple shapes like cubes.
However, it starts acting up when the cube positions overlap in 3d space from ne set of points to another.
And it does even funkier stuff with more complex points:
I am not exactly sure why this breaks down and I could not come up with a simple 2D example of where this approach goes wrong.
We have tried 3 different methods over 3 very long days, and can not seem to find a solution to this seemingly simple problem.
You can interpret this as the Assignment problem, where the black points are the "agents", the red points are the "tasks" (or vice versa) and the distance between them is the cost.
The problem instance has a number of agents and a number of tasks. Any agent can be assigned to perform any task, incurring some cost that may vary depending on the agent-task assignment. It is required to perform all tasks by assigning exactly one agent to each task and exactly one task to each agent in such a way that the total cost of the assignment is minimized.
The Assignment problem can be solved in polynomial time using The Hungarian algorithm. Variations on the problem involve more tasks than agents, which you can apply to your special cases where the sizes of the lists differ.
If you want a "quick 'n dirty" solution that should give decent results, consider adapting your current algorithm to be a probabilistic one. Weight each nearby red point according to how far away it is from the black point, and pick one at random by their weights. You may want to square (or even cube) the distances, to discourage picking farther away points. Overall it should pick many of the same points as your original algorithm, but with a few differences here and there. Repeat as many times as feasible, and pick the best result.
If you want something a bit less hand-wavey, consider modeling the problem as an asymmetric traveling salesman problem. Connect each black point to each red point, with a directional edge of weight proportional to its euclidean distance between them. Then connect each red point to each black point, with a directional edge of weight 0. Then solve with an existing asymmetric TSP solver, and then add extra nodes + connect as normal if necessary. Note however, this will throw away of lot of useful information (for instance, that we don't particularly care which black node we connect with next), in exchange for being able to use existing software with tried and proven heuristics and optimizations.

Find vertices in equilateral triangle mesh originating from a center vertex

I'd like to ask whether there is code out there or if you can give me some help in writing some (C#, but I guess the maths is the same everywhere).
I'd like to specify a center point from which an equilateral triangle mesh is created and get the vertex points of these triangles. The center point should not be a face center, but a vertex itself.
A further input would be the size of the triangles (i.e side length) and a radius to which triangle vertices are generated.
The reason behind it is that I want to create a mesh which is centered nicely on the screen/window center with as little code as possible. I just find mesh generation code, but not a "radial outward propagation" example.
In the end, I'd like to have the subsequently farther away vertices being displaced in a logarithmic fashion, but I guess that's just an easy addition once the mesh code is there.
Can anybody help me with that? Thanks!
You need to specify two things, a radius and the direction that the first triangle points.
The radius will be the distance from the initial point to the vertices of the first triangle. All triangles will have the same radius.
The direction is some specification in radians. I will assume that 0 means pointing to the right (PI would be point to the left).
Finding the vertices of the first triangle can be done like this (pseudo-code, not language specific):
float theta = 0; // The direction, 0 means pointing to the right
float thetaInc = TWO_PI/3; // 3 because you want a triangle
for (int i = 0; i < 3; i++) {
vertX[i] = initialPointX+cos(theta)*radius;
vertY[i] = initialPointY+sin(theta)*radius;
theta += thetaInc;
}
There are many ways to find the center points of the neighboring triangles. One way would be to use the same code but initialize theta = TWO_PI/6, replace radius with foo (see math below), assign new center points of neighboring triangles in the for loop, and then use the same code with an appropriately rotated direction (theta += PI) to find the vertices of those triangles.
Distance from one triangle center to another only knowing radius:
hypotenuse = sqrt(sq(radius)+sq(radius));
halfHypotenuse = hypotenuse/2.0;
Pythagorean theorem to find distance from center of triangle to center of an edge: foo = sqrt(sq(radius)-sq(halfHypotenuse));
Final distance = foo*2.0;
Code to find the center points of the neighboring triangles:
float[] nx = new float[3];
float[] ny = new float[3];
float theta = TWO_PI/6;
float hyp = sqrt(sq(radius)+sq(radius));
float halfHyp = hyp/2.0;
float foo = sqrt((sq(radius)-sq(halfHyp)))*2.0;
for (int i = 0; i < 3; i++) {
nx[i] = initialPointX+cos(theta)*foo;
ny[i] = initialPointY+sin(theta)*foo;
theta += thetaInc;
}
Thank you very much for your answer. I will play around with your code - the propagation part will come handy for sure.
In the meantime I have played around with hexagons instead of triangles and this codes works fairly alright for the same purpose.:
//populate array from the centre hex, going outwards the desired number of hex rings
for (int i = 0; i < numberOfHexagonRings; i++)
{
for (double j = -i; j <= i; j++)
for (double k = -i; k <= i; k++)
for (double l = -i; l <= i; l++)
if ((Math.Abs(j) + Math.Abs(k) + Math.Abs(l) == i * 2) && (j + k + l == 0))
{
positionX = (int)(screenCenterX + ((double)sideLength * (l / 2 + j)));
positionY = (int)(screenCenterY + (3/2 * ((double)sideLength / Math.Sqrt(3)) * l));

Resize a Rectangle which is on an angle

I have a Rectangle which is an array of 4 Point structures. It can be rotated in place on any angle (0 to 360 degrees) and will draw properly.
The user can also drag a corner to resize the rectangle. For example, if they move the bottom-left point, it will also update the X coordinate of the upper-left point, and the Y coordinate of the lower-right point. In this way, it will always be a rectangle no matter which point they move.
Points[point] = newValue;
switch (point)
{
case TopLeft:
Points[BottomLeft].X = newValue.X;
Points[TopRight].Y = newValue.Y;
break;
case BottomRight:
Points[TopRight].X = newValue.X;
Points[BottomLeft].Y = newValue.Y;
break;
case BottomLeft:
Points[TopLeft].X = newValue.X;
Points[BottomRight].Y = newValue.Y;
break;
case TopRight:
Points[BottomRight].X = newValue.X;
Points[TopLeft].Y = newValue.Y;
break;
}
Here I change any of the four points to the given input point (newValue), and then modify the linked points so that it stays a Rectangle shape.
However, I need to modify the above code to work if my rectangle is on an angle like this:
Sample code added here:
http://www.assembla.com/code/moozhe-testing/subversion/nodes/rotateRectangle
I see 2 solutions. The first one theoretically works, but because of rounding, it ends up not working. I'll let the first solution there, but the second one is the good one.
In these samples, I'll call the 4 corners CornerA, B, C and D, named in a clockwise fashion. Let's say you're moving "CornerA" from a position Point oldPoint to position Point newPoint.
First solution :
Get the position delta
Do a projection of that delta on Side sideAtoB and add that vector to PointD.
Do a projection of that delta on Side sideDtoA and add that vector to PointB.
Set PointA to newPoint.
Second solution :
Get the vector linking the opposite corner to the moving corner's new position, let's call it "Diagonal".
Set B's position to "C + [Projection of Diagonal on sideAtoD].
Set D's position to "C + [Projection of Diagonal on sideAtoB].
Set PointA to newPoint.
Here is the code for that 2nd solution :
public class Rectangle
{
// Obviously, one would need to assign values to these points.
Point CornerA = new Point();
Point CornerB = new Point();
Point CornerC = new Point();
Point CornerD = new Point();
Dictionary<int, Point> points = new Dictionary<int, Point>();
public Rectangle()
{
points.Add(0, CornerA);
points.Add(1, CornerB);
points.Add(2, CornerC);
points.Add(3, CornerD);
}
public void MoveAPoint(int id, Point newPoint)
{
// Get the old point
Point oldPoint = points[id];
// Get the previous point
Point pointPrevious = points[(id + 3) % 4];
// Get the next point
Point pointNext = points[(id + 1) % 4];
// Get the opposite point
Point pointOpposite = points[(id + 2) % 4];
// Get the delta (variation) of the moving point
Point delta = newPoint.Substract(oldPoint);
// I call sides points, but they are actually vectors.
// Get side from 'oldPoint' to 'pointPrevious'.
Point sidePrevious = pointPrevious.Substract(oldPoint);
// Get side from 'oldPoint' to 'pointNext'.
Point sideNext = pointNext.Substract(oldPoint);
// Get side from 'pointOpposite' to 'newPoint'.
Point sideTransversal = newPoint.Substract(pointOpposite);
PointF previousProjection;
PointF nextProjection;
if (sideNext.X == 0 && sideNext.Y == 0)
{
if (sidePrevious.X == 0 && sidePrevious.Y == 0)
{
return;
}
sideNext = new PointF(-sidePrevious.Y, sidePrevious.X);
}
else
{
sidePrevious = new PointF(-sideNext.Y, sideNext.X);
}
Point previousProjection = Projection(delta, sidePrevious);
Point nextProjection = Projection(delta, sideNext);
pointNext.SetToPoint(pointNext.AddPoints(previousProjection));
pointPrevious.SetToPoint(pointPrevious.AddPoints(nextProjection));
oldPoint.SetToPoint(newPoint);
}
private static Point Projection(Point vectorA, Point vectorB)
{
Point vectorBUnit = new Point(vectorB.X, vectorB.Y);
vectorBUnit = vectorBUnit.Normalize();
decimal dotProduct = vectorA.X * vectorBUnit.X + vectorA.Y * vectorBUnit.Y;
return vectorBUnit.MultiplyByDecimal(dotProduct);
}
}
public static class ExtendPoint
{
public static Point Normalize(this Point pointA)
{
double length = Math.Sqrt(pointA.X * pointA.X + pointA.Y * pointA.Y);
return new Point(pointA.X / length, pointA.Y / length);
}
public static Point MultiplyByDecimal (this Point point, decimal length)
{
return new Point((int)(point.X * length), (int)(point.Y * length));
}
public static Point AddPoints(this Point firstPoint, Point secondPoint)
{
return new Point(firstPoint.X + secondPoint.X, firstPoint.Y + secondPoint.Y);
}
public static Point Substract(this Point firstPoint, Point secondPoint)
{
return new Point(firstPoint.X - secondPoint.X, firstPoint.Y - secondPoint.Y);
}
public static void SetToPoint(this Point oldPoint, Point newPoint)
{
oldPoint.X = newPoint.X;
oldPoint.Y = newPoint.Y;
}
}
I used a solution that calculates the intersection between perpendicular lines:
Given a rotated rectangle (NOT axis aligned) with points, A, B, C, and D, and a dragged point D1 (which is the new point dragged from D), the goal is to find the new points for C1 and A1. B is opposite of D and will not move.
To find C1 and A1, find the intersection of the lines with points D1, which intersect with AB and BC and that are perpendicular to lines AB and BC (right angle). Those intersections will give you the points C1 and A1. Since the lines are perpendicular to AB and BC, they will form the new rectangle. Use the linear equations to calculate the slopes, intercepts and reciprocal slopes, which will give you both lines needed to calculate the intersection between AB and D1A. Repeat for the other side to get the intersection between BC and D1C (Remember, for axis-aligned rectangles, the below is not necessary and much easier)
Solution:
Find the slopes, opposite reciprocal slopes, and y-intercepts of the lines AB and AC. And find the y-intercepts of the lines D1A and D1C. Then calculate the intersection to get x and plug x into the lienar equation of one of the lines to get y:
slope_AB = (B.y - A.y) / (B.x - A.x)
y_intercept_AB = B.y - slope_AB * B.x
reciprocal_slope_AB = -1 / slope_AB
y_intercept_AD1 = D1.y - reciprocal_slope_AB * D1.x
A1x = (y_intercept_AB - y_intercept_AD1) / (reciprocal_slope_AB - slope_AB);
A1y = (slope_AB * B1x) + y_intercept_AB;
Repeat above 6 calculations accordingly to get C1x and C1y

Categories