First commit.

This commit is contained in:
aj
2016-05-16 22:09:19 -04:00
commit f2595d7cba
189 changed files with 26944 additions and 0 deletions

View File

@@ -0,0 +1,538 @@
using System;
using System.Collections.Generic;
namespace OpenNest.Geometry
{
public class Arc : Entity
{
private double radius;
private double startAngle;
private double endAngle;
private Vector center;
private bool reversed;
public Arc()
{
}
public Arc(double x, double y, double r, double a1, double a2, bool reversed = false)
: this(new Vector(x, y), r, a1, a2, reversed)
{
}
public Arc(Vector center, double radius, double startAngle, double endAngle, bool reversed = false)
{
this.center = center;
this.radius = radius;
this.startAngle = startAngle;
this.endAngle = endAngle;
this.reversed = reversed;
UpdateBounds();
}
/// <summary>
/// Center point.
/// </summary>
public Vector Center
{
get { return center; }
set
{
var offset = value - center;
boundingBox.Offset(offset);
center = value;
}
}
/// <summary>
/// Arc radius.
/// </summary>
public double Radius
{
get { return radius; }
set
{
radius = value;
UpdateBounds();
}
}
/// <summary>
/// Arc radius * 2. Value NOT stored.
/// </summary>
public double Diameter
{
get { return Radius * 2.0; }
set { Radius = value / 2.0; }
}
/// <summary>
/// Start angle in radians.
/// </summary>
public double StartAngle
{
get { return startAngle; }
set
{
startAngle = Angle.NormalizeRad(value);
UpdateBounds();
}
}
/// <summary>
/// End angle in radians.
/// </summary>
public double EndAngle
{
get { return endAngle; }
set
{
endAngle = Angle.NormalizeRad(value);
UpdateBounds();
}
}
/// <summary>
/// Angle in radians between start and end angles.
/// </summary>
/// <returns></returns>
public double SweepAngle()
{
var startAngle = StartAngle;
var endAngle = EndAngle;
if (IsReversed)
Generic.Swap(ref startAngle, ref endAngle);
if (startAngle > endAngle)
startAngle -= Angle.TwoPI;
return endAngle - startAngle;
}
/// <summary>
/// Gets or sets if the arc direction is reversed (clockwise).
/// </summary>
public bool IsReversed
{
get { return reversed; }
set
{
if (reversed != value)
Reverse();
}
}
public RotationType Rotation
{
get { return IsReversed ? RotationType.CW : RotationType.CCW; }
set
{
IsReversed = (value == RotationType.CW);
}
}
/// <summary>
/// Start point of the arc.
/// </summary>
/// <returns></returns>
public Vector StartPoint()
{
return new Vector(
Center.X + Radius * Math.Cos(StartAngle),
Center.Y + Radius * Math.Sin(StartAngle));
}
/// <summary>
/// End point of the arc.
/// </summary>
/// <returns></returns>
public Vector EndPoint()
{
return new Vector(
Center.X + Radius * Math.Cos(EndAngle),
Center.Y + Radius * Math.Sin(EndAngle));
}
/// <summary>
/// Returns true if the given arc has the same center point and radius as this.
/// </summary>
/// <param name="arc"></param>
/// <returns></returns>
public bool IsCoradialTo(Arc arc)
{
return center == arc.Center && Radius.IsEqualTo(arc.Radius);
}
/// <summary>
/// Returns true if the given arc has the same radius as this.
/// </summary>
/// <param name="arc"></param>
/// <returns></returns>
public bool IsConcentricTo(Arc arc)
{
return center == arc.center;
}
/// <summary>
/// Returns true if the given circle has the same radius as this.
/// </summary>
/// <param name="circle"></param>
/// <returns></returns>
public bool IsConcentricTo(Circle circle)
{
return center == circle.Center;
}
/// <summary>
/// Converts the arc to a group of points.
/// </summary>
/// <param name="segments">Number of parts to divide the arc into.</param>
/// <returns></returns>
public List<Vector> ToPoints(int segments = 1000)
{
var points = new List<Vector>();
var stepAngle = reversed
? -SweepAngle() / segments
: SweepAngle() / segments;
for (int i = 0; i <= segments; ++i)
{
var angle = stepAngle * i + StartAngle;
points.Add(new Vector(
Math.Cos(angle) * Radius + Center.X,
Math.Sin(angle) * Radius + Center.Y));
}
return points;
}
/// <summary>
/// Linear distance of the arc.
/// </summary>
public override double Length
{
get { return Diameter * Math.PI * SweepAngle() / Angle.TwoPI; }
}
/// <summary>
/// Reverses the rotation direction.
/// </summary>
public override void Reverse()
{
reversed = !reversed;
Generic.Swap(ref startAngle, ref endAngle);
}
/// <summary>
/// Moves the center point to the given coordinates.
/// </summary>
/// <param name="x">The x-coordinate</param>
/// <param name="y">The y-coordinate</param>
public override void MoveTo(double x, double y)
{
Center = new Vector(x, y);
}
/// <summary>
/// Moves the center point to the given point.
/// </summary>
/// <param name="pt">The new center point location.</param>
public override void MoveTo(Vector pt)
{
Center = pt;
}
/// <summary>
/// Offsets the center point by the given distances.
/// </summary>
/// <param name="x">The x-axis offset distance.</param>
/// <param name="y">The y-axis offset distance.</param>
public override void Offset(double x, double y)
{
Center = new Vector(Center.X + x, Center.Y + y);
}
/// <summary>
/// Offsets the center point by the given distances.
/// </summary>
/// <param name="voffset"></param>
public override void Offset(Vector voffset)
{
Center += voffset;
}
/// <summary>
/// Scales the arc from the zero point.
/// </summary>
/// <param name="factor"></param>
public override void Scale(double factor)
{
center *= factor;
radius *= factor;
UpdateBounds();
}
/// <summary>
/// Scales the arc from the origin.
/// </summary>
/// <param name="factor"></param>
/// <param name="origin"></param>
public override void Scale(double factor, Vector origin)
{
center = center.Scale(factor, origin);
radius *= factor;
UpdateBounds();
}
/// <summary>
/// Rotates the arc from the zero point.
/// </summary>
/// <param name="angle"></param>
public override void Rotate(double angle)
{
startAngle += angle;
endAngle += angle;
center = center.Rotate(angle);
UpdateBounds();
}
/// <summary>
/// Rotates the arc from the origin.
/// </summary>
/// <param name="angle"></param>
/// <param name="origin"></param>
public override void Rotate(double angle, Vector origin)
{
startAngle += angle;
endAngle += angle;
center = center.Rotate(angle, origin);
UpdateBounds();
}
/// <summary>
/// Updates the bounding box.
/// </summary>
public override void UpdateBounds()
{
var startpt = StartPoint();
var endpt = EndPoint();
double minX;
double minY;
double maxX;
double maxY;
if (startpt.X < endpt.X)
{
minX = startpt.X;
maxX = endpt.X;
}
else
{
minX = endpt.X;
maxX = startpt.X;
}
if (startpt.Y < endpt.Y)
{
minY = startpt.Y;
maxY = endpt.Y;
}
else
{
minY = endpt.Y;
maxY = startpt.Y;
}
var angle1 = StartAngle;
var angle2 = EndAngle;
// switch the angle to counter clockwise.
if (IsReversed)
Generic.Swap(ref angle1, ref angle2);
if (Angle.IsBetweenRad(Angle.HalfPI, angle1, angle2))
maxY = Center.Y + Radius;
if (Angle.IsBetweenRad(Math.PI, angle1, angle2))
minX = Center.X - Radius;
const double oneHalfPI = Math.PI * 1.5;
if (Angle.IsBetweenRad(oneHalfPI, angle1, angle2))
minY = Center.Y - Radius;
if (Angle.IsBetweenRad(Angle.TwoPI, angle1, angle2))
maxX = Center.X + Radius;
boundingBox.X = minX;
boundingBox.Y = minY;
boundingBox.Width = maxX - minX;
boundingBox.Height = maxY - minY;
}
public override Entity OffsetEntity(double distance, OffsetSide side)
{
if (side == OffsetSide.Left && reversed)
{
return new Arc(center, radius + distance, startAngle, endAngle, reversed);
}
else
{
if (distance >= radius)
return null;
return new Arc(center, radius - distance, startAngle, endAngle, reversed);
}
}
public override Entity OffsetEntity(double distance, Vector pt)
{
throw new NotImplementedException();
}
/// <summary>
/// Gets the closest point on the arc to the given point.
/// </summary>
/// <param name="pt"></param>
/// <returns></returns>
public override Vector ClosestPointTo(Vector pt)
{
var angle = Center.AngleTo(pt);
if (Angle.IsBetweenRad(angle, StartAngle, EndAngle, IsReversed))
{
return new Vector(
Math.Cos(angle) * Radius + Center.X,
Math.Sin(angle) * Radius + Center.Y);
}
else
{
var sp = StartPoint();
var ep = EndPoint();
return pt.DistanceTo(sp) <= pt.DistanceTo(ep) ? sp : ep;
}
}
/// <summary>
/// Returns true if the given arc is intersecting this.
/// </summary>
/// <param name="arc"></param>
/// <returns></returns>
public override bool Intersects(Arc arc)
{
List<Vector> pts;
return Helper.Intersects(this, arc, out pts);
}
/// <summary>
/// Returns true if the given arc is intersecting this.
/// </summary>
/// <param name="arc"></param>
/// <param name="pts">Points of intersection.</param>
/// <returns></returns>
public override bool Intersects(Arc arc, out List<Vector> pts)
{
return Helper.Intersects(this, arc, out pts); ;
}
/// <summary>
/// Returns true if the given circle is intersecting this.
/// </summary>
/// <param name="circle"></param>
/// <returns></returns>
public override bool Intersects(Circle circle)
{
List<Vector> pts;
return Helper.Intersects(this, circle, out pts);
}
/// <summary>
/// Returns true if the given circle is intersecting this.
/// </summary>
/// <param name="circle"></param>
/// <param name="pts">Points of intersection.</param>
/// <returns></returns>
public override bool Intersects(Circle circle, out List<Vector> pts)
{
return Helper.Intersects(this, circle, out pts);
}
/// <summary>
/// Returns true if the given line is intersecting this.
/// </summary>
/// <param name="line"></param>
/// <returns></returns>
public override bool Intersects(Line line)
{
List<Vector> pts;
return Helper.Intersects(this, line, out pts);
}
/// <summary>
/// Returns true if the given line is intersecting this.
/// </summary>
/// <param name="line"></param>
/// <param name="pts">Points of intersection.</param>
/// <returns></returns>
public override bool Intersects(Line line, out List<Vector> pts)
{
return Helper.Intersects(this, line, out pts);
}
/// <summary>
/// Returns true if the given polygon is intersecting this.
/// </summary>
/// <param name="polygon"></param>
/// <returns></returns>
public override bool Intersects(Polygon polygon)
{
List<Vector> pts;
return Helper.Intersects(this, polygon, out pts);
}
/// <summary>
/// Returns true if the given polygon is intersecting this.
/// </summary>
/// <param name="polygon"></param>
/// <param name="pts">Points of intersection.</param>
/// <returns></returns>
public override bool Intersects(Polygon polygon, out List<Vector> pts)
{
return Helper.Intersects(this, polygon, out pts);
}
/// <summary>
/// Returns true if the given shape is intersecting this.
/// </summary>
/// <param name="shape"></param>
/// <returns></returns>
public override bool Intersects(Shape shape)
{
List<Vector> pts;
return Helper.Intersects(this, shape, out pts);
}
/// <summary>
/// Returns true if the given shape is intersecting this.
/// </summary>
/// <param name="shape"></param>
/// <param name="pts">Points of intersection.</param>
/// <returns></returns>
public override bool Intersects(Shape shape, out List<Vector> pts)
{
return Helper.Intersects(this, shape, out pts);
}
/// <summary>
/// Type of entity.
/// </summary>
public override EntityType Type
{
get { return EntityType.Arc; }
}
}
}

View File

@@ -0,0 +1,415 @@
using System;
using System.Collections.Generic;
namespace OpenNest.Geometry
{
public class Circle : Entity
{
private Vector center;
private double radius;
public Circle()
{
}
public Circle(double x, double y, double radius)
: this(new Vector(x, y), radius)
{
}
public Circle(Vector center, double radius)
{
this.center = center;
this.radius = radius;
this.Rotation = RotationType.CCW;
UpdateBounds();
}
/// <summary>
/// Creates a circle from two points.
/// </summary>
/// <param name="pt1"></param>
/// <param name="pt2"></param>
/// <returns></returns>
public static Circle CreateFrom2Points(Vector pt1, Vector pt2)
{
var line = new Line(pt1, pt2);
return new Circle(line.MidPoint, line.Length * 0.5);
}
/// <summary>
/// Center point of the circle.
/// </summary>
public Vector Center
{
get { return center; }
set
{
var offset = value - center;
boundingBox.Offset(offset);
center = value;
}
}
/// <summary>
/// Radius of the circle.
/// </summary>
public double Radius
{
get { return radius; }
set
{
radius = value;
UpdateBounds();
}
}
/// <summary>
/// Radius * 2. Value NOT stored.
/// </summary>
public double Diameter
{
get { return Radius * 2.0; }
set { Radius = value / 2.0; }
}
/// <summary>
/// Rotation direction.
/// </summary>
public RotationType Rotation { get; set; }
/// <summary>
/// Area of the circle.
/// </summary>
/// <returns></returns>
public double Area()
{
return Math.PI * Radius * Radius;
}
/// <summary>
/// Linear distance around the circle.
/// </summary>
/// <returns></returns>
public double Circumference()
{
return Math.PI * Diameter;
}
/// <summary>
/// Returns true if the given circle has the same radius as this.
/// </summary>
/// <param name="circle"></param>
/// <returns></returns>
public bool IsConcentricTo(Circle circle)
{
return center == circle.center;
}
/// <summary>
/// Returns true if the given circle has the same radius as this.
/// </summary>
/// <param name="arc"></param>
/// <returns></returns>
public bool IsConcentricTo(Arc arc)
{
return center == arc.Center;
}
public bool ContainsPoint(Vector pt)
{
return Center.DistanceTo(pt) <= Radius;
}
public List<Vector> ToPoints(int segments = 1000)
{
var points = new List<Vector>();
var stepAngle = Angle.TwoPI / segments;
for (int i = 0; i <= segments; ++i)
{
var angle = stepAngle * i;
points.Add(new Vector(
Math.Cos(angle) * Radius + Center.X,
Math.Sin(angle) * Radius + Center.Y));
}
return points;
}
/// <summary>
/// Linear distance around the circle.
/// </summary>
public override double Length
{
get { return Circumference(); }
}
/// <summary>
/// Reverses the rotation direction.
/// </summary>
public override void Reverse()
{
if (Rotation == RotationType.CCW)
Rotation = RotationType.CW;
else
Rotation = RotationType.CCW;
}
/// <summary>
/// Moves the center point to the given coordinates.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
public override void MoveTo(double x, double y)
{
Center = new Vector(x, y);
}
/// <summary>
/// Moves the center point to the given point.
/// </summary>
/// <param name="pt"></param>
public override void MoveTo(Vector pt)
{
Center = pt;
}
/// <summary>
/// Offsets the center point by the given distances.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
public override void Offset(double x, double y)
{
Center = new Vector(Center.X + x, Center.Y + y);
}
/// <summary>
/// Offsets the center point by the given distances.
/// </summary>
/// <param name="voffset"></param>
public override void Offset(Vector voffset)
{
Center += voffset;
}
/// <summary>
/// Scales the circle from the zero point.
/// </summary>
/// <param name="factor"></param>
public override void Scale(double factor)
{
center *= factor;
radius *= factor;
UpdateBounds();
}
/// <summary>
/// Scales the circle from the origin.
/// </summary>
/// <param name="factor"></param>
/// <param name="origin"></param>
public override void Scale(double factor, Vector origin)
{
center = center.Scale(factor, origin);
radius *= factor;
UpdateBounds();
}
/// <summary>
/// Rotates the circle from the zero point.
/// </summary>
/// <param name="angle"></param>
public override void Rotate(double angle)
{
Center = Center.Rotate(angle);
}
/// <summary>
/// /// Rotates the circle from the origin.
/// </summary>
/// <param name="angle"></param>
/// <param name="origin"></param>
public override void Rotate(double angle, Vector origin)
{
Center = Center.Rotate(angle, origin);
}
/// <summary>
/// Updates the bounding box.
/// </summary>
public override void UpdateBounds()
{
boundingBox.X = Center.X - Radius;
boundingBox.Y = Center.Y - Radius;
boundingBox.Width = Diameter;
boundingBox.Height = Diameter;
}
public override Entity OffsetEntity(double distance, OffsetSide side)
{
if (side == OffsetSide.Left && Rotation == RotationType.CCW)
{
return Radius <= distance ? null : new Circle(center, Radius - distance)
{
Layer = Layer,
Rotation = Rotation
};
}
else
{
return new Circle(center, Radius + distance) { Layer = Layer };
}
}
public override Entity OffsetEntity(double distance, Vector pt)
{
if (ContainsPoint(pt))
{
return Radius <= distance ? null : new Circle(center, Radius - distance)
{
Layer = Layer,
Rotation = Rotation
};
}
else
{
return new Circle(center, Radius + distance) { Layer = Layer };
}
}
/// <summary>
/// Gets the closest point on the circle to the specified point.
/// </summary>
/// <param name="pt"></param>
/// <returns></returns>
public override Vector ClosestPointTo(Vector pt)
{
var angle = Center.AngleTo(pt);
return new Vector(
Math.Cos(angle) * Radius + Center.X,
Math.Sin(angle) * Radius + Center.Y);
}
/// <summary>
/// Returns true if the given arc is intersecting this.
/// </summary>
/// <param name="arc"></param>
/// <returns></returns>
public override bool Intersects(Arc arc)
{
List<Vector> pts;
return Helper.Intersects(arc, this, out pts);
}
/// <summary>
/// Returns true if the given arc is intersecting this.
/// </summary>
/// <param name="arc"></param>
/// <param name="pts">Points of intersection.</param>
/// <returns></returns>
public override bool Intersects(Arc arc, out List<Vector> pts)
{
return Helper.Intersects(arc, this, out pts);
}
/// <summary>
/// Returns true if the given circle is intersecting this.
/// </summary>
/// <param name="circle"></param>
/// <returns></returns>
public override bool Intersects(Circle circle)
{
var dist = Center.DistanceTo(circle.Center);
return (dist < (Radius + circle.Radius) && dist > Math.Abs(Radius - circle.Radius));
}
/// <summary>
/// Returns true if the given circle is intersecting this.
/// </summary>
/// <param name="circle"></param>
/// <param name="pts">Points of intersection.</param>
/// <returns></returns>
public override bool Intersects(Circle circle, out List<Vector> pts)
{
return Helper.Intersects(this, circle, out pts);
}
/// <summary>
/// Returns true if the given line is intersecting this.
/// </summary>
/// <param name="line"></param>
/// <returns></returns>
public override bool Intersects(Line line)
{
List<Vector> pts;
return Helper.Intersects(this, line, out pts);
}
/// <summary>
/// Returns true if the given line is intersecting this.
/// </summary>
/// <param name="line"></param>
/// <param name="pts">Points of intersection.</param>
/// <returns></returns>
public override bool Intersects(Line line, out List<Vector> pts)
{
return Helper.Intersects(this, line, out pts);
}
/// <summary>
/// Returns true if the given polygon is intersecting this.
/// </summary>
/// <param name="polygon"></param>
/// <returns></returns>
public override bool Intersects(Polygon polygon)
{
List<Vector> pts;
return Helper.Intersects(this, polygon, out pts);
}
/// <summary>
/// Returns true if the given polygon is intersecting this.
/// </summary>
/// <param name="polygon"></param>
/// <param name="pts">Points of intersection.</param>
/// <returns></returns>
public override bool Intersects(Polygon polygon, out List<Vector> pts)
{
return Helper.Intersects(this, polygon, out pts);
}
/// <summary>
/// Returns true if the given shape is intersecting this.
/// </summary>
/// <param name="shape"></param>
/// <returns></returns>
public override bool Intersects(Shape shape)
{
List<Vector> pts;
return Helper.Intersects(this, shape, out pts);
}
/// <summary>
/// Returns true if the given shape is intersecting this.
/// </summary>
/// <param name="shape"></param>
/// <param name="pts">Points of intersection.</param>
/// <returns></returns>
public override bool Intersects(Shape shape, out List<Vector> pts)
{
return Helper.Intersects(this, shape, out pts);
}
/// <summary>
/// Type of entity.
/// </summary>
public override EntityType Type
{
get { return EntityType.Circle; }
}
}
}

View File

@@ -0,0 +1,42 @@
using System.Collections.Generic;
namespace OpenNest.Geometry
{
public class DefinedShape
{
public DefinedShape(Shape shape)
{
Update(shape.Entities);
}
public DefinedShape(List<Entity> entities)
{
Update(entities);
}
private void Update(List<Entity> entities)
{
var shapes = Helper.GetShapes(entities);
Perimeter = shapes[0];
Cutouts = new List<Shape>();
for (int i = 1; i < shapes.Count; i++)
{
if (shapes[i].Left < Perimeter.Left)
{
Cutouts.Add(Perimeter);
Perimeter = shapes[i];
}
else
{
Cutouts.Add(shapes[i]);
}
}
}
public Shape Perimeter { get; set; }
public List<Shape> Cutouts { get; set; }
}
}

View File

@@ -0,0 +1,268 @@
using System.Collections.Generic;
namespace OpenNest.Geometry
{
public abstract class Entity : IBoundable
{
protected Box boundingBox;
protected Entity()
{
Layer = OpenNest.Geometry.Layer.Default;
boundingBox = new Box();
}
/// <summary>
/// Smallest box that contains the entity.
/// </summary>
public Box BoundingBox
{
get { return boundingBox; }
}
/// <summary>
/// Entity layer type.
/// </summary>
public Layer Layer { get; set; }
/// <summary>
/// X-Coordinate of the left-most point.
/// </summary>
public virtual double Left
{
get { return boundingBox.Left; }
}
/// <summary>
/// X-Coordinate of the right-most point.
/// </summary>
public virtual double Right
{
get { return boundingBox.Right; }
}
/// <summary>
/// Y-Coordinate of the highest point.
/// </summary>
public virtual double Top
{
get { return boundingBox.Top; }
}
/// <summary>
/// Y-Coordinate of the lowest point.
/// </summary>
public virtual double Bottom
{
get { return boundingBox.Bottom; }
}
/// <summary>
/// Length of the entity.
/// </summary>
public abstract double Length { get; }
/// <summary>
/// Reverses the entity.
/// </summary>
public abstract void Reverse();
/// <summary>
/// Moves the entity location to the given coordinates.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
public abstract void MoveTo(double x, double y);
/// <summary>
/// Moves the entity location to the given point.
/// </summary>
/// <param name="pt"></param>
public abstract void MoveTo(Vector pt);
/// <summary>
/// Offsets the entity location by the given distances.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
public abstract void Offset(double x, double y);
/// <summary>
/// Offsets the entity location by the given distances.
/// </summary>
/// <param name="voffset"></param>
public abstract void Offset(Vector voffset);
/// <summary>
/// Scales the entity from the zero point.
/// </summary>
/// <param name="factor"></param>
public abstract void Scale(double factor);
/// <summary>
/// Scales the entity from the origin.
/// </summary>
/// <param name="factor"></param>
/// <param name="origin"></param>
public abstract void Scale(double factor, Vector origin);
/// <summary>
/// Rotates the entity from the zero point.
/// </summary>
/// <param name="angle"></param>
public abstract void Rotate(double angle);
/// <summary>
/// Rotates the entity from the origin.
/// </summary>
/// <param name="angle"></param>
/// <param name="origin"></param>
public abstract void Rotate(double angle, Vector origin);
/// <summary>
/// Updates the bounding box.
/// </summary>
public abstract void UpdateBounds();
/// <summary>
/// Gets a new entity offset the given distance from this entity.
/// </summary>
/// <param name="distance"></param>
/// <param name="side"></param>
/// <returns></returns>
public abstract Entity OffsetEntity(double distance, OffsetSide side);
/// <summary>
/// Gets a new entity offset the given distance from this entity. Offset side determined by point.
/// </summary>
/// <param name="distance"></param>
/// <param name="pt"></param>
/// <returns></returns>
public abstract Entity OffsetEntity(double distance, Vector pt);
/// <summary>
/// Gets the closest point on the entity to the given point.
/// </summary>
/// <param name="pt"></param>
/// <returns></returns>
public abstract Vector ClosestPointTo(Vector pt);
/// <summary>
/// Returns true if the given arc is intersecting this.
/// </summary>
/// <param name="arc"></param>
/// <returns></returns>
public abstract bool Intersects(Arc arc);
/// <summary>
/// Returns true if the given arc is intersecting this.
/// </summary>
/// <param name="arc"></param>
/// <param name="pts">List to store the points of intersection.</param>
/// <returns></returns>
public abstract bool Intersects(Arc arc, out List<Vector> pts);
/// <summary>
/// Returns true if the given circle is intersecting this.
/// </summary>
/// <param name="circle"></param>
/// <returns></returns>
public abstract bool Intersects(Circle circle);
/// <summary>
/// Returns true if the given circle is intersecting this.
/// </summary>
/// <param name="circle"></param>
/// <param name="pts"></param>
/// <returns></returns>
public abstract bool Intersects(Circle circle, out List<Vector> pts);
/// <summary>
/// Returns true if the given line is intersecting this.
/// </summary>
/// <param name="line"></param>
/// <returns></returns>
public abstract bool Intersects(Line line);
/// <summary>
/// Returns true if the given line is intersecting this.
/// </summary>
/// <param name="line"></param>
/// <param name="pts"></param>
/// <returns></returns>
public abstract bool Intersects(Line line, out List<Vector> pts);
/// <summary>
/// Returns true if the given polygon is intersecting this.
/// </summary>
/// <param name="polygon"></param>
/// <returns></returns>
public abstract bool Intersects(Polygon polygon);
/// <summary>
/// Returns true if the given polygon is intersecting this.
/// </summary>
/// <param name="polygon"></param>
/// <param name="pts"></param>
/// <returns></returns>
public abstract bool Intersects(Polygon polygon, out List<Vector> pts);
/// <summary>
/// Returns true if the given shape is intersecting this.
/// </summary>
/// <param name="shape"></param>
/// <returns></returns>
public abstract bool Intersects(Shape shape);
/// <summary>
/// Returns true if the given shape is intersecting this.
/// </summary>
/// <param name="shape"></param>
/// <param name="pts"></param>
/// <returns></returns>
public abstract bool Intersects(Shape shape, out List<Vector> pts);
/// <summary>
/// Type of entity.
/// </summary>
public abstract EntityType Type { get; }
}
public static class EntityExtensions
{
public static double FindBestRotation(this List<Entity> entities, double stepAngle, double startAngle = 0, double endAngle = Angle.TwoPI)
{
startAngle = Angle.NormalizeRad(startAngle);
if (!endAngle.IsEqualTo(Angle.TwoPI))
endAngle = Angle.NormalizeRad(endAngle);
if (stepAngle.IsEqualTo(0.0))
return startAngle;
entities.ForEach(e => e.Rotate(startAngle));
var bestAngle = startAngle;
var bestArea = entities.GetBoundingBox().Area();
var steps = startAngle < endAngle
? (endAngle - startAngle) / stepAngle
: (endAngle + Angle.TwoPI) - startAngle / stepAngle;
for (int i = 1; i <= steps; ++i)
{
entities.ForEach(e => e.Rotate(stepAngle));
var area = entities.GetBoundingBox().Area();
if (area < bestArea)
{
bestArea = area;
bestAngle = startAngle + stepAngle * i;
}
}
return bestAngle;
}
}
}

View File

@@ -0,0 +1,12 @@

namespace OpenNest.Geometry
{
public enum EntityType
{
Arc,
Circle,
Line,
Shape,
Polygon
}
}

View File

@@ -0,0 +1,29 @@
using System.Drawing;
namespace OpenNest.Geometry
{
public class Layer
{
public static readonly Layer Default = new Layer("0")
{
Color = Color.White,
IsVisible = true
};
public Layer(string name)
{
Name = name;
}
public string Name { get; set; }
public bool IsVisible { get; set; }
public Color Color { get; set; }
public override string ToString()
{
return Name;
}
}
}

View File

@@ -0,0 +1,552 @@
using System;
using System.Collections.Generic;
namespace OpenNest.Geometry
{
public class Line : Entity
{
private Vector pt1;
private Vector pt2;
public Line()
{
}
public Line(double x1, double y1, double x2, double y2)
: this(new Vector(x1, y1), new Vector(x2, y2))
{
}
public Line(Vector startPoint, Vector endPoint)
{
pt1 = startPoint;
pt2 = endPoint;
UpdateBounds();
}
/// <summary>
/// Start point of the line.
/// </summary>
public Vector StartPoint
{
get { return pt1; }
set
{
pt1 = value;
UpdateBounds();
}
}
/// <summary>
/// Mid-point of the line.
/// </summary>
public Vector MidPoint
{
get
{
var x = (pt1.X + pt2.X) * 0.5;
var y = (pt1.Y + pt2.Y) * 0.5;
return new Vector(x, y);
}
}
/// <summary>
/// End point of the line.
/// </summary>
public Vector EndPoint
{
get { return pt2; }
set
{
pt2 = value;
UpdateBounds();
}
}
/// <summary>
/// Gets the point on the line that is perpendicular from the given point.
/// </summary>
/// <param name="pt"></param>
/// <returns></returns>
public Vector PointPerpendicularFrom(Vector pt)
{
var diff1 = pt - StartPoint;
var diff2 = EndPoint - StartPoint;
var dotProduct = diff1.X * diff2.X + diff1.Y * diff2.Y;
var lengthSquared = diff2.X * diff2.X + diff2.Y * diff2.Y;
var param = dotProduct / lengthSquared;
if (param < 0)
return StartPoint;
else if (param > 1)
return EndPoint;
else
{
return new Vector(
StartPoint.X + param * diff2.X,
StartPoint.Y + param * diff2.Y);
}
}
/// <summary>
/// Returns true if the given line is parallel to this.
/// </summary>
/// <param name="line"></param>
/// <returns></returns>
public bool IsParallelTo(Line line)
{
bool line1Vertical = IsVertical();
bool line2Vertical = line.IsVertical();
if (line1Vertical)
return line2Vertical;
else if (line2Vertical)
return false;
return Slope().IsEqualTo(line.Slope());
}
/// <summary>
/// Returns true if the given line is perpendicular to this.
/// </summary>
/// <param name="line"></param>
/// <returns></returns>
public bool IsPerpendicularTo(Line line)
{
bool line1Vertical = IsVertical();
bool line2Vertical = line.IsVertical();
if (line1Vertical)
return line.IsHorizontal();
else if (line.IsVertical())
return IsHorizontal();
return Slope().IsEqualTo(-1 / line.Slope());
}
/// <summary>
/// Returns true if the given line is intersecting this.
/// </summary>
/// <param name="line"></param>
/// <param name="pt">Point of intersection.</param>
/// <returns></returns>
public bool Intersects(Line line, out Vector pt)
{
var a1 = EndPoint.Y - StartPoint.Y;
var b1 = StartPoint.X - EndPoint.X;
var c1 = a1 * StartPoint.X + b1 * StartPoint.Y;
var a2 = line.EndPoint.Y - line.StartPoint.Y;
var b2 = line.StartPoint.X - line.EndPoint.X;
var c2 = a2 * line.StartPoint.X + b2 * line.StartPoint.Y;
var d = a1 * b2 - a2 * b1;
if (d.IsEqualTo(0.0))
{
pt = new Vector();
return false;
}
else
{
var x = (b2 * c1 - b1 * c2) / d;
var y = (a1 * c2 - a2 * c1) / d;
pt = new Vector(x, y);
return boundingBox.Contains(pt) && line.boundingBox.Contains(pt);
}
}
/// <summary>
/// Returns true if this is vertical.
/// </summary>
/// <returns></returns>
public bool IsVertical()
{
return pt1.X.IsEqualTo(pt2.X);
}
/// <summary>
/// Returns true if this is horizontal.
/// </summary>
/// <returns></returns>
public bool IsHorizontal()
{
return pt1.Y.IsEqualTo(pt2.Y);
}
/// <summary>
/// Returns true if the given line is collinear to this.
/// </summary>
/// <param name="line"></param>
/// <returns></returns>
public bool IsCollinearTo(Line line)
{
if (IsVertical())
{
if (!line.IsVertical())
return false;
return StartPoint.X.IsEqualTo(line.StartPoint.X);
}
else if (line.IsVertical())
return false;
if (!YIntercept().IsEqualTo(line.YIntercept()))
return false;
if (!Slope().IsEqualTo(line.Slope()))
return false;
return true;
}
/// <summary>
/// Angle of the line from start point to end point.
/// </summary>
/// <returns></returns>
public double Angle()
{
return StartPoint.AngleTo(EndPoint);
}
/// <summary>
/// Returns the angle between the two lines in radians.
/// </summary>
/// <param name="line"></param>
/// <returns></returns>
public double AngleBetween(Line line)
{
var m1 = Slope();
var m2 = line.Slope();
return Math.Atan(Math.Abs((m2 - m1) / (1 + m2 * m1)));
}
/// <summary>
/// Slope of the line.
/// </summary>
/// <returns></returns>
public double Slope()
{
if (IsVertical())
throw new DivideByZeroException();
return (EndPoint.Y - StartPoint.Y) / (EndPoint.X - StartPoint.X);
}
/// <summary>
/// Gets the y-axis intersection coordinate.
/// </summary>
/// <returns></returns>
public double YIntercept()
{
return StartPoint.Y - Slope() * StartPoint.X;
}
/// <summary>
/// Length of the line from start point to end point.
/// </summary>
public override double Length
{
get
{
var x = EndPoint.X - StartPoint.X;
var y = EndPoint.Y - StartPoint.Y;
return Math.Sqrt(x * x + y * y);
}
}
/// <summary>
/// Reversed the line.
/// </summary>
public override void Reverse()
{
Generic.Swap<Vector>(ref pt1, ref pt2);
}
/// <summary>
/// Moves the start point to the given coordinates.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
public override void MoveTo(double x, double y)
{
var xoffset = pt1.X - x;
var yoffset = pt1.Y - y;
pt2.X += xoffset;
pt2.Y += yoffset;
pt1.X = x;
pt1.Y = y;
boundingBox.Offset(xoffset, yoffset);
}
/// <summary>
/// Moves the start point to the given point.
/// </summary>
/// <param name="pt"></param>
public override void MoveTo(Vector pt)
{
var offset = pt1 - pt;
pt2 += offset;
pt1 = pt;
boundingBox.Offset(offset);
}
/// <summary>
/// Offsets the line location by the given distances.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
public override void Offset(double x, double y)
{
pt2.X += x;
pt2.Y += y;
pt1.X += x;
pt1.Y += y;
boundingBox.Offset(x, y);
}
/// <summary>
/// Offsets the line location by the given distances.
/// </summary>
/// <param name="voffset"></param>
public override void Offset(Vector voffset)
{
pt1 += voffset;
pt2 += voffset;
boundingBox.Offset(voffset);
}
/// <summary>
/// Scales the line from the zero point.
/// </summary>
/// <param name="factor"></param>
public override void Scale(double factor)
{
pt1 *= factor;
pt2 *= factor;
}
/// <summary>
/// Scales the line from the origin.
/// </summary>
/// <param name="factor"></param>
/// <param name="origin"></param>
public override void Scale(double factor, Vector origin)
{
pt1 = (pt1 - origin) * factor + origin;
pt2 = (pt2 - origin) * factor + origin;
}
/// <summary>
/// Rotates the line from the zero point.
/// </summary>
/// <param name="angle"></param>
public override void Rotate(double angle)
{
StartPoint = StartPoint.Rotate(angle);
EndPoint = EndPoint.Rotate(angle);
}
/// <summary>
/// Rotates the line from the origin.
/// </summary>
/// <param name="angle"></param>
/// <param name="origin"></param>
public override void Rotate(double angle, Vector origin)
{
StartPoint = StartPoint.Rotate(angle, origin);
EndPoint = EndPoint.Rotate(angle, origin);
}
/// <summary>
/// Updates the bounding box.
/// </summary>
public override sealed void UpdateBounds()
{
if (StartPoint.X < EndPoint.X)
{
boundingBox.X = StartPoint.X;
boundingBox.Width = EndPoint.X - StartPoint.X;
}
else
{
boundingBox.X = EndPoint.X;
boundingBox.Width = StartPoint.X - EndPoint.X;
}
if (StartPoint.Y < EndPoint.Y)
{
boundingBox.Y = StartPoint.Y;
boundingBox.Height = EndPoint.Y - StartPoint.Y;
}
else
{
boundingBox.Y = EndPoint.Y;
boundingBox.Height = StartPoint.Y - EndPoint.Y;
}
}
public override Entity OffsetEntity(double distance, OffsetSide side)
{
var angle = OpenNest.Angle.NormalizeRad(Angle() + OpenNest.Angle.HalfPI);
var x = Math.Cos(angle) * distance;
var y = Math.Sin(angle) * distance;
var pt = new Vector(x, y);
return side == OffsetSide.Left
? new Line(StartPoint + pt, EndPoint + pt)
: new Line(EndPoint + pt, StartPoint + pt);
}
public override Entity OffsetEntity(double distance, Vector pt)
{
var a = pt - StartPoint;
var b = EndPoint - StartPoint;
var c = a.DotProduct(b);
var side = c < 0 ? OffsetSide.Left : OffsetSide.Right;
return OffsetEntity(distance, side);
}
/// <summary>
/// Gets the closest point on the line to the given point.
/// </summary>
/// <param name="pt"></param>
/// <returns></returns>
public override Vector ClosestPointTo(Vector pt)
{
var perpendicularPt = PointPerpendicularFrom(pt);
if (BoundingBox.Contains(perpendicularPt))
return perpendicularPt;
else
return pt.DistanceTo(StartPoint) <= pt.DistanceTo(EndPoint) ? StartPoint : EndPoint;
}
/// <summary>
/// Returns true if the given arc is intersecting this.
/// </summary>
/// <param name="arc"></param>
/// <returns></returns>
public override bool Intersects(Arc arc)
{
List<Vector> pts;
return Helper.Intersects(arc, this, out pts);
}
/// <summary>
/// Returns true if the given arc is intersecting this.
/// </summary>
/// <param name="arc"></param>
/// <param name="pts"></param>
/// <returns></returns>
public override bool Intersects(Arc arc, out List<Vector> pts)
{
return Helper.Intersects(arc, this, out pts);
}
/// <summary>
/// Returns true if the given circle is intersecting this.
/// </summary>
/// <param name="circle"></param>
/// <returns></returns>
public override bool Intersects(Circle circle)
{
List<Vector> pts;
return Helper.Intersects(circle, this, out pts);
}
/// <summary>
/// Returns true if the given circle is intersecting this.
/// </summary>
/// <param name="circle"></param>
/// <param name="pts"></param>
/// <returns></returns>
public override bool Intersects(Circle circle, out List<Vector> pts)
{
return Helper.Intersects(circle, this, out pts);
}
/// <summary>
/// Returns true if the given line is intersecting this.
/// </summary>
/// <param name="line"></param>
/// <returns></returns>
public override bool Intersects(Line line)
{
Vector pt;
return Intersects(line, out pt);
}
/// <summary>
/// Returns true if the given line is intersecting this.
/// </summary>
/// <param name="line"></param>
/// <param name="pts"></param>
/// <returns></returns>
public override bool Intersects(Line line, out List<Vector> pts)
{
Vector pt;
var success = Helper.Intersects(this, line, out pt);
pts = new List<Vector>(new[] { pt });
return success;
}
/// <summary>
/// Returns true if the given polygon is intersecting this.
/// </summary>
/// <param name="polygon"></param>
/// <returns></returns>
public override bool Intersects(Polygon polygon)
{
List<Vector> pts;
return Helper.Intersects(this, polygon, out pts);
}
/// <summary>
/// Returns true if the given polygon is intersecting this.
/// </summary>
/// <param name="polygon"></param>
/// <param name="pts"></param>
/// <returns></returns>
public override bool Intersects(Polygon polygon, out List<Vector> pts)
{
return Helper.Intersects(this, polygon, out pts);
}
/// <summary>
/// Returns true if the given shape is intersecting this.
/// </summary>
/// <param name="shape"></param>
/// <returns></returns>
public override bool Intersects(Shape shape)
{
List<Vector> pts;
return Helper.Intersects(this, shape, out pts);
}
/// <summary>
/// Returns true if the given shape is intersecting this.
/// </summary>
/// <param name="shape"></param>
/// <param name="pts"></param>
/// <returns></returns>
public override bool Intersects(Shape shape, out List<Vector> pts)
{
return Helper.Intersects(this, shape, out pts);
}
/// <summary>
/// Type of entity.
/// </summary>
public override EntityType Type
{
get { return EntityType.Line; }
}
}
}

View File

@@ -0,0 +1,500 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace OpenNest.Geometry
{
public class Polygon : Entity
{
public List<Vector> Vertices;
public Polygon()
{
Vertices = new List<Vector>();
}
/// <summary>
/// Closes the polygon if it's not already.
/// </summary>
public void Close()
{
if (Vertices.Count < 3)
return;
var first = Vertices.First();
var last = Vertices.Last();
if (first != last)
Vertices.Add(first);
}
/// <summary>
/// Returns true if the polygon is closed.
/// </summary>
/// <returns></returns>
public bool IsClosed()
{
if (Vertices.Count < 3)
return false;
return (Vertices.First() == Vertices.Last());
}
/// <summary>
/// Returns true if the polygon is self intersecting.
/// </summary>
/// <returns></returns>
public bool IsComplex()
{
var lines = ToLines();
for (int i = 0; i < lines.Count; ++i)
{
var line1 = lines[i];
for (int j = i; j < lines.Count; ++j)
{
var line2 = lines[j];
if (line1.Intersects(line2))
return true;
}
}
return false;
}
/// <summary>
/// Area of the polygon.
/// </summary>
/// <returns>Returns the area or 0 if the polygon is NOT closed.</returns>
public double Area()
{
if (Vertices.Count < 3)
return 0.0;
return Math.Abs(CalculateArea());
}
/// <summary>
/// Distance around the polygon.
/// </summary>
/// <returns></returns>
public double Perimeter()
{
if (Vertices.Count < 3)
return 0.0;
double sum = 0.0;
var last = Vertices[0];
for (int i = 1; i < Vertices.Count; ++i)
{
var current = Vertices[i];
sum += last.DistanceTo(current);
last = current;
}
return sum;
}
/// <summary>
/// Gets the rotation direction of the polygon.
/// </summary>
/// <returns></returns>
public RotationType RotationDirection()
{
if (Vertices.Count < 3)
throw new Exception("Not enough points to determine direction. Must have at least 3 points.");
return CalculateArea() > 0 ? RotationType.CCW : RotationType.CW;
}
/// <summary>
/// Converts the polygon to a group of lines.
/// </summary>
/// <returns></returns>
public List<Line> ToLines()
{
var list = new List<Line>();
if (Vertices.Count < 2)
return list;
var last = Vertices[0];
for (int i = 1; i < Vertices.Count; ++i)
{
var current = Vertices[i];
list.Add(new Line(last, current));
last = current;
}
return list;
}
/// <summary>
/// Gets the area of the polygon.
/// </summary>
/// <returns>
/// Returns the area of the polygon.
/// * Positive number = counter-clockwise rotation
/// * Negative number = clockwise rotation
/// </returns>
private double CalculateArea()
{
double xsum = 0;
double ysum = 0;
for (int i = 0; i < Vertices.Count - 1; ++i)
{
var current = Vertices[i];
var next = Vertices[i + 1];
xsum += current.X * next.Y;
ysum += current.Y * next.X;
}
return (xsum - ysum) * 0.5;
}
/// <summary>
/// Distance around the polygon.
/// </summary>
public override double Length
{
get { return Perimeter(); }
}
/// <summary>
/// Reverses the rotation direction of the polygon.
/// </summary>
public override void Reverse()
{
Vertices.Reverse();
}
/// <summary>
/// Moves the start point to the given coordinates.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
public override void MoveTo(double x, double y)
{
if (Vertices.Count == 0)
return;
var first = Vertices[0];
var offset = new Vector(x - first.X, y - first.Y);
Vertices.ForEach(vertex => vertex += offset);
boundingBox.Offset(offset);
}
/// <summary>
/// Moves the start point to the given point.
/// </summary>
/// <param name="pt"></param>
public override void MoveTo(Vector pt)
{
if (Vertices.Count == 0)
return;
var first = Vertices[0];
var offset = pt - first;
Vertices.ForEach(vertex => vertex += offset);
boundingBox.Offset(offset);
}
/// <summary>
/// Offsets the location by the given distances.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
public override void Offset(double x, double y)
{
for (int i = 0; i < Vertices.Count; i++)
Vertices[i] = Vertices[i].Offset(x, y);
boundingBox.Offset(x, y);
}
/// <summary>
/// Offsets the location by the given distances.
/// </summary>
/// <param name="voffset"></param>
public override void Offset(Vector voffset)
{
for (int i = 0; i < Vertices.Count; i++)
Vertices[i] = Vertices[i].Offset(voffset);
boundingBox.Offset(voffset);
}
/// <summary>
/// Scales the polygon from the zero point.
/// </summary>
/// <param name="factor"></param>
public override void Scale(double factor)
{
for (int i = 0; i < Vertices.Count; i++)
Vertices[i] *= factor;
UpdateBounds();
}
/// <summary>
/// Scales the polygon from the zero point.
/// </summary>
/// <param name="factor"></param>
/// <param name="origin"></param>
public override void Scale(double factor, Vector origin)
{
for (int i = 0; i < Vertices.Count; i++)
Vertices[i] = (Vertices[i] - origin) * factor + origin;
UpdateBounds();
}
/// <summary>
/// Rotates the polygon from the zero point.
/// </summary>
/// <param name="angle"></param>
public override void Rotate(double angle)
{
for (int i = 0; i < Vertices.Count; i++)
Vertices[i] = Vertices[i].Rotate(angle);
UpdateBounds();
}
/// <summary>
/// Rotates the polygon from the origin.
/// </summary>
/// <param name="angle"></param>
/// <param name="origin"></param>
public override void Rotate(double angle, Vector origin)
{
for (int i = 0; i < Vertices.Count; i++)
Vertices[i] = Vertices[i].Rotate(angle, origin);
UpdateBounds();
}
/// <summary>
/// Updates the bounding box.
/// </summary>
public override void UpdateBounds()
{
if (Vertices.Count == 0)
return;
var first = Vertices[0];
var minX = first.X;
var maxX = first.X;
var minY = first.Y;
var maxY = first.Y;
for (int i = 1; i < Vertices.Count; ++i)
{
var vertex = Vertices[i];
if (vertex.X < minX) minX = vertex.X;
else if (vertex.X > maxX) maxX = vertex.X;
if (vertex.Y < minY) minY = vertex.Y;
else if (vertex.Y > maxY) maxY = vertex.Y;
}
boundingBox.X = minX;
boundingBox.Y = minY;
boundingBox.Width = maxX - minX;
boundingBox.Height = maxY - minY;
}
public override Entity OffsetEntity(double distance, OffsetSide side)
{
throw new NotImplementedException();
}
public override Entity OffsetEntity(double distance, Vector pt)
{
throw new NotImplementedException();
}
/// <summary>
/// Gets the closest point on the polygon to the given point.
/// </summary>
/// <param name="pt"></param>
/// <returns></returns>
public override Vector ClosestPointTo(Vector pt)
{
var lines = ToLines();
if (lines.Count == 0)
return Vector.Invalid;
Vector closestPt = lines[0].ClosestPointTo(pt);
double distance = closestPt.DistanceTo(pt);
for (int i = 1; i < lines.Count; i++)
{
var line = lines[i];
var closestPt2 = line.ClosestPointTo(pt);
var distance2 = closestPt2.DistanceTo(pt);
if (distance2 < distance)
{
closestPt = closestPt2;
distance = distance2;
}
}
return closestPt;
}
/// <summary>
/// Returns true if the given arc is intersecting this.
/// </summary>
/// <param name="arc"></param>
/// <returns></returns>
public override bool Intersects(Arc arc)
{
List<Vector> pts;
return Helper.Intersects(arc, this, out pts);
}
/// <summary>
/// Returns true if the given arc is intersecting this.
/// </summary>
/// <param name="arc"></param>
/// <param name="pts"></param>
/// <returns></returns>
public override bool Intersects(Arc arc, out List<Vector> pts)
{
return Helper.Intersects(arc, this, out pts);
}
/// <summary>
/// Returns true if the given circle is intersecting this.
/// </summary>
/// <param name="circle"></param>
/// <returns></returns>
public override bool Intersects(Circle circle)
{
List<Vector> pts;
return Helper.Intersects(circle, this, out pts);
}
/// <summary>
/// Returns true if the given circle is intersecting this.
/// </summary>
/// <param name="circle"></param>
/// <param name="pts"></param>
/// <returns></returns>
public override bool Intersects(Circle circle, out List<Vector> pts)
{
return Helper.Intersects(circle, this, out pts);
}
/// <summary>
/// Returns true if the given line is intersecting this.
/// </summary>
/// <param name="line"></param>
/// <returns></returns>
public override bool Intersects(Line line)
{
List<Vector> pts;
return Helper.Intersects(line, this, out pts);
}
/// <summary>
/// Returns true if the given line is intersecting this.
/// </summary>
/// <param name="line"></param>
/// <param name="pts"></param>
/// <returns></returns>
public override bool Intersects(Line line, out List<Vector> pts)
{
return Helper.Intersects(line, this, out pts);
}
/// <summary>
/// Returns true if the given polygon is intersecting this.
/// </summary>
/// <param name="polygon"></param>
/// <returns></returns>
public override bool Intersects(Polygon polygon)
{
List<Vector> pts;
return Helper.Intersects(this, polygon, out pts);
}
/// <summary>
/// Returns true if the given polygon is intersecting this.
/// </summary>
/// <param name="polygon"></param>
/// <param name="pts"></param>
/// <returns></returns>
public override bool Intersects(Polygon polygon, out List<Vector> pts)
{
return Helper.Intersects(this, polygon, out pts);
}
/// <summary>
/// Returns true if the given shape is intersecting this.
/// </summary>
/// <param name="shape"></param>
/// <returns></returns>
public override bool Intersects(Shape shape)
{
List<Vector> pts;
return Helper.Intersects(shape, this, out pts);
}
/// <summary>
/// Returns true if the given shape is intersecting this.
/// </summary>
/// <param name="shape"></param>
/// <param name="pts"></param>
/// <returns></returns>
public override bool Intersects(Shape shape, out List<Vector> pts)
{
return Helper.Intersects(shape, this, out pts);
}
/// <summary>
/// Type of entity.
/// </summary>
public override EntityType Type
{
get { return EntityType.Polygon; }
}
internal void Cleanup()
{
for (int i = Vertices.Count - 1; i > 0; i--)
{
var vertex = Vertices[i];
var nextVertex = Vertices[i - 1];
if (vertex == nextVertex)
Vertices.RemoveAt(i);
}
}
public double FindBestRotation(double stepAngle)
{
var entities = new List<Entity>(ToLines());
return entities.FindBestRotation(stepAngle);
}
public double FindBestRotation(double stepAngle, double startAngle, double endAngle)
{
var entities = new List<Entity>(ToLines());
return entities.FindBestRotation(stepAngle, startAngle, endAngle);
}
}
}

View File

@@ -0,0 +1,569 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace OpenNest.Geometry
{
public class Shape : Entity
{
/// <summary>
/// Entities that make up the shape.
/// </summary>
public List<Entity> Entities;
public Shape()
{
Entities = new List<Entity>();
}
/// <summary>
/// Returns true if the shape is closed.
/// </summary>
/// <returns></returns>
public bool IsClosed()
{
if (Entities.Count == 0)
return false;
var first = Entities[0];
Vector firstStartPoint;
Vector firstEndPoint;
switch (first.Type)
{
case EntityType.Arc:
var arc = (Arc)first;
firstStartPoint = arc.StartPoint();
firstEndPoint = arc.EndPoint();
break;
case EntityType.Circle:
return Entities.Count == 1;
case EntityType.Line:
var line = (Line)first;
firstStartPoint = line.StartPoint;
firstEndPoint = line.EndPoint;
break;
default:
Debug.Fail("Unhandled geometry type");
return false;
}
var endpt = firstEndPoint;
Entity geo = null;
for (int i = 1; i < Entities.Count; ++i)
{
geo = Entities[i];
switch (geo.Type)
{
case EntityType.Arc:
var arc = (Arc)geo;
if (arc.StartPoint() != endpt)
return false;
endpt = arc.EndPoint();
break;
case EntityType.Circle:
return Entities.Count == 1;
case EntityType.Line:
var line = (Line)geo;
if (line.StartPoint != endpt)
return false;
endpt = line.EndPoint;
break;
default:
Debug.Fail("Unhandled geometry type");
return false;
}
}
if (geo == null)
return false;
var last = geo;
Vector lastEndPoint;
switch (last.Type)
{
case EntityType.Arc:
var arc = (Arc)last;
lastEndPoint = arc.EndPoint();
break;
case EntityType.Line:
var line = (Line)last;
lastEndPoint = line.EndPoint;
break;
default:
Debug.Fail("Unhandled geometry type");
return false;
}
return lastEndPoint == firstStartPoint;
}
/// <summary>
/// Gets the area.
/// </summary>
/// <returns>Returns the area or 0 if the shape is NOT closed.</returns>
public double Area()
{
// Check if the shape is closed so we can get the area.
if (!IsClosed())
return 0;
// If the shape is closed and only one entity in the geometry
// then that entity would have to be a circle.
if (Entities.Count == 1)
{
var circle = Entities[0] as Circle;
return circle == null ? 0 : circle.Area();
}
return ToPolygon().Area();
}
/// <summary>
/// Joins all overlapping lines and arcs.
/// </summary>
public void Optimize()
{
var lines = new List<Line>();
var arcs = new List<Arc>();
foreach (var geo in Entities)
{
switch (geo.Type)
{
case EntityType.Arc:
arcs.Add((Arc)geo);
break;
case EntityType.Line:
lines.Add((Line)geo);
break;
}
}
Helper.Optimize(lines);
Helper.Optimize(arcs);
}
/// <summary>
/// Gets the closest point on the shape to the given point.
/// </summary>
/// <param name="pt"></param>
/// <param name="entity">Entity that contains the point.</param>
/// <returns></returns>
public Vector ClosestPointTo(Vector pt, out Entity entity)
{
if (Entities.Count == 0)
{
entity = null;
return Vector.Invalid;
}
var first = Entities[0];
Vector closestPt = first.ClosestPointTo(pt);
double distance = closestPt.DistanceTo(pt);
entity = first;
for (int i = 1; i < Entities.Count; i++)
{
var entity2 = Entities[i];
var closestPt2 = entity2.ClosestPointTo(pt);
var distance2 = closestPt2.DistanceTo(pt);
if (distance2 < distance)
{
closestPt = closestPt2;
distance = distance2;
entity = entity2;
}
}
return closestPt;
}
/// <summary>
/// Converts the shape to a polygon.
/// </summary>
/// <returns></returns>
public Polygon ToPolygon(int arcSegments = 1000)
{
var polygon = new Polygon();
foreach (var entity in Entities)
{
switch (entity.Type)
{
case EntityType.Arc:
var arc = (Arc)entity;
polygon.Vertices.AddRange(arc.ToPoints(arcSegments));
break;
case EntityType.Line:
var line = (Line)entity;
polygon.Vertices.AddRange(new[]
{
line.StartPoint,
line.EndPoint
});
break;
case EntityType.Circle:
var circle = (Circle)entity;
polygon.Vertices.AddRange(circle.ToPoints(arcSegments));
break;
default:
Debug.Fail("Unhandled geometry type");
break;
}
}
polygon.Close();
polygon.Cleanup();
return polygon;
}
/// <summary>
/// Reverses the rotation direction of the shape.
/// </summary>
public override void Reverse()
{
Entities.ForEach(e => e.Reverse());
Entities.Reverse();
}
/// <summary>
/// Linear distance of the shape.
/// </summary>
public override double Length
{
get { return Entities.Sum(geo => geo.Length); }
}
/// <summary>
/// Moves the start point to the given coordinates.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
public override void MoveTo(double x, double y)
{
throw new NotImplementedException();
}
/// <summary>
/// Moves the start point to the given point.
/// </summary>
/// <param name="pt"></param>
public override void MoveTo(Vector pt)
{
throw new NotImplementedException();
}
/// <summary>
/// Offsets the shape location by the given distances.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
public override void Offset(double x, double y)
{
Entities.ForEach(e => e.Offset(x, y));
boundingBox.Offset(x, y);
}
/// <summary>
/// Offsets the shape location by the given distances.
/// </summary>
/// <param name="voffset"></param>
public override void Offset(Vector voffset)
{
Entities.ForEach(e => e.Offset(voffset));
boundingBox.Offset(voffset);
}
/// <summary>
/// Scales the shape from the zero point.
/// </summary>
/// <param name="factor"></param>
public override void Scale(double factor)
{
Entities.ForEach(e => e.Scale(factor));
UpdateBounds();
}
/// <summary>
/// Scales the shape from the origin.
/// </summary>
/// <param name="factor"></param>
/// <param name="origin"></param>
public override void Scale(double factor, Vector origin)
{
Entities.ForEach(e => e.Scale(factor, origin));
UpdateBounds();
}
/// <summary>
/// Rotates the shape from the zero point.
/// </summary>
/// <param name="angle"></param>
public override void Rotate(double angle)
{
Entities.ForEach(e => e.Rotate(angle));
UpdateBounds();
}
/// <summary>
/// Rotates the shape from the origin.
/// </summary>
/// <param name="angle"></param>
/// <param name="origin"></param>
public override void Rotate(double angle, Vector origin)
{
Entities.ForEach(e => e.Rotate(angle, origin));
UpdateBounds();
}
/// <summary>
/// Updates the bounding box.
/// </summary>
public override void UpdateBounds()
{
boundingBox = Entities.Select(geo => geo.BoundingBox)
.ToList()
.GetBoundingBox();
}
public override Entity OffsetEntity(double distance, OffsetSide side)
{
var offsetShape = new Shape();
var definedShape = new DefinedShape(this);
Entity lastEntity = null;
Entity lastOffsetEntity = null;
foreach (var entity in definedShape.Perimeter.Entities)
{
var offsetEntity = entity.OffsetEntity(distance, side);
if (offsetEntity == null)
continue;
switch (entity.Type)
{
case EntityType.Line:
{
var line = (Line)entity;
var offsetLine = (Line)offsetEntity;
if (lastOffsetEntity != null && lastOffsetEntity.Type == EntityType.Line)
{
var lastLine = lastEntity as Line;
var lastOffsetLine = lastOffsetEntity as Line;
if (lastLine == null || lastOffsetLine == null)
continue;
Vector intersection;
if (Helper.Intersects(offsetLine, lastOffsetLine, out intersection))
{
offsetLine.StartPoint = intersection;
lastOffsetLine.EndPoint = intersection;
}
else
{
var arc = new Arc(
line.StartPoint,
distance,
line.StartPoint.AngleTo(lastOffsetLine.EndPoint),
line.StartPoint.AngleTo(offsetLine.StartPoint),
side == OffsetSide.Left
);
offsetShape.Entities.Add(arc);
}
}
offsetShape.Entities.Add(offsetLine);
break;
}
default:
offsetShape.Entities.Add(offsetEntity);
break;
}
lastOffsetEntity = offsetEntity;
lastEntity = entity;
}
foreach (var cutout in definedShape.Cutouts)
offsetShape.Entities.AddRange(((Shape)cutout.OffsetEntity(distance, side)).Entities);
return offsetShape;
}
public override Entity OffsetEntity(double distance, Vector pt)
{
throw new NotImplementedException();
}
/// <summary>
/// Gets the closest point on the shape to the given point.
/// </summary>
/// <param name="pt"></param>
/// <returns></returns>
public override Vector ClosestPointTo(Vector pt)
{
Entity entity;
return ClosestPointTo(pt, out entity);
}
/// <summary>
/// Returns true if the given arc is intersecting this.
/// </summary>
/// <param name="arc"></param>
/// <returns></returns>
public override bool Intersects(Arc arc)
{
List<Vector> pts;
return Helper.Intersects(arc, this, out pts);
}
/// <summary>
/// Returns true if the given arc is intersecting this.
/// </summary>
/// <param name="arc"></param>
/// <param name="pts"></param>
/// <returns></returns>
public override bool Intersects(Arc arc, out List<Vector> pts)
{
return Helper.Intersects(arc, this, out pts);
}
/// <summary>
/// Returns true if the given circle is intersecting this.
/// </summary>
/// <param name="circle"></param>
/// <returns></returns>
public override bool Intersects(Circle circle)
{
List<Vector> pts;
return Helper.Intersects(circle, this, out pts);
}
/// <summary>
/// Returns true if the given circle is intersecting this.
/// </summary>
/// <param name="circle"></param>
/// <param name="pts"></param>
/// <returns></returns>
public override bool Intersects(Circle circle, out List<Vector> pts)
{
return Helper.Intersects(circle, this, out pts);
}
/// <summary>
/// Returns true if the given line is intersecting this.
/// </summary>
/// <param name="line"></param>
/// <returns></returns>
public override bool Intersects(Line line)
{
List<Vector> pts;
return Helper.Intersects(line, this, out pts);
}
/// <summary>
/// Returns true if the given line is intersecting this.
/// </summary>
/// <param name="line"></param>
/// <param name="pts"></param>
/// <returns></returns>
public override bool Intersects(Line line, out List<Vector> pts)
{
return Helper.Intersects(line, this, out pts);
}
/// <summary>
/// Returns true if the given polygon is intersecting this.
/// </summary>
/// <param name="polygon"></param>
/// <returns></returns>
public override bool Intersects(Polygon polygon)
{
List<Vector> pts;
return Helper.Intersects(this, polygon, out pts);
}
/// <summary>
/// Returns true if the given polygon is intersecting this.
/// </summary>
/// <param name="polygon"></param>
/// <param name="pts"></param>
/// <returns></returns>
public override bool Intersects(Polygon polygon, out List<Vector> pts)
{
return Helper.Intersects(this, polygon, out pts);
}
/// <summary>
/// Returns true if the given shape is intersecting this.
/// </summary>
/// <param name="shape"></param>
/// <returns></returns>
public override bool Intersects(Shape shape)
{
List<Vector> pts;
return Helper.Intersects(this, shape, out pts);
}
/// <summary>
/// Returns true if the given shape is intersecting this.
/// </summary>
/// <param name="shape"></param>
/// <param name="pts"></param>
/// <returns></returns>
public override bool Intersects(Shape shape, out List<Vector> pts)
{
return Helper.Intersects(this, shape, out pts);
}
/// <summary>
/// Type of entity.
/// </summary>
public override EntityType Type
{
get { return EntityType.Shape; }
}
public double FindBestRotation(double stepAngle)
{
return Entities.FindBestRotation(stepAngle);
}
public double FindBestRotation(double stepAngle, double startAngle, double endAngle)
{
return Entities.FindBestRotation(stepAngle, startAngle, endAngle);
}
}
}