Author Topic: .NET GEOMETRY Routines  (Read 23884 times)

0 Members and 1 Guest are viewing this topic.

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11653
  • class keyThumper<T>:ILazy<T>
.NET GEOMETRY Routines
« on: January 27, 2010, 03:34:28 am »
LIBRARY THREAD for  AutoCAD GEOMETRY
 Members are encouraged to post any functions, methods, snips regarding
AutoCAD GEOMETRY in .NET : C# ,  VB , F# , Python , etc

Feel free to include comments, descriptive notes, limitations,  and images to document your post.

Please post questions in a regular thread.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

--> Donate to theSwamp<--

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #1 on: January 27, 2010, 05:28:26 pm »
Hi,

Some geometry extensions methods for Point2d, Point3d, CircularArc2d, Polyline and Polyline2d classes and two little (maybe usefull) classes: Triangle2d, Triangle3d.

All these classes work together and have to be compiled in a single DLL.

The extension methods requires to reference System.Core (.NET Framework 3.5)

EDIT: extended

The GeomExt class, contains the following extension methods:

Matrix3d Editor.UCS2WCS()
Matrix3d Editor.WCS2UCS()
Matrix3d Editor.DCS2WCS()
Matrix3d Editor.WCS2DCS()
Matrix3d Editor.DCS2PSDCS()
Matrix3d Editor.PSDCS2DCS()

Matrix3d Viewport.DCS2WCS()
Matrix3d Viewport.WCS2DCS()
Matrix3d Viewport.PSDCS2DCS()
Matrix3d Viewport.DCS2PSDCS()

Point3d Point2d.Convert3d()
Point3d Point2d.Convert3d(Plane plane)
Point3d Point2d.Convert3d(Vector3d normal, double elevation)
Point2d Point2d.Flatten(Vector3d normal)
bool IsBetween(Point2d p1, Point2d p2)
bool IsBetween(Point2d p1, Point2d p2, Tolerance tol)
Point2d Point2d.Polar(double angle, double distance)

Point3d Point3d.Convert2d()
Point3d Point3d.Flatten()
bool IsBetween(Point3d p1, Point3d p2)
bool IsBetween(Point3d p1, Point3d p2, Tolerance tol)
Point3d Point3d.Polar(double angle, double distance)
Point3d Point3d.Trans(int from, int to)
Point3d Point3d.Trans(Editor ed, int from, int to)
Point3d Point3d.Trans(CoordSystem from, CoordSystem to)
Point3d Point3d.Trans(Editor ed, CoordSystem from, CoordSystem to)

Vector3d Vector3d .Flatten()

void Point2dCollection.RemoveDuplicate()
void Point2dCollection.RemoveDuplicate(Tolerance tol)
bool Point2dCollection.Contains(Point2d pt, Tolerance tol)

void Point3dCollection.RemoveDuplicate()
void Point3dCollection.RemoveDuplicate(Tolerance tol)
bool Point3dCollection.Contains(Point3d pt, Tolerance tol)

double CircularArc2d.AlgebricArea()
Point2d CircularArc2d.Centroid()

Point2d Polyline.Centroid2d()
Point3d Polyline.Centroid()
Polyline Polyline.GetProjectedPolyline(Plane plane, Vector3d direction)
Polyline Polyline.GetOrthoProjectedPolyline(Plane plane)

Point3d Polyline2d.Centroid()
CircularArc3d Polyline2d.GetArcSegmentAt(int index)
CircularArc2d Polyline2d.GetArcSegment2dAt(int index)
LineSegment3d Polyline2d.GetLineSegmentAt(int index)
LineSegment2d Polyline2d.GetLineSegment2dAt(int index)
Polyline Polyline2d.GetProjectedPolyline(Plane plane, Vector3d direction)
Polyline Polyline2d.GetOrthoProjectedPolyline(Plane plane)
List<Vertex2d> Polyline2d.GetVertices()

Polyline Polyline3d.GetProjectedPolyline(Plane plane, Vector3d direction)
Polyline Polyline3d.GetOrthoProjectedPolyline(Plane plane)

Point3d Region.Centroid()

Point3d Spline.Centroid()

Polyline Ellipse.ToPolyline()


Triangle<T> abstract class

Constructors
Triangle()
Triangle(T[] pts)
Triangle(T a, T b, T c)

Indexor
T Item

Methods
T Inverse()
void Set(T[] pts)
void Set(T a, T b, T c)
T[] ToArray()


Triangle2d : Triangle<Point2d> class

Constructors
Triangle2d() : base()
Triangle2d(Point2d[] pts) : base(pts)
Triangle2d(Point2d a, Point2d b, Point2d c) : base(a, b, c)
Triangle2d(Point2d org, Vector2d v1, Vector2d v2)

Properties
double AlgebricArea
Point2d Centroid
CircularArc2d CircumscribedCircle
CircularArc2d InscribedCircle
bool IsClockwise

Methods
Triangle3d Convert3d(Plane plane)
Triangle3d Convert3d(Vector3d normal, double elevation)
double GetAngleAt(int index)
LineSegment2d GetSegmentAt(int index)
List<Point2d> IntersectWith(LinearEntity2d le2d)
List<Point2d> IntersectWith(LinearEntity2d le2d, Tolerance tol)
bool IsEqualTo(Triangle2d t2d)
bool IsEqualTo(Triangle2d t2d, Tolerance tol)
bool IsPointInside(Point2d pt)
bool IsPointOn(Point2d pt)
void Set(Point2d org, Vector2d v1, Vector2d v2)
Triangle2d TransformBy(Matrix2d mat)


Triangle3d : Triangle<Point3d> class

Constructors
Triangle3d() : base()
Triangle3d(Point3d[] pts) : base(pts)
Triangle3d(Point3d a, Point3d b, Point3d c) : base(a, b, c)
Triangle3d(Point3d org, Vector3d v1, Vector3d v2)

Properties
double Area
Point3d Centroid
CircularArc3d CircumscribedCircle
double Elevation
Vector3d GreatestSlope
Vector3d Horizontal
CircularArc3d InscribedCircle
bool IsHorizontal
Vector3d Normal
double SlopePerCent
Matrix3d SlopeUCS

Methods
Triangle2d Convert2d()
Triangle2d Flatten()
double GetAngleAt(int index)
BoundedPlane GetBoundedPlane()
Plane GetPlane()
LineSegment3d GetSegmentAt(int index)
bool IsEqualTo(Triangle3d t3d)
bool IsEqualTo(Triangle3d t3d, Tolerance tol)
bool IsPointInside(Point3d pt)
bool IsPointOn(Point3d pt)
void Set(Point3d org, Vector3d v1, Vector3d v2)
Triangle3d Transformby(Matrix3d mat)


PolylineSegment class

Constructors

PolylineSegment(Point2d startPoint, Point2d endPoint)
PolylineSegment(Point2d startPoint, Point2d endPoint, double bulge)
PolylineSegment(Point2d startPoint, Point2d endPoint, double bulge, double constantWidth)
PolylineSegment(Point2d startPoint, Point2d endPoint, double bulge, double startWidth, double endWidth)
PolylineSegment(Line2dSegment line)
PolylineSegment(CircularArc2d arc)

Properties

double Bulge
Point2d EndPoint
double EndWidth
bool IsLinear
Point2d StartPoint
double StartWidth

Methods

bool Equals(object obj)
PolylineSegment Clone()
int GetHashCode()
double GetParameterOf(Point3d pt)
void Inverse()
CircularArc2d ToCircularArc()
Curve2d ToCurve2d()
LineSegment2d ToLineSegment()
string ToString()


PolylineSegmentCollection : IList<PolylineSegment> class

Constructors

PolylineSegmentCollection()
PolylineSegmentCollection(Circle circle)
PolylineSegmentCollection(Ellipse ellipse)
PolylineSegmentCollection(Polyline pline)
PolylineSegmentCollection(Polyline2d pline)
PolylineSegmentCollection(IEnumerable<PolylineSegment> segments)

Methods

void Add(PolylineSegment segment)
void AddRange(IEnumerable<PolylineSegment> range)
void Clear()
bool Contains(PolylineSegment segment)
int FinIindex(Predicate<PolylineSegment> match)
int GetClosestSegmentTo(Point2d pt)
IEnumerator<PolylineSegment> GetEnumerator()
int IndexOf(PolylineSegment item)
void Insert(int index, PolylineSegment item)
void InsertRange(int index, IEnumerable<PolylineSegment> collection)
List<PolylineSegmentCollection> Join()
List<PolylineSegmentCollection> Join(Tolerance tol)
bool Remove(PolylineSegment item)
void RemoveAt(int index)
public void RemoveRange(int index, int count)
Polyline ToPolyline()

For more information, see the attached documentation.

GeomExtDoc.zip contains a .chm documentation file.
GeometryExtensions(1.6).zip contains the C# source code files and the DLLs (GeometryExtensions_18.dll for A2010-2012 and GeometryExtensions_19 for A2013+)

<EDIT: version 1.6> added CircularArc2d and CircularArc3d GetTangentsTo methods, reorganized: one class per extended type.
« Last Edit: September 15, 2013, 05:30:34 pm by gile »
Speaking English as a French Frog

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #2 on: February 11, 2010, 04:31:52 pm »
Added some extension methods for Polyline2d.
Speaking English as a French Frog

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #3 on: February 16, 2010, 06:03:22 pm »
I added some more extensions and rewrite the Triangle2d and Triangle3d classes
Speaking English as a French Frog

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #4 on: April 17, 2010, 08:56:17 am »
Hi,

I added some properties and methods to the Triangle2d and Triangle3d classes that I needed to deal with 3dfaces (ground modeling).

Triangle2d class:
- Inverse() method returns the triangle in inverse order (Triangle2d)

Triangle3d class :
- GreatestSlope property returns the single unit vector of the triangle plane greatest slope (Vector3d)
- Horizontal property returns the single unit vector of the triangle plane horizontal (Vector3d)
- IsHorizontal property returns true if the triangle plane is parallel to WCS XY plane (Boolean)
- SlopePerCent property returns the percent expression of the greatest slope (Double)
- SlopeUCS property returns a coordinates system matrix which origin is the triangle centroid, X axis is horizontal vector, Y axis the negated greatest slope vector (Matrix3d)
- Flatten() method returns the flatten triangle (Triangle2d)
- Inverse() method returns the triangle in inverse order (Triangle3d)


Speaking English as a French Frog

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #5 on: August 22, 2010, 02:18:56 pm »
Hi,

Some more adds:

Point3d and Point2d extension methods:
- IsBetween returns true if the first point (first parameter) is strictly between the two others (with or without Tolerance).

Triangle2d methods:
- GetSegmentAt: returns the LinearSegment2d at specified index.
- IntersecWith: returns a List<Point2d> of the intersection points between the triangle and a LinearEntity2d (with or without Tolerance).
- IsPointInside: returns true if the point is strictly inside the triangle.
- IsPointOn: returns true if the point lies on an edge.

Triangle3d methods:
- GetBoundedPlane: returns a BoundedPlane object.
- IsPointInside: returns true if the point is strictly inside the triangle.
- IsPointOn: returns true if the point lies on an edge.
Speaking English as a French Frog

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #6 on: August 23, 2010, 03:49:33 am »
 :oops:

There was a mistake in the Triangle2d.IsPointInside() method.
Attached file in the first post is updated.
Speaking English as a French Frog

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11653
  • class keyThumper<T>:ILazy<T>
Re: .NET GEOMETRY Routines
« Reply #7 on: August 23, 2010, 04:45:37 am »

Thanks gile, that should be a handy library.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

--> Donate to theSwamp<--

fixo

  • Guest
Re: .NET GEOMETRY Routines
« Reply #8 on: August 23, 2010, 05:23:12 am »
:oops:

There was a mistake in the Triangle2d.IsPointInside() method.
Attached file in the first post is updated.

Thanks Gilles, I've used it once, very useful
I'm pretty sure you working on Rectangle 2d/3d as well :)

Oleg

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #9 on: August 23, 2010, 05:48:04 am »
Thanks both, you're welcome.

Quote
I'm pretty sure you working on Rectangle 2d/3d as well
I'm not so sure, IMO, in geometry (almost 3d geometry) rectangles are'nt as useful as triangles which are not deformable, define planes and so on...
Speaking English as a French Frog

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #10 on: February 03, 2011, 05:03:03 am »
Hi,

Added some extension methods:

void Point2dCollection.RemoveDuplicate()
void Point2dCollection.RemoveDuplicate(Tolerance tol)
bool Point2dCollection.Contains(Point2d pt, Tolerance tol)

void Point3dCollection.RemoveDuplicate()
void Point3dCollection.RemoveDuplicate(Tolerance tol)
bool Point3dCollection.Contains(Point3d pt, Tolerance tol)

And edit the Triangle2d and Triangle3d classes:
both inherits from a 'generic' abstract class: Triangle<T> and have two new properties : CircumscribedCircle and InscribedCircle
« Last Edit: February 03, 2011, 05:11:58 am by gile »
Speaking English as a French Frog

jgr

  • Guest
Re: .NET GEOMETRY Routines
« Reply #11 on: March 03, 2011, 10:37:26 am »
« Last Edit: March 03, 2011, 12:47:02 pm by jgr »

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #12 on: April 14, 2011, 05:16:55 pm »
Hi

Here's a F# convex hull (Graham's scan)

EDIT: new code see here

EDIT: a tolerance in comparisons was needed in some cases with colinear points

Code - F#: [Select]
  1. module ConvexHull
  2.  
  3. open System
  4. open Autodesk.AutoCAD.ApplicationServices
  5. open Autodesk.AutoCAD.DatabaseServices
  6. open Autodesk.AutoCAD.EditorInput
  7. open Autodesk.AutoCAD.Geometry
  8. open Autodesk.AutoCAD.Runtime
  9.  
  10. let clockwise (p1:Point2d) (p2:Point2d) (p3:Point2d) =
  11.    (p2.X - p1.X) * (p3.Y - p1.Y) - (p2.Y - p1.Y) * (p3.X - p1.X) < 1e-8
  12.  
  13. let convexHull (pts : Point2d seq) =
  14.    let rec fill acc pt =
  15.        match acc with
  16.        | a :: b :: _ when clockwise b a pt -> fill acc.Tail pt
  17.        | _ -> pt :: acc
  18.    let p0 = pts
  19.             |> Seq.reduce (fun p1 p2 ->
  20.                if p2.Y < p1.Y || (p1.Y = p2.Y && p2.X < p1.X) then p2 else p1)
  21.    pts
  22.    |> List.ofSeq
  23.    |> List.sortBy (fun p ->
  24.        let d = p0.GetDistanceTo(p)
  25.        (Math.Round((p0.X - p.X) / d, 8), d))
  26.    |> List.fold fill []
  27.    |> List.rev
  28.  
  29. [<CommandMethod("ch")>]
  30. let Test() =
  31.    let doc = Application.DocumentManager.MdiActiveDocument
  32.    let psr = doc.Editor.GetSelection(new SelectionFilter([| new TypedValue(0, "POINT") |]))
  33.    if psr.Status = PromptStatus.OK then
  34.        use tr = doc.TransactionManager.StartTransaction()
  35.        use pl = new Polyline()
  36.        psr.Value
  37.        |> Seq.cast
  38.        |> Seq.map (fun (so : SelectedObject) ->
  39.            let pt = tr.GetObject(so.ObjectId, OpenMode.ForRead) :?> DBPoint
  40.            new Point2d(pt.Position.X, pt.Position.Y))
  41.        |> convexHull
  42.        |> List.iteri(fun i p -> pl.AddVertexAt(i, p, 0.0, 0.0, 0.0))
  43.        pl.Closed <- true
  44.        let btr = tr.GetObject(doc.Database.CurrentSpaceId, OpenMode.ForWrite) :?> BlockTableRecord
  45.        btr.AppendEntity(pl) |> ignore
  46.        tr.AddNewlyCreatedDBObject(pl, true)
  47.        tr.Commit()
« Last Edit: June 10, 2017, 11:16:23 am by gile »
Speaking English as a French Frog

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #13 on: April 15, 2011, 06:09:33 am »
And the new C# implementation:

EDIT: I repalced the lambda function with a delegate (ComparePoints) so that it can be used with NET Framework 2.0 (default for A2007 -> A2009)

Code - C#: [Select]
  1. using System.Collections.Generic;
  2. using Autodesk.AutoCAD.ApplicationServices;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.EditorInput;
  5. using Autodesk.AutoCAD.Geometry;
  6. using Autodesk.AutoCAD.Runtime;
  7. using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;
  8.  
  9. namespace ConvexHull
  10. {
  11.    public class Commands
  12.    {
  13.        private Point2d _p0;
  14.  
  15.        private bool Clockwise(Point2d p1, Point2d p2, Point2d p3)
  16.        {
  17.            return ((p2.X - p1.X) * (p3.Y - p1.Y) - (p2.Y - p1.Y) * (p3.X - p1.X)) < 1e-8;
  18.        }
  19.  
  20.        private int ComparePoints(Point2d p1, Point2d p2)
  21.        {
  22.            if (p1.IsEqualTo(p2)) return 0;
  23.            double d1 = _p0.GetDistanceTo(p1);
  24.            double d2 = _p0.GetDistanceTo(p2);
  25.            if (d1 == 0.0) return -1;
  26.            if (d2 == 0.0) return 1;
  27.            double cos = (p2.X - _p0.X) / d2 - (p1.X - _p0.X) / d1;
  28.            if (cos < -1e-8) return -1;
  29.            if (cos > 1e-8) return 1;
  30.            return d1.CompareTo(d2);
  31.        }
  32.  
  33.        private List<Point2d> ConvexHull(List<Point2d> pts)
  34.        {
  35.            _p0 = pts[0];
  36.            for (int i = 1; i < pts.Count; i++)
  37.            {
  38.                Point2d pt = pts[i];
  39.                if (pt.Y < _p0.Y || (pt.Y == _p0.Y && pt.X < _p0.X))
  40.                    _p0 = pt;
  41.            }
  42.            pts.Sort(ComparePoints);
  43.            for (int i = 1; i < pts.Count - 1; i++)
  44.            {
  45.                while (i > 0 && Clockwise(pts[i - 1], pts[i], pts[i + 1]))
  46.                {
  47.                    pts.RemoveAt(i);
  48.                    i--;
  49.                }
  50.            }
  51.            return pts;
  52.        }
  53.  
  54.        [CommandMethod("ch")]
  55.        public void testCh()
  56.        {
  57.            Document doc = AcAp.DocumentManager.MdiActiveDocument;
  58.            Database db = doc.Database;
  59.            Editor ed = doc.Editor;
  60.            TypedValue[] filter = new TypedValue[1] { new TypedValue(0, "POINT") };
  61.            PromptSelectionResult psr = ed.GetSelection(new SelectionFilter(filter));
  62.            if (psr.Status != PromptStatus.OK) return;
  63.            using (Transaction tr = db.TransactionManager.StartTransaction())
  64.            using (Polyline pline = new Polyline())
  65.            {
  66.                List<Point2d> pts = new List<Point2d>();
  67.                foreach (SelectedObject so in psr.Value)
  68.                {
  69.                    DBPoint dbPt = (DBPoint)tr.GetObject(so.ObjectId, OpenMode.ForRead);
  70.                    pts.Add(new Point2d(dbPt.Position.X, dbPt.Position.Y));
  71.                }
  72.                for (int i = 0; i < ConvexHull(pts).Count; i++)
  73.                {
  74.                    pline.AddVertexAt(i, pts[i], 0.0, 0.0, 0.0);
  75.                }
  76.                pline.Closed = true;
  77.                pline.SetDatabaseDefaults();
  78.                BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
  79.                btr.AppendEntity(pline);
  80.                tr.AddNewlyCreatedDBObject(pline, true);
  81.                tr.Commit();
  82.            }
  83.        }
  84.    }
  85. }
« Last Edit: June 10, 2017, 11:16:52 am by gile »
Speaking English as a French Frog

fixo

  • Guest
Re: .NET GEOMETRY Routines
« Reply #14 on: April 18, 2011, 04:18:27 pm »
Nice as always, thanks :)
Regards,

Oleg

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #15 on: April 19, 2011, 01:16:01 pm »
Thanks, Oleg. You're very welcome.
Speaking English as a French Frog

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #16 on: April 25, 2011, 01:55:10 pm »
A new C# implementation of the ConvexHull using Ling (.NET Framework 3 or upper)

Code - C#: [Select]
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Autodesk.AutoCAD.ApplicationServices;
  5. using Autodesk.AutoCAD.DatabaseServices;
  6. using Autodesk.AutoCAD.EditorInput;
  7. using Autodesk.AutoCAD.Geometry;
  8. using Autodesk.AutoCAD.Runtime;
  9. using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;
  10.  
  11. namespace LinqConvexHull
  12. {
  13.    public class Commands
  14.    {
  15.        private Point2d _p0;
  16.  
  17.        private bool Clockwise(Point2d p1, Point2d p2, Point2d p3)
  18.        {
  19.            return ((p2.X - p1.X) * (p3.Y - p1.Y) - (p2.Y - p1.Y) * (p3.X - p1.X)) < 1e-9;
  20.        }
  21.  
  22.        private double Cosine(Point2d pt)
  23.        {
  24.            double d = _p0.GetDistanceTo(pt);
  25.            return d == 0.0 ? 1.0 : Math.Round((pt.X - _p0.X) / d, 9);
  26.        }
  27.  
  28.        private List<Point2d> ConvexHull(List<Point2d> pts)
  29.        {
  30.            _p0 = pts.OrderBy(p => p.Y).ThenBy(p => p.X).First();
  31.            pts = pts.OrderByDescending(p => Cosine(p)).ThenBy(p => _p0.GetDistanceTo(p)).ToList();
  32.            for (int i = 1; i < pts.Count - 1; i++)
  33.            {
  34.                while (i > 0 && Clockwise(pts[i - 1], pts[i], pts[i + 1]))
  35.                {
  36.                    pts.RemoveAt(i);
  37.                    i--;
  38.                }
  39.            }
  40.            return pts;
  41.        }
  42.  
  43.        [CommandMethod("Test")]
  44.        public void Test()
  45.        {
  46.            Document doc = AcAp.DocumentManager.MdiActiveDocument;
  47.            Database db = doc.Database;
  48.            Editor ed = doc.Editor;
  49.            TypedValue[] filter = new TypedValue[1] { new TypedValue(0, "POINT") };
  50.            PromptSelectionResult psr = ed.GetSelection(new SelectionFilter(filter));
  51.            if (psr.Status != PromptStatus.OK) return;
  52.            using (Transaction tr = db.TransactionManager.StartTransaction())
  53.            using (Polyline pline = new Polyline())
  54.            {
  55.                List<Point2d> pts = new List<Point2d>();
  56.                foreach (SelectedObject so in psr.Value)
  57.                {
  58.                    DBPoint dbPt = (DBPoint)tr.GetObject(so.ObjectId, OpenMode.ForRead);
  59.                    pts.Add(new Point2d(dbPt.Position.X, dbPt.Position.Y));
  60.                }
  61.                pts = ConvexHull(pts);
  62.                for (int i = 0; i < pts.Count; i++)
  63.                {
  64.                    pline.AddVertexAt(i, pts[i], 0.0, 0.0, 0.0);
  65.                }
  66.                pline.Closed = true;
  67.                pline.SetDatabaseDefaults();
  68.                BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
  69.                btr.AppendEntity(pline);
  70.                tr.AddNewlyCreatedDBObject(pline, true);
  71.                tr.Commit();
  72.            }
  73.        }
  74.    }
  75. }
  76.  
« Last Edit: July 02, 2017, 01:39:37 am by gile »
Speaking English as a French Frog

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #17 on: July 10, 2011, 10:41:46 am »
Hi,

I made a minor update in GeometryExtensions and I added comments to create a documentation file using Sandcastle Help File Builder.

The GeomExtDoc.chm is downloadable from the reply #2.

Speaking English as a French Frog

qjchen

  • Bull Frog
  • Posts: 285
  • Best wishes to all
Re: .NET GEOMETRY Routines
« Reply #18 on: July 13, 2011, 09:41:50 am »
Gile, Thank you your great library, I will learn a lot from it~

And this code about convex hull which using Linq is so concise, very cool~
« Last Edit: July 13, 2011, 10:11:29 am by qjchen »
http://qjchen.mjtd.com
My blog http://chenqj.blogspot.com (Chinese, can be translate into English)

kaefer

  • Swamp Rat
  • Posts: 572
Re: .NET GEOMETRY Routines
« Reply #19 on: July 13, 2011, 10:39:07 am »
Gile, Thank you your great library, I will learn a lot from it~

I can only second that. Merci beaucoup!

Alas, there was a minor snag in appropriating your intellectual property: The paper space transformations are extension methods to Editor, whereas I needed them for Viewport to replicate the CHSPACE command. Any chance to make them more generic?

Code: [Select]
type Viewport with
    member vp.DCS2PSDCS =
        Matrix3d.Scaling(vp.CustomScale, vp.CenterPoint) *
        Matrix3d.Displacement(vp.CenterPoint - Point3d.Origin) *
        Matrix3d.Displacement(Point3d.Origin - vp.ViewCenter.To3d)
    member vp.PSDCS2DCS = vp.DCS2PSDCS.Inverse()

    member vp.DCS2WCS =
        Matrix3d.Rotation(-vp.TwistAngle, vp.ViewDirection, vp.ViewTarget) *
        Matrix3d.Displacement(vp.ViewTarget - Point3d.Origin) *
        Matrix3d.PlaneToWorld vp.ViewDirection
    member vp.WCS2DCS = vp.DCS2WCS.Inverse()

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #20 on: July 13, 2011, 01:39:14 pm »
Thank you both.

Quote
there was a minor snag in appropriating your intellectual property
I do not really care about "intellectual property" with the code I post on TheSwamp or elsewhere. I learn a lot from you and others and most of the code I write is "hacked" from what I read.
If I can bring my "two cents" sometimes, it makes me happy when other find it usefull.

Anyway, you're right, the Viewport extensions methods may be useful as the actual methods only work with the current (or last activated) one (I wanted to mimic the AutoLISP trans function with the Point3d.Trans() method).

If you don't mind it's an "appropriation of your intellectual proprety" ;-) , I'd add them to the library.
Speaking English as a French Frog

jgr

  • Guest
Re: .NET GEOMETRY Routines
« Reply #21 on: July 15, 2011, 07:49:05 pm »

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #22 on: August 29, 2011, 04:16:38 am »
Hi,

Here's a method to write the Brep (Boundary Representation) geométric informations of Solid3d, Region, Surface to an Xml file.
Requires A2009 or upper.

Method parameters:
- id: A Solid3d, Region or Surface ObjectId
- filename: The Xml file complete path
- append: If the file exists and this parameter is True, the new data is append to the file, else, the file is overwritten

Code - C#: [Select]
  1.        public void WriteBrepToXml(ObjectId id, string filename, bool append)
  2.        {
  3.            XmlDocument xmlDoc = new XmlDocument();
  4.            if (System.IO.File.Exists(filename) && append)
  5.            {
  6.                xmlDoc.Load(filename);
  7.            }
  8.            else
  9.            {
  10.                string s = @"<?xml version='1.0' ?>
  11.                <Brep>
  12.                </Brep>";
  13.                xmlDoc.LoadXml(s);
  14.            }
  15.            Database db = HostApplicationServices.WorkingDatabase;
  16.            using (Transaction tr = db.TransactionManager.StartTransaction())
  17.            {
  18.                Entity ent = (Entity)tr.GetObject(id, OpenMode.ForRead);
  19.                string solidType;
  20.                if (ent is Solid3d) solidType = "Solide";
  21.                else if (ent is Region) solidType = "Region";
  22.                else if (ent is AcDb.Surface) solidType = "Surface";
  23.                else return;
  24.                using (Brep brp = new Brep(ent))
  25.                {
  26.                    XmlElement entNode = xmlDoc.CreateElement(solidType);
  27.                    entNode.SetAttribute("Handle", ent.Handle.ToString());
  28.                    xmlDoc.DocumentElement.AppendChild(entNode);
  29.                    foreach (Complex cplx in brp.Complexes)
  30.                    {
  31.                        XmlElement cplxNode = xmlDoc.CreateElement("Complex");
  32.                        entNode.AppendChild(cplxNode);
  33.                        foreach (Shell shl in cplx.Shells)
  34.                        {
  35.                            XmlElement shlNode = xmlDoc.CreateElement("Shell");
  36.                            shlNode.SetAttribute("Type", shl.ShellType.ToString());
  37.                            cplxNode.AppendChild(shlNode);
  38.                            foreach (AcBr.Face face in shl.Faces)
  39.                            {
  40.                                XmlElement faceNode = xmlDoc.CreateElement("Face");
  41.                                shlNode.AppendChild(faceNode);
  42.                                try
  43.                                {
  44.                                    foreach (BoundaryLoop loop in face.Loops)
  45.                                    {
  46.                                        XmlElement loopNode = xmlDoc.CreateElement("Loop");
  47.                                        loopNode.SetAttribute("Type", loop.LoopType.ToString());
  48.                                        faceNode.AppendChild(loopNode);
  49.                                        foreach (Edge edge in loop.Edges)
  50.                                        {
  51.                                            ExternalCurve3d curve = (ExternalCurve3d)edge.Curve;
  52.                                            switch (curve.NativeCurve.GetType().Name)
  53.                                            {
  54.                                                case "LineSegment3d":
  55.                                                    LineSegment3d ls = (LineSegment3d)curve.NativeCurve;
  56.                                                    XmlElement lsNode = xmlDoc.CreateElement("LineSegment3d");
  57.                                                    lsNode.SetAttribute("StartPoint", ls.StartPoint.ToString());
  58.                                                    lsNode.SetAttribute("EndPoint", ls.EndPoint.ToString());
  59.                                                    lsNode.SetAttribute("Direction", ls.Direction.ToString());
  60.                                                    loopNode.AppendChild(lsNode);
  61.                                                    break;
  62.                                                case "CircularArc3d":
  63.                                                    CircularArc3d ca = (CircularArc3d)curve.NativeCurve;
  64.                                                    double angle = Vector3d.XAxis
  65.                                                         .TransformBy(Matrix3d.PlaneToWorld(ca.Normal))
  66.                                                         .GetAngleTo(ca.ReferenceVector, ca.Normal);
  67.                                                    XmlElement caNode = xmlDoc.CreateElement("CircularArc3d");
  68.                                                    caNode.SetAttribute("StartPoint", ca.StartPoint.ToString());
  69.                                                    caNode.SetAttribute("EndPoint", ca.EndPoint.ToString());
  70.                                                    caNode.SetAttribute("Center", ca.Center.ToString());
  71.                                                    caNode.SetAttribute("Radius", ca.Radius.ToString());
  72.                                                    caNode.SetAttribute("StartAngle", (ca.StartAngle + angle).ToString());
  73.                                                    caNode.SetAttribute("EndAngle", (ca.EndAngle + angle).ToString());
  74.                                                    caNode.SetAttribute("Normal", ca.Normal.ToString());
  75.                                                    loopNode.AppendChild(caNode);
  76.                                                    break;
  77.                                                case "EllipticalArc3d":
  78.                                                    EllipticalArc3d ea = (EllipticalArc3d)curve.NativeCurve;
  79.                                                    XmlElement eaNode = xmlDoc.CreateElement("EllipticalArc3d");
  80.                                                    eaNode.SetAttribute("StartPoint", ea.StartPoint.ToString());
  81.                                                    eaNode.SetAttribute("EndPoint", ea.EndPoint.ToString());
  82.                                                    eaNode.SetAttribute("Center", ea.Center.ToString());
  83.                                                    eaNode.SetAttribute("StartAngle", ea.StartAngle.ToString());
  84.                                                    eaNode.SetAttribute("EndAngle", ea.EndAngle.ToString());
  85.                                                    eaNode.SetAttribute("MajorAxis", ea.MajorAxis.ToString());
  86.                                                    eaNode.SetAttribute("MinorAxis", ea.MinorAxis.ToString());
  87.                                                    eaNode.SetAttribute("MajorRadius", ea.MajorRadius.ToString());
  88.                                                    eaNode.SetAttribute("MinorRadius", ea.MinorRadius.ToString());
  89.                                                    eaNode.SetAttribute("Normal", ea.Normal.ToString());
  90.                                                    loopNode.AppendChild(eaNode);
  91.                                                    break;
  92.                                                case "NurbCurve3d":
  93.                                                    XmlElement ncNode = xmlDoc.CreateElement("NurbCurve3d");
  94.                                                    NurbCurve3d nc = (NurbCurve3d)curve.NativeCurve;
  95.                                                    NurbCurve3dData data = nc.DefinitionData;
  96.                                                    ncNode.SetAttribute("StartPoint", nc.StartPoint.ToString());
  97.                                                    ncNode.SetAttribute("EndPoint", nc.EndPoint.ToString());
  98.                                                    ncNode.SetAttribute("Degree", data.Degree.ToString());
  99.                                                    ncNode.SetAttribute("Periodic", data.Periodic.ToString());
  100.                                                    ncNode.SetAttribute("Rational", data.Rational.ToString());
  101.                                                    for (int i = 0; i < data.Knots.Count; i++)
  102.                                                    {
  103.                                                        ncNode.SetAttribute(string.Format("Knot_{0}", i + 1), data.Knots[i].ToString());
  104.                                                    }
  105.                                                    for (int i = 0; i < data.Weights.Count; i++)
  106.                                                    {
  107.                                                        ncNode.SetAttribute(string.Format("Weight_{0}", i + 1), data.Weights[i].ToString());
  108.                                                    }
  109.                                                    for (int i = 0; i < data.ControlPoints.Count; i++)
  110.                                                    {
  111.                                                        ncNode.SetAttribute(string.Format("ControlPoint_{0}", i + 1), data.ControlPoints[i].ToString());
  112.                                                    }
  113.                                                    loopNode.AppendChild(ncNode);
  114.                                                    break;
  115.                                            }
  116.                                        }
  117.                                    }
  118.                                }
  119.                                catch
  120.                                {
  121.                                    XmlElement loopNode = xmlDoc.CreateElement("LoopUnbounded");
  122.                                    faceNode.AppendChild(loopNode);
  123.                                }
  124.                            }
  125.                        }
  126.                    }
  127.                    xmlDoc.Save(filename);
  128.                }
  129.                tr.Commit();
  130.            }
  131.        }

Heres a little example of the returned file content for a region made of a rectangle from 0,0 to 80,50 with a circular hole which center is at 20,30 and a 10 radius:
Code - XML: [Select]
  1. <?xml version="1.0"?>
  2. <Brep>
  3.  <Region Handle="2747">
  4.    <Complex>
  5.      <Shell Type="ShellExterior">
  6.        <Face>
  7.          <Loop Type="LoopInterior">
  8.            <CircularArc3d StartPoint="(30,30,0)" EndPoint="(30,30,0)" Center="(20,30,0)" Radius="10" StartAngle="3.14159265358979" EndAngle="9.42477796076938" Normal="(0,0,-1)" />
  9.          </Loop>
  10.          <Loop Type="LoopExterior">
  11.            <LineSegment3d StartPoint="(0,0,0)" EndPoint="(80,0,0)" Direction="(1,0,0)" />
  12.            <LineSegment3d StartPoint="(80,0,0)" EndPoint="(80,50,0)" Direction="(0,1,0)" />
  13.            <LineSegment3d StartPoint="(80,50,0)" EndPoint="(0,50,0)" Direction="(-1,0,0)" />
  14.            <LineSegment3d StartPoint="(0,50,0)" EndPoint="(0,0,0)" Direction="(0,-1,0)" />
  15.          </Loop>
  16.        </Face>
  17.      </Shell>
  18.    </Complex>
  19.  </Region>
  20. </Brep>
« Last Edit: July 12, 2016, 06:31:08 am by gile »
Speaking English as a French Frog

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #23 on: September 11, 2011, 08:36:50 am »
Hi,

I expanded a little the GeometryExtensions library.
There're two more classes: PolylineSegment and PolylineSegmentCollection (which inherits from List<PolylineSegment>) and a new extension method: Ellipse.ToPolyline().
I hope these classes make easier some work with polylines or to create polylines from other entities.

Here's a quick and dirty example (will give unexpected result with entities which don't lies on the WCS XY plane).
The MJOIN command joins the selected entities: line, arc, polyline, ellipse and spline (A2010 or later for spline) into one or more polyline(s).

Code: [Select]
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using GeometryExtensions;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;

namespace GeometryExtensionsTestCommands
{
    public class MjoinCommand
    {
        [CommandMethod("mjoin")]
        public void PolyJoin()
        {
            Document doc = AcAp.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            TypedValue[] filter = {   
                new TypedValue(-4,"<OR"),
                new TypedValue(0, "ARC,ELLIPSE,LINE,LWPOLYLINE"),
                new TypedValue(-4, "<AND"),
                new TypedValue(0, "SPLINE"),
                new TypedValue(-4, "&"),
                new TypedValue(70, 8),
                new TypedValue(-4, "AND>"),
                new TypedValue(-4, "OR>")};
            PromptSelectionResult psr = ed.GetSelection(new SelectionFilter(filter));
            if (psr.Status != PromptStatus.OK) return;
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTableRecord btr =
                    (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
                PolylineSegmentCollection psc = new PolylineSegmentCollection();
                Plane plane = new Plane(Point3d.Origin, Vector3d.ZAxis);
                foreach (ObjectId id in psr.Value.GetObjectIds())
                {
                    Entity ent = (Entity)tr.GetObject(id, OpenMode.ForRead);
                    switch (ent.GetType().Name)
                    {
                        case "Arc":
                            Arc arc = (Arc)ent;
                            psc.Add(new PolylineSegment(
                                new CircularArc2d(
                                    arc.Center.Convert2d(plane),
                                    arc.Radius,
                                    arc.StartAngle,
                                    arc.EndAngle,
                                    Vector2d.XAxis,
                                    false)));
                            break;
                        case "Ellipse":
                            Ellipse el = (Ellipse)ent;
                            psc.AddRange(new PolylineSegmentCollection(el));
                            break;
                        case "Line":
                            Line l = (Line)ent;
                            psc.Add(new PolylineSegment(
                                new LineSegment2d(
                                    l.StartPoint.Convert2d(plane),
                                    l.EndPoint.Convert2d(plane))));
                            break;
                        case "Polyline":
                            Polyline pl = (Polyline)ent;
                            psc.AddRange(new PolylineSegmentCollection(pl));
                            break;
                        case "Spline":
                            try
                            {
                                Spline spl = (Spline)ent;
                                psc.AddRange(new PolylineSegmentCollection((Polyline)spl.ToPolyline()));
                            }
                            catch { }
                            break;
                        default:
                            break;
                    }
                }
                foreach (PolylineSegmentCollection segs in psc.Join())
                {
                    Polyline pline = segs.ToPolyline();
                    btr.AppendEntity(pline);
                    tr.AddNewlyCreatedDBObject(pline, true);
                }
                tr.Commit();
            }
        }
    }
}
Speaking English as a French Frog

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #24 on: October 16, 2011, 11:02:36 am »
Hi,

The GeometryExtension librairy keeps on growing.

The main new things are Polyline, Polyline2d and Polyline3d extensions methods : GetProjectedPolyline and GetOrthoProjectedPolyline.

These methods create a new Polyline object and may replace the GetProjectedCurve and GetOrthoProjectedCurve methods which do not give expected results with Polyline and Polyline2d entities.
The elliptical arcs resulting of the polyline arcs projection are approximated using the PolylineSegmentCollection(Ellipse ellipse) constructor. This constructor uses the same geometric method as the 'old' AutoCAD native approximation of ellipses (i.e. PELLIPSE = 1).
« Last Edit: October 16, 2011, 11:16:30 am by gile »
Speaking English as a French Frog

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #25 on: November 14, 2011, 03:13:32 pm »
New version of GeometryExtensions (1.3).
Download updated in the first message.
Corrected some bugs with the GetProjectedPolyline and GetOrthoProjectedPolyline methods.
Speaking English as a French Frog

fixo

  • Guest
Re: .NET GEOMETRY Routines
« Reply #26 on: May 05, 2012, 04:56:14 am »
Draw bisector berween 2 lines
Code: [Select]
        [CommandMethod("bisect", CommandFlags.Modal | CommandFlags.Redraw)]
        public static void TestForBiLine()
        {
            // Get the current document and database
            Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            // Select lines
            PromptEntityOptions opt = new PromptEntityOptions("\nSelect a first line:");
            opt.SetRejectMessage("\nMust be selected the line only!");
            opt.AddAllowedClass(typeof(Line),false);

            PromptEntityResult res1 = ed.GetEntity(opt);
            // Set 'black hole' to exit if something wrong
            if (res1.Status != PromptStatus.OK)return;
         opt=   new PromptEntityOptions("\nSelect a second line:");

            PromptEntityResult res2 = ed.GetEntity(opt);
            // Set 'black hole' to exit if something wrong
            if (res2.Status != PromptStatus.OK) return;
            // Open transaction
             Transaction tr = db.TransactionManager.StartTransaction();
             using (tr)
             {
             
                 DBObject obj1 = tr.GetObject(res1.ObjectId, OpenMode.ForWrite);
                 Line ln1 = obj1 as Line;
                 // Set 'black hole' to exit if something wrong
                 if (ln1 == null) return;
               

                 DBObject obj2 = tr.GetObject(res2.ObjectId, OpenMode.ForWrite);
                 Line ln2 = obj2 as Line;
                 // Set 'black hole' to exit if something wrong
                 if (ln2 == null) return;

         
                 Point3d ps1 = ln1.StartPoint;
                 Point3d pe1 = ln1.EndPoint;
                 Point3d ps2 = ln2.StartPoint;
                 Point3d pe2 = ln2.EndPoint;

                 Point3dCollection pts = new Point3dCollection();
                 ln1.IntersectWith(ln2, Intersect.ExtendBoth, pts, 0, 0);
                 // Set 'black hole' to exit if no intersection
                 if (pts.Count != 1)
                 {
                     acadApp.ShowAlertDialog("Lines are colinear or does not intersects each another");

                     return;
                 }
                 // Calculate nearest point to the first line
                 Point3d pk1 = res1.PickedPoint;
                 pk1 = ln1.GetClosestPointTo(pk1, true);
                 // Calculate nearest point to the first line
                 Point3d pk2 = res2.PickedPoint;
                 pk2 = ln2.GetClosestPointTo(pk2, true);
                 // Swap start and end points if lines has different direction
                 if (ps1.DistanceTo(ps2) + pe1.DistanceTo(pe2) > (ps1.DistanceTo(pe2) + pe1.DistanceTo(ps2)))
                 {
                     Point3d tp = new Point3d(ps1.X, ps1.Y, ps1.Z);
                     ps1 = pe1;
                     pe1 = tp;

                 }
                 // create the first temporary Line3d object
                 Line3d tl1 = new Line3d(ps1, ps2);
                 // Calculate midpoint of this line
                 Point3d tp1 = tl1.EvaluatePoint(0.5);
                 // create the second temporary Line3d object
                 Line3d tl2 = new Line3d(pe1, pe2);
                 // Calculate midpoint of this line
                 Point3d tp2 = tl2.EvaluatePoint(0.5);
                 // Create bisector line
                 Line ln = new Line(tp1, tp2);
                 ln.SetDatabaseDefaults();
                 BlockTableRecord btr = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                 // Append line to the current space and transaction
                 btr.AppendEntity(ln);
                 tr.AddNewlyCreatedDBObject(ln, true);
                 // Commit transaction
                 tr.Commit();
             }
        }

Regards,

~'J'~
« Last Edit: May 05, 2012, 03:21:03 pm by fixo »

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #27 on: May 05, 2012, 03:13:26 pm »
Hi Oleg,

Here's another way using vectors

Code - C#: [Select]
  1.        [CommandMethod("Bisect")]
  2.        public void Bisector()
  3.        {
  4.            Document doc = AcAp.DocumentManager.MdiActiveDocument;
  5.            Database db = doc.Database;
  6.            Editor ed = doc.Editor;
  7.            PromptEntityOptions peo = new PromptEntityOptions("\nSelect the first line: ");
  8.            peo.SetRejectMessage("Selected object is not a line !");
  9.            peo.AddAllowedClass(typeof(Line), true);
  10.            PromptEntityResult per = ed.GetEntity(peo);
  11.            if (per.Status != PromptStatus.OK) return;
  12.            Point3d p1 = per.PickedPoint.TransformBy(ed.CurrentUserCoordinateSystem);
  13.            ObjectId id1 = per.ObjectId;
  14.            peo.Message = "\nSelect the second line: ";
  15.            per = ed.GetEntity(peo);
  16.            if (per.Status != PromptStatus.OK) return;
  17.            Point3d p2 = per.PickedPoint.TransformBy(ed.CurrentUserCoordinateSystem);
  18.            ObjectId id2 = per.ObjectId;
  19.            using (Transaction tr = db.TransactionManager.StartTransaction())
  20.            {
  21.                Line l1 = (Line)tr.GetObject(id1, OpenMode.ForRead);
  22.                Line l2 = (Line)tr.GetObject(id2, OpenMode.ForRead);
  23.  
  24.                // Checks if lines intersect
  25.                Plane plane;
  26.                Line3d line1 = new Line3d(l1.StartPoint, l1.EndPoint);
  27.                Line3d line2 = new Line3d(l2.StartPoint, l2.EndPoint);
  28.                if (!line1.IsCoplanarWith(line2, out plane) || line1.IsParallelTo(line2))
  29.                    return;
  30.  
  31.                // Calculates the bisector
  32.                Point3d inters = line1.IntersectWith(line2)[0];
  33.                Vector3d vec1 = line1.Direction;
  34.                Vector3d vec2 = line2.Direction;
  35.                // Corrects the vectors direction according to picked points
  36.                if (vec1.DotProduct(inters.GetVectorTo(p1)) < 0)
  37.                    vec1 = vec1.Negate();
  38.                if (vec2.DotProduct(inters.GetVectorTo(p2)) < 0)
  39.                    vec2 = vec2.Negate();
  40.                Vector3d bisectDir = (vec1 + vec2) / 2.0;
  41.  
  42.                // Draws the bisector (XLine)
  43.                Xline xline = new Xline();
  44.                xline.UnitDir = bisectDir.GetNormal();
  45.                xline.BasePoint = inters;
  46.                BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
  47.                btr.AppendEntity(xline);
  48.                tr.AddNewlyCreatedDBObject(xline, true);
  49.                tr.Commit();
  50.            }
  51.        }

Note: AFAIK, there's no need to call Dispose() on Line3d objects (as for Vector3d, Point3d, ...).
Speaking English as a French Frog

fixo

  • Guest
Re: .NET GEOMETRY Routines
« Reply #28 on: May 05, 2012, 03:22:47 pm »
Thanks, Gilles, I removed these lines from code
Regards,
Oleg

fixo

  • Guest
Re: .NET GEOMETRY Routines
« Reply #29 on: May 06, 2012, 02:27:34 am »
Circle by 3 points
Code: [Select]

        [CommandMethod("circ3p")]// This method can have any name
        public static void TestFor3PointCircle()
        {
            // Get the document and database
            Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            // If we have a valid point selection, get all 3 points
           PromptPointOptions ppo =   new PromptPointOptions("\nPick a point : ");
            ppo.AllowNone = true;
            PromptPointResult ppr;
                ppr = ed.GetPoint(ppo);
                if (ppr.Status != PromptStatus.OK) return;
            Point3d p1= ppr.Value;
            ppo =   new PromptPointOptions("\nPick a second point : ");
                 ppr = ed.GetPoint(ppo);
                if (ppr.Status != PromptStatus.OK) return;
            Point3d p2= ppr.Value;
                        ppo =   new PromptPointOptions("\nPick a third point : ");
                 ppr = ed.GetPoint(ppo);
                if (ppr.Status != PromptStatus.OK) return;
            Point3d p3= ppr.Value;
            // To pass center point by reference
            Point3d pc = new Point3d();
            // Get result
            bool result = CircleCenter(p1, p2, p3, out pc);
            if (result)
                ed.WriteMessage("\nCenter point calculated:\t{0:f3},{1:f3},{2:f3}", pc.X, pc.Y, pc.Z);
            else return;
            // Calculate radius
            double rad = pc.DistanceTo(p1);
            // Open transaction and add circle to the current space
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                Circle circ = new Circle(pc, Vector3d.ZAxis, rad);
                BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
                btr.AppendEntity(circ);
                tr.AddNewlyCreatedDBObject(circ, true);
                // Commit transaction
                tr.Commit();
               
            }
         
        }

        public static bool CircleCenter(Point3d p0, Point3d pm, Point3d p1, out Point3d pc)
        {
            Vector3d v1 = p0 - pm;

            Vector3d v2 = p1 - pm;

            if (v1.IsParallelTo(v2))
            {
                pc = new Point3d();

                return false;
            }

            CircularArc3d crc = new CircularArc3d(p0, pm, p1);

            pc = crc.Center;

            return true;
        }

    }

Regards,

~'J'~

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #30 on: August 15, 2012, 05:52:44 am »
New release of the GeometryExtensions Library (1.4).
See reply #1.
Speaking English as a French Frog

lamarn

  • Swamp Rat
  • Posts: 520
Re: .NET GEOMETRY Routines
« Reply #31 on: August 15, 2012, 10:23:58 am »
Hi Gile,
I am intrested in this extension, however i am not a C programmer.
Do you have a workaround to test it or do you have a test file or programm?
Design is something you should do with both hands. My 2d hand , my 3d hand ..

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #32 on: August 15, 2012, 01:05:21 pm »
Hi,

You can reference the DLL in a VB project to get all extensions methods and classes available in this project.
« Last Edit: August 15, 2012, 01:20:43 pm by gile »
Speaking English as a French Frog

Jeff H

  • Needs a day job
  • Posts: 5808
Re: .NET GEOMETRY Routines
« Reply #33 on: September 27, 2012, 01:25:34 am »
Gile I can not believe I have not thanked you yet for extension libraries.
So Thanks.
 
Good stuff.

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #34 on: September 27, 2012, 05:03:11 am »
You're very welcome Jeff.
Speaking English as a French Frog

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #35 on: September 14, 2013, 12:58:46 pm »
Related to this thread, a little extension for GeometryExtensions library: two methods which extents the CircularArc3d type to find tangents between to a point or another instance of CircularArc3d.

Code - C#: [Select]
  1. using System;
  2. using Autodesk.AutoCAD.Geometry;
  3.  
  4. namespace GeometryExtensions
  5. {
  6.    /// <summary>
  7.    /// Tangent type enum
  8.    /// </summary>
  9.    [Flags]
  10.    public enum TangentType { Inner = 1, Outer = 2 }
  11.  
  12.    /// <summary>
  13.    /// Provides extension methods for the CircularArc3d class.
  14.    /// </summary>
  15.    public static class CircularArc3dExtensions
  16.    {
  17.        /// <summary>
  18.        /// Returns the tangents between the active CircularArc3d instance complete circle and another one.
  19.        /// </summary>
  20.        /// <remarks>
  21.        /// Tangents start points are on the object to which this method applies, end points on the one passed as argument.
  22.        /// Tangents are always returned in the same order: outer tangents before inner tangents, and for both,
  23.        /// the tangent on the left side of the line from this circular arc center to the other one before the one on the right side.
  24.        /// </remarks>
  25.        /// <param name="arc">The object to which this method applies.</param>
  26.        /// <param name="other">The CircularArc3d to which searched for tangents.</param>
  27.        /// <param name="flags">An enum value specifying which type of tangent is returned.</param>
  28.        /// <returns>An array of LineSegment3d representing the tangents (maybe 2 or 4) or null if there is none.</returns>
  29.        /// <exception cref="Autodesk.AutoCAD.Runtime.exception">eNonCoplanarGeometry is thrown if the objects do not lies on the same plane.</exception>
  30.        public static LineSegment3d[] GetTangentsTo(this CircularArc3d arc, CircularArc3d other, TangentType flags)
  31.        {
  32.            // check if circles lies on the same plane
  33.            Vector3d normal = arc.Normal;
  34.            double elev1 = arc.Center.TransformBy(Matrix3d.WorldToPlane(normal)).Z;
  35.            double elev2 = other.Center.TransformBy(Matrix3d.WorldToPlane(normal)).Z;
  36.            if (!(normal.IsParallelTo(other.Normal) &&
  37.                Math.Abs(elev1 - elev2) < Tolerance.Global.EqualPoint))
  38.                throw new Autodesk.AutoCAD.Runtime.Exception(
  39.                    Autodesk.AutoCAD.Runtime.ErrorStatus.NonCoplanarGeometry);
  40.  
  41.            // check if a circle is inside the other
  42.            double dist = arc.Center.DistanceTo(other.Center);
  43.            if (dist - Math.Abs(arc.Radius - other.Radius) <= Tolerance.Global.EqualPoint)
  44.                return null;
  45.  
  46.            // check if circles overlap
  47.            bool overlap = arc.Radius + other.Radius >= dist;
  48.            if (overlap && flags == TangentType.Inner)
  49.                return null;
  50.  
  51.            CircularArc3d tmp1, tmp2;
  52.            Point3d[] inters;
  53.            Vector3d vec1, vec2, vec = other.Center - arc.Center;
  54.            int i, j;
  55.            LineSegment3d[] result = new LineSegment3d[(int)flags == 3 && !overlap ? 4 : 2];
  56.  
  57.            // outer tangents
  58.            if (flags.HasFlag(TangentType.Outer))
  59.            {
  60.                if (arc.Radius == other.Radius)
  61.                {
  62.                    Line3d perp = new Line3d(arc.Center, vec.CrossProduct(normal));
  63.                    inters = arc.IntersectWith(perp);
  64.                    vec1 = (inters[0] - arc.Center).GetNormal();
  65.                    vec2 = (inters[1] - arc.Center).GetNormal();
  66.                    i = vec.GetAngleTo(vec1, normal) < vec.GetAngleTo(vec2, normal) ? 0 : 1;
  67.                    j = i ^ 1;
  68.                    result[i] = new LineSegment3d(inters[0], inters[0] + vec);
  69.                    result[j] = new LineSegment3d(inters[1], inters[1] + vec);
  70.                }
  71.                else
  72.                {
  73.                    Point3d center = arc.Radius < other.Radius ? other.Center : arc.Center;
  74.                    tmp1 = new CircularArc3d(center, normal, Math.Abs(arc.Radius - other.Radius));
  75.                    tmp2 = new CircularArc3d(arc.Center + vec / 2.0, normal, dist / 2.0);
  76.                    inters = tmp1.IntersectWith(tmp2);
  77.                    vec1 = (inters[0] - center).GetNormal();
  78.                    vec2 = (inters[1] - center).GetNormal();
  79.                    i = vec.GetAngleTo(vec1, normal) < vec.GetAngleTo(vec2, normal) ? 0 : 1;
  80.                    j = i ^ 1;
  81.                    result[i] = new LineSegment3d(arc.Center + vec1 * arc.Radius, other.Center + vec1 * other.Radius);
  82.                    result[j] = new LineSegment3d(arc.Center + vec2 * arc.Radius, other.Center + vec2 * other.Radius);
  83.                }
  84.            }
  85.  
  86.            // inner tangents
  87.            if (flags.HasFlag(TangentType.Inner) && !overlap)
  88.            {
  89.                double ratio = (arc.Radius / (arc.Radius + other.Radius)) / 2.0;
  90.                tmp1 = new CircularArc3d(arc.Center + vec * ratio, normal, dist * ratio);
  91.                inters = arc.IntersectWith(tmp1);
  92.                vec1 = (inters[0] - arc.Center).GetNormal();
  93.                vec2 = (inters[1] - arc.Center).GetNormal();
  94.                i = vec.GetAngleTo(vec1, normal) < vec.GetAngleTo(vec2, normal) ? 2 : 3;
  95.                j = i == 2 ? 3 : 2;
  96.                result[i] = new LineSegment3d(arc.Center + vec1 * arc.Radius, other.Center + vec1.Negate() * other.Radius);
  97.                result[j] = new LineSegment3d(arc.Center + vec2 * arc.Radius, other.Center + vec2.Negate() * other.Radius);
  98.            }
  99.            return result;
  100.        }
  101.  
  102.        /// <summary>
  103.        /// Returns the tangents between the active CircularArc3d instance complete circle and a point.
  104.        /// </summary>
  105.        /// <remarks>
  106.        /// Tangents start points are on the object to which this method applies, end points on the point passed as argument.
  107.        /// Tangents are always returned in the same order: the tangent on the left side of the line from the circular arc center
  108.        /// to the point before the one on the right side.
  109.        /// </remarks>
  110.        /// <param name="arc">The object to which this method applies.</param>
  111.        /// <param name="pt">The Point3d to which tangents are searched</param>
  112.        /// <returns>An array of LineSegement3d representing the tangents (2) or null if there is none.</returns>
  113.        /// <exception cref="Autodesk.AutoCAD.Runtime.exception">eNonCoplanarGeometry is thrown if the objects do not lies on the same plane.</exception>
  114.        public static LineSegment3d[] GetTangentsTo(this CircularArc3d arc, Point3d pt)
  115.        {
  116.            // check if circle and point lies on the plane
  117.            Vector3d normal = arc.Normal;
  118.            double elev1 = arc.Center.TransformBy(Matrix3d.WorldToPlane(normal)).Z;
  119.            double elev2 = pt.TransformBy(Matrix3d.WorldToPlane(normal)).Z;
  120.            if (Math.Abs(elev1 - elev2) < Tolerance.Global.EqualPoint)
  121.                throw new Autodesk.AutoCAD.Runtime.Exception(
  122.                    Autodesk.AutoCAD.Runtime.ErrorStatus.NonCoplanarGeometry);
  123.  
  124.            // check if the point is inside the circle
  125.            Point3d center = arc.Center;
  126.            if (pt.DistanceTo(center) <= arc.Radius)
  127.                return null;
  128.  
  129.            Vector3d vec = pt.GetVectorTo(center) / 2.0;
  130.            CircularArc3d tmp = new CircularArc3d(pt + vec, arc.Normal, vec.Length);
  131.            Point3d[] inters = arc.IntersectWith(tmp);
  132.            LineSegment3d[] result = new LineSegment3d[2];
  133.            int i = vec.GetAngleTo(inters[0] - center, normal) < vec.GetAngleTo(inters[1] - center, normal) ? 0 : 1;
  134.            int j = i ^ 1;
  135.            result[i] = new LineSegment3d(inters[0], pt);
  136.            result[j] = new LineSegment3d(inters[1], pt);
  137.            return result;
  138.        }
  139.    }
  140. }
  141.  

A (maybe usefull) command example which draws a closed polyline along the outer tangents and the trimmed selected circles

Code - C#: [Select]
  1.        [CommandMethod("JoinCircles", CommandFlags.Modal)]
  2.        public void JoinCircles()
  3.        {
  4.            Document doc = Application.DocumentManager.MdiActiveDocument;
  5.            Database db = doc.Database;
  6.            Editor ed = doc.Editor;
  7.  
  8.            PromptEntityOptions peo = new PromptEntityOptions("\nSelect a circle: ");
  9.            peo.SetRejectMessage("Only a circle.");
  10.            peo.AddAllowedClass(typeof(Circle), true);
  11.            PromptEntityResult per = ed.GetEntity(peo);
  12.            if (per.Status != PromptStatus.OK)
  13.                return;
  14.            ObjectId id1 = per.ObjectId;
  15.  
  16.            peo.Message = "\nSelect another circle: ";
  17.            ObjectId id2;
  18.            while (true)
  19.            {
  20.                per = ed.GetEntity(peo);
  21.                if (per.Status != PromptStatus.OK)
  22.                    return;
  23.                id2 = per.ObjectId;
  24.                if (id1 == id2)
  25.                    ed.WriteMessage("\nThe second circle is the same as the first one.");
  26.                else break;
  27.            }
  28.  
  29.            try
  30.            {
  31.                using (Transaction tr = db.TransactionManager.StartTransaction())
  32.                {
  33.                    Circle c1 = (Circle)tr.GetObject(id1, OpenMode.ForRead);
  34.                    Circle c2 = (Circle)tr.GetObject(id2, OpenMode.ForRead);
  35.                    CircularArc3d ca1 = new CircularArc3d(c1.Center, c1.Normal, c1.Radius);
  36.                    CircularArc3d ca2 = new CircularArc3d(c2.Center, c2.Normal, c2.Radius);
  37.                    LineSegment3d[] lines = ca1.GetTangentsTo(ca2, TangentType.Outer);
  38.                    if (lines != null)
  39.                    {
  40.                        BlockTableRecord btr =
  41.                            (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
  42.                        Vector3d vec = c1.Center.GetVectorTo(c2.Center);
  43.                        Plane plane = new Plane(Point3d.Origin, c1.Normal);
  44.                        double a1 = vec.GetAngleTo(ca1.Center.GetVectorTo(lines[1].StartPoint), ca1.Normal) -
  45.                            vec.GetAngleTo(ca1.Center.GetVectorTo(lines[0].StartPoint), ca1.Normal);
  46.                        double a2 = vec.Negate().GetAngleTo(ca2.Center.GetVectorTo(lines[0].EndPoint), ca1.Normal) -
  47.                            vec.Negate().GetAngleTo(ca2.Center.GetVectorTo(lines[1].EndPoint), ca1.Normal);
  48.                        Polyline pline = new Polyline(4);
  49.                        pline.AddVertexAt(0, lines[0].StartPoint.Convert2d(plane), Math.Tan(a1 / 4.0), 0.0, 0.0);
  50.                        pline.AddVertexAt(1, lines[1].StartPoint.Convert2d(plane), 0.0, 0.0, 0.0);
  51.                        pline.AddVertexAt(2, lines[1].EndPoint.Convert2d(plane), Math.Tan(a2 / 4.0), 0.0, 0.0);
  52.                        pline.AddVertexAt(3, lines[0].EndPoint.Convert2d(plane), 0.0, 0.0, 0.0);
  53.                        pline.Closed = true;
  54.                        pline.Normal = c1.Normal;
  55.                        pline.Elevation = c1.Center.TransformBy(Matrix3d.WorldToPlane(c1.Normal)).Z;
  56.                        btr.AppendEntity(pline);
  57.                        tr.AddNewlyCreatedDBObject(pline, true);
  58.                        c1.UpgradeOpen();
  59.                        c2.UpgradeOpen();
  60.                        c1.Erase();
  61.                        c2.Erase();
  62.                    }
  63.                    tr.Commit();
  64.                }
  65.            }
  66.            catch (System.Exception exn)
  67.            {
  68.                ed.WriteMessage("\nError: " + exn.Message);
  69.            }
  70.        }
Speaking English as a French Frog

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #36 on: September 15, 2013, 11:36:01 am »
As i encountered some issues with the CircularArc3d.IntersectWith() method in some randomly 3d rotated planes, I decided to try the CircularArc2d route it seems to work fine.

The two methods shown upper now call equivalent extension methods for the CircularArc2d type after converting the CircularArc3d instance into CircularArc2d ones.
The Point2d.Convert3d() extension method is extracted from the GeometryExtensions library.

Code - C#: [Select]
  1. using System;
  2. using Autodesk.AutoCAD.Geometry;
  3.  
  4. namespace GeometryExtensions
  5. {
  6.    /// <summary>
  7.    /// Tangent type enum
  8.    /// </summary>
  9.    [Flags]
  10.    public enum TangentType { Inner = 1, Outer = 2 }
  11.  
  12.    /// <summary>
  13.    /// Provides extension methods for the Point2d structure.
  14.    /// </summary>
  15.    public static class Point2dExtensions
  16.    {        
  17.        /// <summary>
  18.        /// Converts a 2d point into a 3d point according to the plane defined by
  19.        /// the specified normal vector and elevation.
  20.        /// </summary>
  21.        /// <param name="pt">The instance to which the method applies.</param>
  22.        /// <param name="normal">The normal vector of the plane which the point lies on.</param>
  23.        /// <param name="elevation">The elevation of the plane which the point lies on.</param>
  24.        /// <returns>The corresponding 3d point</returns>
  25.        public static Point3d Convert3d(this Point2d pt, Vector3d normal, double elevation)
  26.        {
  27.            return new Point3d(pt.X, pt.Y, elevation).TransformBy(Matrix3d.PlaneToWorld(normal));
  28.        }
  29.    }
  30.  
  31.    /// <summary>
  32.    /// Provides extension methods for the CircularArc3d class.
  33.    /// </summary>
  34.    public static class CircularArc3dExtensions
  35.    {
  36.        /// <summary>
  37.        /// Returns the tangents between the active CircularArc3d instance complete circle and a point.
  38.        /// </summary>
  39.        /// <remarks>
  40.        /// Tangents start points are on the object to which this method applies, end points on the point passed as argument.
  41.        /// Tangents are always returned in the same order: the tangent on the left side of the line from the circular arc center
  42.        /// to the point before the one on the right side.
  43.        /// </remarks>
  44.        /// <param name="arc">The object to which this method applies.</param>
  45.        /// <param name="pt">The Point3d to which tangents are searched</param>
  46.        /// <returns>An array of LineSegement3d representing the tangents (2) or null if there is none.</returns>
  47.        /// <exception cref="Autodesk.AutoCAD.Runtime.exception">eNonCoplanarGeometry is thrown if the objects do not lies on the same plane.</exception>
  48.        public static LineSegment3d[] GetTangentsTo(this CircularArc3d arc, Point3d pt)
  49.        {
  50.            // check if arc and point lies on the plane
  51.            Vector3d normal = arc.Normal;
  52.            Matrix3d WCS2OCS = Matrix3d.WorldToPlane(normal);
  53.            double elevation = arc.Center.TransformBy(WCS2OCS).Z;
  54.            if (Math.Abs(elevation - pt.TransformBy(WCS2OCS).Z) < Tolerance.Global.EqualPoint)
  55.                throw new Autodesk.AutoCAD.Runtime.Exception(
  56.                    Autodesk.AutoCAD.Runtime.ErrorStatus.NonCoplanarGeometry);
  57.  
  58.            Plane plane = new Plane(Point3d.Origin, normal);
  59.            Matrix3d OCS2WCS = Matrix3d.PlaneToWorld(plane);
  60.            CircularArc2d ca2d = new CircularArc2d(arc.Center.Convert2d(plane), arc.Radius);
  61.            LineSegment2d[] lines2d = ca2d.GetTangentsTo(pt.Convert2d(plane));
  62.  
  63.            if (lines2d == null)
  64.                return null;
  65.  
  66.            LineSegment3d[] result = new LineSegment3d[lines2d.Length];
  67.            for (int i = 0; i < lines2d.Length; i++)
  68.            {
  69.                LineSegment2d ls2d = lines2d[i];
  70.                //Point3d p1 = new Point3d(ls2d.StartPoint.X, ls2d.StartPoint.Y, elevation);
  71.                //Point3d p2 = new Point3d(ls2d.EndPoint.X, ls2d.EndPoint.Y, elevation);
  72.                //result[i] = new LineSegment3d(p1.TransformBy(OCS2WCS), p2.TransformBy(OCS2WCS));
  73.                result[i] = new LineSegment3d(ls2d.StartPoint.Convert3d(normal, elevation), ls2d.EndPoint.Convert3d(normal, elevation));
  74.            }
  75.            return result;
  76.        }
  77.  
  78.        /// <summary>
  79.        /// Returns the tangents between the active CircularArc3d instance complete circle and another one.
  80.        /// </summary>
  81.        /// <remarks>
  82.        /// Tangents start points are on the object to which this method applies, end points on the one passed as argument.
  83.        /// Tangents are always returned in the same order: outer tangents before inner tangents, and for both,
  84.        /// the tangent on the left side of the line from this circular arc center to the other one before the one on the right side.
  85.        /// </remarks>
  86.        /// <param name="arc">The object to which this method applies.</param>
  87.        /// <param name="other">The CircularArc3d to which searched for tangents.</param>
  88.        /// <param name="flags">An enum value specifying which type of tangent is returned.</param>
  89.        /// <returns>An array of LineSegment3d representing the tangents (maybe 2 or 4) or null if there is none.</returns>
  90.        /// <exception cref="Autodesk.AutoCAD.Runtime.exception">eNonCoplanarGeometry is thrown if the objects do not lies on the same plane.</exception>
  91.        public static LineSegment3d[] GetTangentsTo(this CircularArc3d arc, CircularArc3d other, TangentType flags)
  92.        {
  93.            // check if circles lies on the same plane
  94.            Vector3d normal = arc.Normal;
  95.            Matrix3d WCS2OCS = Matrix3d.WorldToPlane(normal);
  96.            double elevation = arc.Center.TransformBy(WCS2OCS).Z;
  97.            if (!(normal.IsParallelTo(other.Normal) &&
  98.                Math.Abs(elevation - other.Center.TransformBy(WCS2OCS).Z) < Tolerance.Global.EqualPoint))
  99.                throw new Autodesk.AutoCAD.Runtime.Exception(
  100.                    Autodesk.AutoCAD.Runtime.ErrorStatus.NonCoplanarGeometry);
  101.  
  102.            Plane plane = new Plane(Point3d.Origin, normal);
  103.            Matrix3d OCS2WCS = Matrix3d.PlaneToWorld(plane);
  104.            CircularArc2d ca2d1 = new CircularArc2d(arc.Center.Convert2d(plane), arc.Radius);
  105.            CircularArc2d ca2d2 = new CircularArc2d(other.Center.Convert2d(plane), other.Radius);
  106.            LineSegment2d[] lines2d = ca2d1.GetTangentsTo(ca2d2, flags);
  107.  
  108.            if (lines2d == null)
  109.                return null;
  110.  
  111.            LineSegment3d[] result = new LineSegment3d[lines2d.Length];
  112.            for (int i = 0; i < lines2d.Length; i++)
  113.            {
  114.                LineSegment2d ls2d = lines2d[i];
  115.                //Point3d p1 = new Point3d(ls2d.StartPoint.X, ls2d.StartPoint.Y, elevation);
  116.                //Point3d p2 = new Point3d(ls2d.EndPoint.X, ls2d.EndPoint.Y, elevation);
  117.                //result[i] = new LineSegment3d(p1.TransformBy(OCS2WCS), p2.TransformBy(OCS2WCS));
  118.                result[i] = new LineSegment3d(ls2d.StartPoint.Convert3d(normal, elevation), ls2d.EndPoint.Convert3d(normal, elevation));
  119.            }
  120.            return result;
  121.        }
  122.    }
  123.  
  124.    /// <summary>
  125.    /// Provides extension methods for the CircularArc2d class.
  126.    /// </summary>
  127.    public static class CircularArc2dExtensions
  128.    {
  129.        /// <summary>
  130.        /// Returns the tangents between the active CircularArc2d instance complete circle and a point.
  131.        /// </summary>
  132.        /// <remarks>
  133.        /// Tangents start points are on the object to which this method applies, end points on the point passed as argument.
  134.        /// Tangents are always returned in the same order: the tangent on the left side of the line from the circular arc center
  135.        /// to the point before the one on the right side.
  136.        /// </remarks>
  137.        /// <param name="arc">The object to which this method applies.</param>
  138.        /// <param name="pt">The Point2d to which tangents are searched</param>
  139.        /// <returns>An array of LineSegement2d representing the tangents (2) or null if there is none.</returns>
  140.        public static LineSegment2d[] GetTangentsTo(this CircularArc2d arc, Point2d pt)
  141.        {
  142.            // check if the point is inside the circle
  143.            Point2d center = arc.Center;
  144.            if (pt.GetDistanceTo(center) <= arc.Radius)
  145.                return null;
  146.  
  147.            Vector2d vec = center.GetVectorTo(pt) / 2.0;
  148.            CircularArc2d tmp = new CircularArc2d(center + vec, vec.Length);
  149.            Point2d[] inters = arc.IntersectWith(tmp);
  150.            if (inters == null)
  151.                return null;
  152.            LineSegment2d[] result = new LineSegment2d[2];
  153.            Vector2d v1 = inters[0] - center;
  154.            Vector2d v2 = inters[1] - center;
  155.            int i = vec.X * v1.Y - vec.Y - v1.X > 0 ? 0 : 1;
  156.            int j = i ^ 1;
  157.            result[i] = new LineSegment2d(inters[0], pt);
  158.            result[j] = new LineSegment2d(inters[1], pt);
  159.            return result;
  160.        }
  161.  
  162.        /// <summary>
  163.        /// Returns the tangents between the active CircularArc2d instance complete circle and another one.
  164.        /// </summary>
  165.        /// <remarks>
  166.        /// Tangents start points are on the object to which this method applies, end points on the one passed as argument.
  167.        /// Tangents are always returned in the same order: outer tangents before inner tangents, and for both,
  168.        /// the tangent on the left side of the line from this circular arc center to the other one before the one on the right side.
  169.        /// </remarks>
  170.        /// <param name="arc">The object to which this method applies.</param>
  171.        /// <param name="other">The CircularArc2d to which searched for tangents.</param>
  172.        /// <param name="flags">An enum value specifying which type of tangent is returned.</param>
  173.        /// <returns>An array of LineSegment2d representing the tangents (maybe 2 or 4) or null if there is none.</returns>
  174.        public static LineSegment2d[] GetTangentsTo(this CircularArc2d arc, CircularArc2d other, TangentType flags)
  175.        {
  176.            // check if a circle is inside the other
  177.            double dist = arc.Center.GetDistanceTo(other.Center);
  178.            if (dist - Math.Abs(arc.Radius - other.Radius) <= Tolerance.Global.EqualPoint)
  179.                return null;
  180.  
  181.            // check if circles overlap
  182.            bool overlap = arc.Radius + other.Radius >= dist;
  183.            if (overlap && flags == TangentType.Inner)
  184.                return null;
  185.  
  186.            CircularArc2d tmp1, tmp2;
  187.            Point2d[] inters;
  188.            Vector2d vec1, vec2, vec = other.Center - arc.Center;
  189.            int i, j;
  190.            LineSegment2d[] result = new LineSegment2d[(int)flags == 3 && !overlap ? 4 : 2];
  191.  
  192.            // outer tangents
  193.            if (flags.HasFlag(TangentType.Outer))
  194.            {
  195.                if (arc.Radius == other.Radius)
  196.                {
  197.                    Line2d perp = new Line2d(arc.Center, vec.GetPerpendicularVector());
  198.                    inters = arc.IntersectWith(perp);
  199.                    if (inters == null)
  200.                        return null;
  201.                    vec1 = (inters[0] - arc.Center).GetNormal();
  202.                    vec2 = (inters[1] - arc.Center).GetNormal();
  203.                    i = vec.X * vec1.Y - vec.Y - vec1.X > 0 ? 0 : 1;
  204.                    j = i ^ 1;
  205.                    result[i] = new LineSegment2d(inters[0], inters[0] + vec);
  206.                    result[j] = new LineSegment2d(inters[1], inters[1] + vec);
  207.                }
  208.                else
  209.                {
  210.                    Point2d center = arc.Radius < other.Radius ? other.Center : arc.Center;
  211.                    tmp1 = new CircularArc2d(center, Math.Abs(arc.Radius - other.Radius));
  212.                    tmp2 = new CircularArc2d(arc.Center + vec / 2.0, dist / 2.0);
  213.                    inters = tmp1.IntersectWith(tmp2);
  214.                    if (inters == null)
  215.                        return null;
  216.                    vec1 = (inters[0] - center).GetNormal();
  217.                    vec2 = (inters[1] - center).GetNormal();
  218.                    i = vec.X * vec1.Y - vec.Y - vec1.X > 0 ? 0 : 1;
  219.                    j = i ^ 1;
  220.                    result[i] = new LineSegment2d(arc.Center + vec1 * arc.Radius, other.Center + vec1 * other.Radius);
  221.                    result[j] = new LineSegment2d(arc.Center + vec2 * arc.Radius, other.Center + vec2 * other.Radius);
  222.                }
  223.            }
  224.  
  225.            // inner tangents
  226.            if (flags.HasFlag(TangentType.Inner) && !overlap)
  227.            {
  228.                double ratio = (arc.Radius / (arc.Radius + other.Radius)) / 2.0;
  229.                tmp1 = new CircularArc2d(arc.Center + vec * ratio, dist * ratio);
  230.                inters = arc.IntersectWith(tmp1);
  231.                if (inters == null)
  232.                    return null;
  233.                vec1 = (inters[0] - arc.Center).GetNormal();
  234.                vec2 = (inters[1] - arc.Center).GetNormal();
  235.                i = vec.X * vec1.Y - vec.Y - vec1.X > 0 ? 2 : 3;
  236.                j = i == 2 ? 3 : 2;
  237.                result[i] = new LineSegment2d(arc.Center + vec1 * arc.Radius, other.Center + vec1.Negate() * other.Radius);
  238.                result[j] = new LineSegment2d(arc.Center + vec2 * arc.Radius, other.Center + vec2.Negate() * other.Radius);
  239.            }
  240.            return result;
  241.        }
  242.    }
  243. }
  244.  

A Test command which draws all tangents between the two selected circles. the lines are colored according to their order in the LineSegment3d array returned by GetTangentsTo() (1 red, 2 yellow, 3 green, 4 cyan).

Code - C#: [Select]
  1.        [CommandMethod("Test")]
  2.        public void Test()
  3.        {
  4.            Document doc = Application.DocumentManager.MdiActiveDocument;
  5.            Database db = doc.Database;
  6.            Editor ed = doc.Editor;
  7.  
  8.            PromptEntityOptions peo = new PromptEntityOptions("\nSelect a circle: ");
  9.            peo.SetRejectMessage("Only a circle.");
  10.            peo.AddAllowedClass(typeof(Circle), true);
  11.            PromptEntityResult per = ed.GetEntity(peo);
  12.            if (per.Status != PromptStatus.OK)
  13.                return;
  14.            ObjectId id1 = per.ObjectId;
  15.  
  16.            peo.Message = "\nSelect another circle: ";
  17.            ObjectId id2;
  18.            while (true)
  19.            {
  20.                per = ed.GetEntity(peo);
  21.                if (per.Status != PromptStatus.OK)
  22.                    return;
  23.                id2 = per.ObjectId;
  24.                if (id1 == id2)
  25.                    ed.WriteMessage("\nThe second circle is the same as the first one.");
  26.                else break;
  27.            }
  28.  
  29.            try
  30.            {
  31.                using (Transaction tr = db.TransactionManager.StartTransaction())
  32.                {
  33.                    Circle c1 = (Circle)tr.GetObject(id1, OpenMode.ForRead);
  34.                    Circle c2 = (Circle)tr.GetObject(id2, OpenMode.ForRead);
  35.  
  36.                    CircularArc3d ca1 = new CircularArc3d(c1.Center, c1.Normal, c1.Radius);
  37.                    CircularArc3d ca2 = new CircularArc3d(c2.Center, c2.Normal, c2.Radius);
  38.                    LineSegment3d[] lines = ca1.GetTangentsTo(ca2, TangentType.Inner | TangentType.Outer);
  39.                    if (lines != null)
  40.                    {
  41.                        BlockTableRecord btr =
  42.                            (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
  43.                        for (int i = 0; i < lines.Length; i++)
  44.                        {
  45.                            Line line = new Line(lines[i].StartPoint, lines[i].EndPoint);
  46.                            line.ColorIndex = i + 1;
  47.                            btr.AppendEntity(line);
  48.                            tr.AddNewlyCreatedDBObject(line, true);
  49.                        }
  50.                    }
  51.                    tr.Commit();
  52.                }
  53.            }
  54.            catch (System.Exception exn)
  55.            {
  56.                ed.WriteMessage("\n" + exn.Message);
  57.            }
  58.        }
Speaking English as a French Frog

gile

  • Water Moccasin
  • Posts: 2081
  • Marseille, France
Re: .NET GEOMETRY Routines
« Reply #37 on: September 15, 2013, 05:35:46 pm »
GeometryExtensions Update: version 1.6

Added: CircularArc2d and CircularArc3d GetTangentsTo methods.
Reorganized: one class per extended type.

GeomExtDoc.chm help file updated.

Attachments in reply #1
Speaking English as a French Frog

Bryco

  • Water Moccasin
  • Posts: 1799
Re: .NET GEOMETRY Routines
« Reply #38 on: May 14, 2016, 02:14:44 pm »
Code to extend a line to the Ucs
Code: [Select]

        [CommandMethod("trt", CommandFlags.UsePickSet)]

        public void TrimToPlane()
        {
            Database db = HostApplicationServices.WorkingDatabase;
            Document doc = acadApp.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            PromptEntityOptions peo = new PromptEntityOptions
                ("\nPick a line to extend/trim to the current Ucs:");
            peo.SetRejectMessage("Must be a line:");
            peo.AddAllowedClass(typeof(Line), true);
            PromptEntityResult per = ed.GetEntity(peo);
            if (per.Status != PromptStatus.OK) return;
            Matrix3d Ucs = ed.CurrentUserCoordinateSystem;
            using(Transaction tr = db.TransactionManager.StartTransaction())       
            { 
                Line line=tr.GetObject(per.ObjectId,OpenMode.ForWrite) as Line;
                Point3d p = line.StartPoint;
                Point3d pt = Ucs.CoordinateSystem3d.Origin;
                Vector3d v = line.EndPoint - line.StartPoint; //line.Delta;//.GetNormal();
                Vector3d n = Ucs.CoordinateSystem3d.Zaxis;
                //double Elev = pt.X * n.X + pt.Y * n.Y + pt.Z * n.Z;
                //double Dist = (Elev - (p.X * n.X) - (p.Y * n.Y) - (p.Z * n.Z)) / ((v.X * n.X) + (v.Y * n.Y) + (v.Z * n.Z));
                //Point3d intersectionPt=new Point3d(p.X + Dist * v.X, p.Y + Dist * v.Y, p.Z + Dist * v.Z);
                if (Math.Abs( n.DotProduct(v))<0.00000001)
                {
                    MessageBox.Show("The plane and line-segment are parallel, no intersection:");
                    return;
                }
                double t = n.DotProduct((pt-p))/n.DotProduct(v);
                Point3d ptofIntersection=p + v.MultiplyBy(t);
                if ((p - ptofIntersection).Length > (line.EndPoint - ptofIntersection).Length)
                    line.EndPoint = ptofIntersection;
                else
                    line.StartPoint = ptofIntersection;

            //http://www.thepolygoners.com/tutorials/lineplane/lineplane.html
                //If:
                //t > 0 and t < 1:The intersection occurs between the two end points
                //t = 0:The intersection falls on the first end point
                //t = 1:Intersection falls on the second end point
                //t > 1:Intersection occurs beyond second end Point
                //t < 0:Intersection happens before 1st end point.
                 
                tr.Commit();       
            }

        }

FriendFromArea51

  • Swamp Rat
  • Posts: 583
  • Mars sucks!
Re: .NET GEOMETRY Routines
« Reply #39 on: May 14, 2016, 10:27:19 pm »
Thanks..these have been consistently helpful

BKSpurgeon

  • Mosquito
  • Posts: 4
Re: .NET GEOMETRY Routines
« Reply #40 on: August 03, 2017, 04:35:11 am »
Does it make sense to add these ones as well? Not sure if I know what I'm doing, these are for the PolylineSegmentCollection class, many thanks to Gilles. chrs.

Code: [Select]
 
/// <summary>
        /// Creates a new instance of PolylineSegmentCollection from a Line.
        /// </summary>
        /// <param name="line">A Line instance.</param>
        public PolylineSegmentCollection(Line line)
        {
            Plane plane = new Plane(Point3d.Origin, Vector3d.ZAxis);
            _contents.Add(new PolylineSegment(
                               new LineSegment2d(
                                   line.StartPoint.Convert2d(plane),
                                   line.EndPoint.Convert2d(plane))));
        }

        /// <summary>
        /// Creates a new instance of PolylineSegmentCollection from a spline.
        /// </summary>
        /// <param name="spline">A spline instance.</param>
        public PolylineSegmentCollection(Spline spline)
        {
            _contents.AddRange(new PolylineSegmentCollection((Polyline)spline.ToPolyline()));
        }

        /// <summary>
        /// Creates a new instance of PolylineSegmentCollection from an arc.
        /// </summary>
        /// <param name="spline">An Arc instance.</param>
        public PolylineSegmentCollection(Arc arc)
        {
            Plane plane = new Plane(Point3d.Origin, Vector3d.ZAxis);
            _contents.Add(new PolylineSegment(
                                new CircularArc2d(
                                    arc.Center.Convert2d(plane),
                                    arc.Radius,
                                    arc.StartAngle,
                                    arc.EndAngle,
                                    Vector2d.XAxis,
                                    false)));
        }


Hi,

Some geometry extensions methods for Point2d, Point3d, CircularArc2d, Polyline and Polyline2d classes and two little (maybe usefull) classes: Triangle2d, Triangle3d.

All these classes work together and have to be compiled in a single DLL.

The extension methods requires to reference System.Core (.NET Framework 3.5)

EDIT: extended

The GeomExt class, contains the following extension methods:

Matrix3d Editor.UCS2WCS()
Matrix3d Editor.WCS2UCS()
Matrix3d Editor.DCS2WCS()
Matrix3d Editor.WCS2DCS()
Matrix3d Editor.DCS2PSDCS()
Matrix3d Editor.PSDCS2DCS()

Matrix3d Viewport.DCS2WCS()
Matrix3d Viewport.WCS2DCS()
Matrix3d Viewport.PSDCS2DCS()
Matrix3d Viewport.DCS2PSDCS()

Point3d Point2d.Convert3d()
Point3d Point2d.Convert3d(Plane plane)
Point3d Point2d.Convert3d(Vector3d normal, double elevation)
Point2d Point2d.Flatten(Vector3d normal)
bool IsBetween(Point2d p1, Point2d p2)
bool IsBetween(Point2d p1, Point2d p2, Tolerance tol)
Point2d Point2d.Polar(double angle, double distance)

Point3d Point3d.Convert2d()
Point3d Point3d.Flatten()
bool IsBetween(Point3d p1, Point3d p2)
bool IsBetween(Point3d p1, Point3d p2, Tolerance tol)
Point3d Point3d.Polar(double angle, double distance)
Point3d Point3d.Trans(int from, int to)
Point3d Point3d.Trans(Editor ed, int from, int to)
Point3d Point3d.Trans(CoordSystem from, CoordSystem to)
Point3d Point3d.Trans(Editor ed, CoordSystem from, CoordSystem to)

Vector3d Vector3d .Flatten()

void Point2dCollection.RemoveDuplicate()
void Point2dCollection.RemoveDuplicate(Tolerance tol)
bool Point2dCollection.Contains(Point2d pt, Tolerance tol)

void Point3dCollection.RemoveDuplicate()
void Point3dCollection.RemoveDuplicate(Tolerance tol)
bool Point3dCollection.Contains(Point3d pt, Tolerance tol)

double CircularArc2d.AlgebricArea()
Point2d CircularArc2d.Centroid()

Point2d Polyline.Centroid2d()
Point3d Polyline.Centroid()
Polyline Polyline.GetProjectedPolyline(Plane plane, Vector3d direction)
Polyline Polyline.GetOrthoProjectedPolyline(Plane plane)

Point3d Polyline2d.Centroid()
CircularArc3d Polyline2d.GetArcSegmentAt(int index)
CircularArc2d Polyline2d.GetArcSegment2dAt(int index)
LineSegment3d Polyline2d.GetLineSegmentAt(int index)
LineSegment2d Polyline2d.GetLineSegment2dAt(int index)
Polyline Polyline2d.GetProjectedPolyline(Plane plane, Vector3d direction)
Polyline Polyline2d.GetOrthoProjectedPolyline(Plane plane)
List<Vertex2d> Polyline2d.GetVertices()

Polyline Polyline3d.GetProjectedPolyline(Plane plane, Vector3d direction)
Polyline Polyline3d.GetOrthoProjectedPolyline(Plane plane)

Point3d Region.Centroid()

Point3d Spline.Centroid()

Polyline Ellipse.ToPolyline()


Triangle<T> abstract class

Constructors
Triangle()
Triangle(T[] pts)
Triangle(T a, T b, T c)

Indexor
T Item

Methods
T Inverse()
void Set(T[] pts)
void Set(T a, T b, T c)
T[] ToArray()


Triangle2d : Triangle<Point2d> class

Constructors
Triangle2d() : base()
Triangle2d(Point2d[] pts) : base(pts)
Triangle2d(Point2d a, Point2d b, Point2d c) : base(a, b, c)
Triangle2d(Point2d org, Vector2d v1, Vector2d v2)

Properties
double AlgebricArea
Point2d Centroid
CircularArc2d CircumscribedCircle
CircularArc2d InscribedCircle
bool IsClockwise

Methods
Triangle3d Convert3d(Plane plane)
Triangle3d Convert3d(Vector3d normal, double elevation)
double GetAngleAt(int index)
LineSegment2d GetSegmentAt(int index)
List<Point2d> IntersectWith(LinearEntity2d le2d)
List<Point2d> IntersectWith(LinearEntity2d le2d, Tolerance tol)
bool IsEqualTo(Triangle2d t2d)
bool IsEqualTo(Triangle2d t2d, Tolerance tol)
bool IsPointInside(Point2d pt)
bool IsPointOn(Point2d pt)
void Set(Point2d org, Vector2d v1, Vector2d v2)
Triangle2d TransformBy(Matrix2d mat)


Triangle3d : Triangle<Point3d> class

Constructors
Triangle3d() : base()
Triangle3d(Point3d[] pts) : base(pts)
Triangle3d(Point3d a, Point3d b, Point3d c) : base(a, b, c)
Triangle3d(Point3d org, Vector3d v1, Vector3d v2)

Properties
double Area
Point3d Centroid
CircularArc3d CircumscribedCircle
double Elevation
Vector3d GreatestSlope
Vector3d Horizontal
CircularArc3d InscribedCircle
bool IsHorizontal
Vector3d Normal
double SlopePerCent
Matrix3d SlopeUCS

Methods
Triangle2d Convert2d()
Triangle2d Flatten()
double GetAngleAt(int index)
BoundedPlane GetBoundedPlane()
Plane GetPlane()
LineSegment3d GetSegmentAt(int index)
bool IsEqualTo(Triangle3d t3d)
bool IsEqualTo(Triangle3d t3d, Tolerance tol)
bool IsPointInside(Point3d pt)
bool IsPointOn(Point3d pt)
void Set(Point3d org, Vector3d v1, Vector3d v2)
Triangle3d Transformby(Matrix3d mat)


PolylineSegment class

Constructors

PolylineSegment(Point2d startPoint, Point2d endPoint)
PolylineSegment(Point2d startPoint, Point2d endPoint, double bulge)
PolylineSegment(Point2d startPoint, Point2d endPoint, double bulge, double constantWidth)
PolylineSegment(Point2d startPoint, Point2d endPoint, double bulge, double startWidth, double endWidth)
PolylineSegment(Line2dSegment line)
PolylineSegment(CircularArc2d arc)

Properties

double Bulge
Point2d EndPoint
double EndWidth
bool IsLinear
Point2d StartPoint
double StartWidth

Methods

bool Equals(object obj)
PolylineSegment Clone()
int GetHashCode()
double GetParameterOf(Point3d pt)
void Inverse()
CircularArc2d ToCircularArc()
Curve2d ToCurve2d()
LineSegment2d ToLineSegment()
string ToString()


PolylineSegmentCollection : IList<PolylineSegment> class

Constructors

PolylineSegmentCollection()
PolylineSegmentCollection(Circle circle)
PolylineSegmentCollection(Ellipse ellipse)
PolylineSegmentCollection(Polyline pline)
PolylineSegmentCollection(Polyline2d pline)
PolylineSegmentCollection(IEnumerable<PolylineSegment> segments)

Methods

void Add(PolylineSegment segment)
void AddRange(IEnumerable<PolylineSegment> range)
void Clear()
bool Contains(PolylineSegment segment)
int FinIindex(Predicate<PolylineSegment> match)
int GetClosestSegmentTo(Point2d pt)
IEnumerator<PolylineSegment> GetEnumerator()
int IndexOf(PolylineSegment item)
void Insert(int index, PolylineSegment item)
void InsertRange(int index, IEnumerable<PolylineSegment> collection)
List<PolylineSegmentCollection> Join()
List<PolylineSegmentCollection> Join(Tolerance tol)
bool Remove(PolylineSegment item)
void RemoveAt(int index)
public void RemoveRange(int index, int count)
Polyline ToPolyline()

For more information, see the attached documentation.

GeomExtDoc.zip contains a .chm documentation file.
GeometryExtensions(1.6).zip contains the C# source code files and the DLLs (GeometryExtensions_18.dll for A2010-2012 and GeometryExtensions_19 for A2013+)

<EDIT: version 1.6> added CircularArc2d and CircularArc3d GetTangentsTo methods, reorganized: one class per extended type.

BKSpurgeon

  • Mosquito
  • Posts: 4
Re: .NET GEOMETRY Routines
« Reply #41 on: August 07, 2017, 12:09:12 am »
Why not allow for pull requests for GeometryExtensions classes? That way people can contribute, i'm sure many will.

Here is a small method but handy method i've added to the PolylineExtensions class which you all may find useful:

Code: [Select]
public static Point3d[] GetPolylinePoints(this Polyline pline)
        {
               
            int vn = pline.NumberOfVertices;

            Point3d[] pointsInPolyline = new Point3d[vn];
            for (int i = 0; i < vn; i++)
            {               
                Point3d pt = pline.GetPoint3dAt(i);
                pointsInPolyline[i] = pt;
            }

            return pointsInPolyline;
        }




Hi,

Some geometry extensions methods for Point2d, Point3d, CircularArc2d, Polyline and Polyline2d classes and two little (maybe usefull) classes: Triangle2d, Triangle3d.

All these classes work together and have to be compiled in a single DLL.

The extension methods requires to reference System.Core (.NET Framework 3.5)

EDIT: extended

The GeomExt class, contains the following extension methods:

Matrix3d Editor.UCS2WCS()
Matrix3d Editor.WCS2UCS()
Matrix3d Editor.DCS2WCS()
Matrix3d Editor.WCS2DCS()
Matrix3d Editor.DCS2PSDCS()
Matrix3d Editor.PSDCS2DCS()

Matrix3d Viewport.DCS2WCS()
Matrix3d Viewport.WCS2DCS()
Matrix3d Viewport.PSDCS2DCS()
Matrix3d Viewport.DCS2PSDCS()

Point3d Point2d.Convert3d()
Point3d Point2d.Convert3d(Plane plane)
Point3d Point2d.Convert3d(Vector3d normal, double elevation)
Point2d Point2d.Flatten(Vector3d normal)
bool IsBetween(Point2d p1, Point2d p2)
bool IsBetween(Point2d p1, Point2d p2, Tolerance tol)
Point2d Point2d.Polar(double angle, double distance)

Point3d Point3d.Convert2d()
Point3d Point3d.Flatten()
bool IsBetween(Point3d p1, Point3d p2)
bool IsBetween(Point3d p1, Point3d p2, Tolerance tol)
Point3d Point3d.Polar(double angle, double distance)
Point3d Point3d.Trans(int from, int to)
Point3d Point3d.Trans(Editor ed, int from, int to)
Point3d Point3d.Trans(CoordSystem from, CoordSystem to)
Point3d Point3d.Trans(Editor ed, CoordSystem from, CoordSystem to)

Vector3d Vector3d .Flatten()

void Point2dCollection.RemoveDuplicate()
void Point2dCollection.RemoveDuplicate(Tolerance tol)
bool Point2dCollection.Contains(Point2d pt, Tolerance tol)

void Point3dCollection.RemoveDuplicate()
void Point3dCollection.RemoveDuplicate(Tolerance tol)
bool Point3dCollection.Contains(Point3d pt, Tolerance tol)

double CircularArc2d.AlgebricArea()
Point2d CircularArc2d.Centroid()

Point2d Polyline.Centroid2d()
Point3d Polyline.Centroid()
Polyline Polyline.GetProjectedPolyline(Plane plane, Vector3d direction)
Polyline Polyline.GetOrthoProjectedPolyline(Plane plane)

Point3d Polyline2d.Centroid()
CircularArc3d Polyline2d.GetArcSegmentAt(int index)
CircularArc2d Polyline2d.GetArcSegment2dAt(int index)
LineSegment3d Polyline2d.GetLineSegmentAt(int index)
LineSegment2d Polyline2d.GetLineSegment2dAt(int index)
Polyline Polyline2d.GetProjectedPolyline(Plane plane, Vector3d direction)
Polyline Polyline2d.GetOrthoProjectedPolyline(Plane plane)
List<Vertex2d> Polyline2d.GetVertices()

Polyline Polyline3d.GetProjectedPolyline(Plane plane, Vector3d direction)
Polyline Polyline3d.GetOrthoProjectedPolyline(Plane plane)

Point3d Region.Centroid()

Point3d Spline.Centroid()

Polyline Ellipse.ToPolyline()


Triangle<T> abstract class

Constructors
Triangle()
Triangle(T[] pts)
Triangle(T a, T b, T c)

Indexor
T Item

Methods
T Inverse()
void Set(T[] pts)
void Set(T a, T b, T c)
T[] ToArray()


Triangle2d : Triangle<Point2d> class

Constructors
Triangle2d() : base()
Triangle2d(Point2d[] pts) : base(pts)
Triangle2d(Point2d a, Point2d b, Point2d c) : base(a, b, c)
Triangle2d(Point2d org, Vector2d v1, Vector2d v2)

Properties
double AlgebricArea
Point2d Centroid
CircularArc2d CircumscribedCircle
CircularArc2d InscribedCircle
bool IsClockwise

Methods
Triangle3d Convert3d(Plane plane)
Triangle3d Convert3d(Vector3d normal, double elevation)
double GetAngleAt(int index)
LineSegment2d GetSegmentAt(int index)
List<Point2d> IntersectWith(LinearEntity2d le2d)
List<Point2d> IntersectWith(LinearEntity2d le2d, Tolerance tol)
bool IsEqualTo(Triangle2d t2d)
bool IsEqualTo(Triangle2d t2d, Tolerance tol)
bool IsPointInside(Point2d pt)
bool IsPointOn(Point2d pt)
void Set(Point2d org, Vector2d v1, Vector2d v2)
Triangle2d TransformBy(Matrix2d mat)


Triangle3d : Triangle<Point3d> class

Constructors
Triangle3d() : base()
Triangle3d(Point3d[] pts) : base(pts)
Triangle3d(Point3d a, Point3d b, Point3d c) : base(a, b, c)
Triangle3d(Point3d org, Vector3d v1, Vector3d v2)

Properties
double Area
Point3d Centroid
CircularArc3d CircumscribedCircle
double Elevation
Vector3d GreatestSlope
Vector3d Horizontal
CircularArc3d InscribedCircle
bool IsHorizontal
Vector3d Normal
double SlopePerCent
Matrix3d SlopeUCS

Methods
Triangle2d Convert2d()
Triangle2d Flatten()
double GetAngleAt(int index)
BoundedPlane GetBoundedPlane()
Plane GetPlane()
LineSegment3d GetSegmentAt(int index)
bool IsEqualTo(Triangle3d t3d)
bool IsEqualTo(Triangle3d t3d, Tolerance tol)
bool IsPointInside(Point3d pt)
bool IsPointOn(Point3d pt)
void Set(Point3d org, Vector3d v1, Vector3d v2)
Triangle3d Transformby(Matrix3d mat)


PolylineSegment class

Constructors

PolylineSegment(Point2d startPoint, Point2d endPoint)
PolylineSegment(Point2d startPoint, Point2d endPoint, double bulge)
PolylineSegment(Point2d startPoint, Point2d endPoint, double bulge, double constantWidth)
PolylineSegment(Point2d startPoint, Point2d endPoint, double bulge, double startWidth, double endWidth)
PolylineSegment(Line2dSegment line)
PolylineSegment(CircularArc2d arc)

Properties

double Bulge
Point2d EndPoint
double EndWidth
bool IsLinear
Point2d StartPoint
double StartWidth

Methods

bool Equals(object obj)
PolylineSegment Clone()
int GetHashCode()
double GetParameterOf(Point3d pt)
void Inverse()
CircularArc2d ToCircularArc()
Curve2d ToCurve2d()
LineSegment2d ToLineSegment()
string ToString()


PolylineSegmentCollection : IList<PolylineSegment> class

Constructors

PolylineSegmentCollection()
PolylineSegmentCollection(Circle circle)
PolylineSegmentCollection(Ellipse ellipse)
PolylineSegmentCollection(Polyline pline)
PolylineSegmentCollection(Polyline2d pline)
PolylineSegmentCollection(IEnumerable<PolylineSegment> segments)

Methods

void Add(PolylineSegment segment)
void AddRange(IEnumerable<PolylineSegment> range)
void Clear()
bool Contains(PolylineSegment segment)
int FinIindex(Predicate<PolylineSegment> match)
int GetClosestSegmentTo(Point2d pt)
IEnumerator<PolylineSegment> GetEnumerator()
int IndexOf(PolylineSegment item)
void Insert(int index, PolylineSegment item)
void InsertRange(int index, IEnumerable<PolylineSegment> collection)
List<PolylineSegmentCollection> Join()
List<PolylineSegmentCollection> Join(Tolerance tol)
bool Remove(PolylineSegment item)
void RemoveAt(int index)
public void RemoveRange(int index, int count)
Polyline ToPolyline()

For more information, see the attached documentation.

GeomExtDoc.zip contains a .chm documentation file.
GeometryExtensions(1.6).zip contains the C# source code files and the DLLs (GeometryExtensions_18.dll for A2010-2012 and GeometryExtensions_19 for A2013+)

<EDIT: version 1.6> added CircularArc2d and CircularArc3d GetTangentsTo methods, reorganized: one class per extended type.