mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-11 18:01:09 -05:00
Geometry, nesting and import code now call PortableMath instead of System.Math for Sin, Cos, Tan, Atan, Atan2, Asin and Acos (Core, Engine, IO, Data; 269 calls in 64 files). A test fails if those projects call the runtime's trig again. With the runtime's trig, Windows and Linux disagreed in the last bit at non-quarter angles and the same job could nest differently: the learned-angle nests (a cross fixture) placed 23 parts in Strip on Windows and 28 on Linux. Now every Engine.Tests pin gives the same layout on both (3600 passing on Linux and on DesktopPC), Linux layouts are unchanged, and the learned-angle rows are re-pinned to the shared layouts, which pass the overlap and work-area checks, and run on every platform again. Fixes Project Memory 518f3b58.
223 lines
6.0 KiB
C#
223 lines
6.0 KiB
C#
using System;
|
|
using OpenNest.Math;
|
|
|
|
namespace OpenNest.Geometry
|
|
{
|
|
public struct Vector : IEquatable<Vector>
|
|
{
|
|
public static readonly Vector Invalid = new Vector(double.NaN, double.NaN);
|
|
public static readonly Vector Zero = new Vector(0, 0);
|
|
|
|
public double X;
|
|
public double Y;
|
|
|
|
public Vector(double x, double y)
|
|
{
|
|
X = x;
|
|
Y = y;
|
|
}
|
|
|
|
public bool Equals(Vector other)
|
|
{
|
|
return X.IsEqualTo(other.X) && Y.IsEqualTo(other.Y);
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
return obj is Vector other && Equals(other);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
unchecked
|
|
{
|
|
// Use a simple but effective hash combine.
|
|
// We use a small epsilon-safe rounding if needed, but for uniqueness in HashSet
|
|
// during a single operation, raw bits or slightly rounded is usually fine.
|
|
// However, IsEqualTo uses Tolerance.Epsilon, so we should probably round to some precision.
|
|
// But typically for these geometric algorithms, exact matches (or very close) are what we want to prune.
|
|
return (X.GetHashCode() * 397) ^ Y.GetHashCode();
|
|
}
|
|
}
|
|
|
|
public double DistanceTo(Vector pt)
|
|
{
|
|
var vx = pt.X - X;
|
|
var vy = pt.Y - Y;
|
|
|
|
return System.Math.Sqrt(vx * vx + vy * vy);
|
|
}
|
|
|
|
public double DistanceTo(double x, double y)
|
|
{
|
|
var vx = x - X;
|
|
var vy = y - Y;
|
|
|
|
return System.Math.Sqrt(vx * vx + vy * vy);
|
|
}
|
|
|
|
public double DotProduct(Vector pt)
|
|
{
|
|
return X * pt.X + Y * pt.Y;
|
|
}
|
|
|
|
public double Angle()
|
|
{
|
|
return OpenNest.Math.Angle.NormalizeRad(PortableMath.Atan2(Y, X));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the angle to the given point when the origin is this point.
|
|
/// </summary>
|
|
/// <param name="pt"></param>
|
|
/// <returns></returns>
|
|
public double AngleTo(Vector pt)
|
|
{
|
|
return (pt - this).Angle();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the angle when the origin is set at the given point.
|
|
/// </summary>
|
|
/// <param name="pt"></param>
|
|
/// <returns></returns>
|
|
public double AngleFrom(Vector pt)
|
|
{
|
|
return (this - pt).Angle();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the angle between this point and the given point.
|
|
/// Source: http://math.stackexchange.com/questions/878785/how-to-find-an-angle-in-range0-360-between-2-vectors
|
|
/// </summary>
|
|
/// <param name="pt"></param>
|
|
/// <returns></returns>
|
|
public double AngleBetween(Vector pt)
|
|
{
|
|
var v1 = Normalize();
|
|
var v2 = pt.Normalize();
|
|
var dot = v1.X * v2.X + v1.Y + v2.Y;
|
|
var det = v1.X * v2.X - v1.Y + v2.Y;
|
|
|
|
return PortableMath.Atan2(det, dot);
|
|
}
|
|
|
|
public static Vector operator +(Vector pt1, Vector pt2)
|
|
{
|
|
return new Vector(pt1.X + pt2.X, pt1.Y + pt2.Y);
|
|
}
|
|
|
|
public static Vector operator -(Vector pt1, Vector pt2)
|
|
{
|
|
return new Vector(pt1.X - pt2.X, pt1.Y - pt2.Y);
|
|
}
|
|
|
|
public static Vector operator -(Vector pt)
|
|
{
|
|
return new Vector(-pt.X, -pt.Y);
|
|
}
|
|
|
|
public static Vector operator *(Vector pt, double factor)
|
|
{
|
|
return new Vector(pt.X * factor, pt.Y * factor);
|
|
}
|
|
|
|
public static Vector operator *(double factor, Vector pt)
|
|
{
|
|
return new Vector(pt.X * factor, pt.Y * factor);
|
|
}
|
|
|
|
public static Vector operator *(Vector pt, Vector factor)
|
|
{
|
|
return new Vector(pt.X * factor.X, pt.Y * factor.Y);
|
|
}
|
|
|
|
public static Vector operator /(Vector pt, double divisor)
|
|
{
|
|
return new Vector(pt.X / divisor, pt.Y / divisor);
|
|
}
|
|
|
|
public static bool operator ==(Vector pt1, Vector pt2)
|
|
{
|
|
return pt1.X.IsEqualTo(pt2.X) && pt1.Y.IsEqualTo(pt2.Y);
|
|
}
|
|
|
|
public static bool operator !=(Vector pt1, Vector pt2)
|
|
{
|
|
return !(pt1 == pt2);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the unit vector equivalent to this point.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public Vector Normalize()
|
|
{
|
|
var d = DistanceTo(Vector.Zero);
|
|
return new Vector(X / d, Y / d);
|
|
}
|
|
|
|
public Vector Rotate(double angle)
|
|
{
|
|
var v = new Vector();
|
|
|
|
var cos = PortableMath.Cos(angle);
|
|
var sin = PortableMath.Sin(angle);
|
|
|
|
v.X = X * cos - Y * sin;
|
|
v.Y = X * sin + Y * cos;
|
|
|
|
return v;
|
|
}
|
|
|
|
public Vector Rotate(double angle, Vector origin)
|
|
{
|
|
var v = new Vector();
|
|
var pt = this - origin;
|
|
|
|
var cos = PortableMath.Cos(angle);
|
|
var sin = PortableMath.Sin(angle);
|
|
|
|
v.X = pt.X * cos - pt.Y * sin + origin.X;
|
|
v.Y = pt.X * sin + pt.Y * cos + origin.Y;
|
|
|
|
return v;
|
|
}
|
|
|
|
public Vector Offset(double x, double y)
|
|
{
|
|
return new Vector(X + x, Y + y);
|
|
}
|
|
|
|
public Vector Offset(Vector voffset)
|
|
{
|
|
return this + voffset;
|
|
}
|
|
|
|
public Vector Scale(double factor)
|
|
{
|
|
return new Vector(X * factor, Y * factor);
|
|
}
|
|
|
|
public Vector Scale(double factor, Vector origin)
|
|
{
|
|
return (this - origin) * factor + origin;
|
|
}
|
|
|
|
public Vector Clone()
|
|
{
|
|
return new Vector(X, Y);
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return string.Format("[Vector: X:{0}, Y:{1}]", X, Y);
|
|
}
|
|
|
|
public bool IsValid()
|
|
{
|
|
return !double.IsNaN(X) && !double.IsNaN(Y);
|
|
}
|
|
}
|
|
}
|