I prepared the code. Ready to go. Works correctly .Any feedback is welcome.
using System;
using System.Collections.Generic;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
namespace CCWHub
{
public class plineCCW
{
public static Boolean IsEqualacPoints(Point2d firstPoint, Point2d secondPoint, double precision)
{
return (IsEqual(firstPoint.X, secondPoint.X, precision) && IsEqual(firstPoint.Y, secondPoint.Y, precision));
}
public static Boolean IsEqual(double firstNumber, double secondNumber, double precision)
{
return (Math.Abs(firstNumber - secondNumber)) <= precision ? true : false;
}
public static Boolean IsInRange(double firstNumber, double secondNumber, double checkedNumber, double precision)
{
if (IsEqual(firstNumber, checkedNumber, precision))
return true;
if (IsEqual(secondNumber, checkedNumber, precision))
return true;
if (firstNumber < checkedNumber && checkedNumber < secondNumber)
return true;
if (secondNumber < checkedNumber && checkedNumber < firstNumber)
return true;
return false;
}
public static int GetLowestRightestPointInd(Point2dCollection points, double precision)
{
double ymin = points[0].Y, xmax = points[0].X;
int indexToReturn = 0;
for (int i = 1; i < points.Count; i++)
{
if (ymin > points[i].Y || (IsEqual(ymin, points[i].Y, precision) && xmax < points[i].X))
{
indexToReturn = i;
ymin = points[i].Y;
xmax = points[i].X;
}
}
return indexToReturn;
}
public static void Reverse(Transaction acTrans, ObjectId plineId)
{
#region init collectors
Polyline acPoly = (Polyline)acTrans.GetObject(plineId, OpenMode.ForWrite);
Point2dCollection revercedPoints = new Point2dCollection();
List<double> buldgeList = new List<double>();
List<double> width1 = new List<double>();
List<double> width2 = new List<double>();
#endregion init collectors
#region collecting
for (int i = acPoly.NumberOfVertices - 1; i >= 0; i--)
{
revercedPoints.Add(acPoly.GetPoint2dAt(i));
int k = (i == 0) ? acPoly.NumberOfVertices - 1 : i - 1;
buldgeList.Add(acPoly.GetBulgeAt(k) * -1);
width1.Add(acPoly.GetEndWidthAt(k));
width2.Add(acPoly.GetStartWidthAt(k));
}
#endregion collecting
#region changing settings
for (int i = 0; i < acPoly.NumberOfVertices; i++)
{
acPoly.SetPointAt(i, revercedPoints[i]);
acPoly.SetStartWidthAt(i, width1[i]);
acPoly.SetEndWidthAt(i, width2[i]);
acPoly.SetBulgeAt(i, buldgeList[i]);
}
#endregion changing settings
}
public static Point2dCollection GetPoints2D(Polyline acPoly)
{
Point2dCollection points = new Point2dCollection();
for (int i = 0; i < acPoly.NumberOfVertices; i++)
{
points.Add(acPoly.GetPoint2dAt(i));
}
return points;
}
public static Boolean IsPointBelongToLWPlineArcSegment(Polyline acPoly, int indStart, int indEnd, Point2d pointToCheck, double precision)
{
CircularArc2d circularArc2d = acPoly.GetArcSegment2dAt(indStart);
double radius = circularArc2d.Radius;
if (pointToCheck.GetDistanceTo(circularArc2d.Center) > radius)
{
return false;
}
//bulge
double bulge = acPoly.GetBulgeAt(indStart);
//translate points by arc center to 0,0
Vector2d centerV = circularArc2d.Center.GetAsVector();
Vector2d startV = acPoly.GetPoint2dAt(indStart).GetAsVector() - centerV;
Vector2d endV = acPoly.GetPoint2dAt(indEnd).GetAsVector() - centerV;
Vector2d pointToCheckV = pointToCheck.GetAsVector() - centerV;
//angle of rotation
double angleToRotate = (endV - startV).Angle * -1;
//make hord horizontal
Vector2d startV1 = startV.RotateBy(angleToRotate);
Vector2d endV1 = endV.RotateBy(angleToRotate);
Vector2d pointToCheckV1 = pointToCheckV.RotateBy(angleToRotate);
double firstLimit = startV1.Y, secondlimit = 0;
double radiusCorrection = (Math.Round(startV1.Y / Math.Abs(endV1.Y), 0) > 0) ? 1 : -1;
#region finding second limit
if (IsEqual(Math.Abs(bulge), 1, precision))
{
if (bulge >= 0)
{
secondlimit = radius * -1;
}
else
{
secondlimit = radius;
}
}
else if (Math.Abs(bulge) < 1)
{
secondlimit = radius * radiusCorrection;
}
else
{
secondlimit = radius * radiusCorrection * -1;
}
#endregion finding second limit
//Is in belong to segment
Boolean isIn = IsInRange(firstLimit, secondlimit, pointToCheckV1.Y, precision);
return isIn;
}
public static Boolean IsCCWnoarcs(Polyline acPoly, double precision)
{
Point2dCollection points =GetPoints2D(acPoly);
//lrpInd - Lowest Rightest Point Index
//plrpInd - previous to lrpInd
//nlrpInd - next of lrpInd
int lrpInd = GetLowestRightestPointInd(points, precision),
plrpInd = (lrpInd == 0) ? points.Count - 1 : lrpInd - 1,
nlrpInd = (lrpInd == points.Count - 1) ? 0 : lrpInd + 1;
Point2d
curPoint = acPoly.GetPoint2dAt(lrpInd),
prevPoint = acPoly.GetPoint2dAt(plrpInd),
nextPoint = acPoly.GetPoint2dAt(nlrpInd);
double
curAngle = (nextPoint.GetAsVector() - curPoint.GetAsVector()).Angle,
prevAngle = (prevPoint.GetAsVector() - curPoint.GetAsVector()).Angle;
return (prevAngle > curAngle) ? true : false;
}
public static Boolean IsCCWstraigtorarc(Transaction acTrans, ObjectId plineId, double precision)
{
Boolean isCCW = false;
Polyline acPoly = (Polyline)acTrans.GetObject(plineId, OpenMode.ForRead);
//Polyline doesn't have any arcs
if (!acPoly.HasBulges)
{
isCCW = IsCCWnoarcs(acPoly, precision);
return isCCW;
}
else
//at least one arc
{
Extents3d acPolyBB = acPoly.GeometricExtents;
double
bbMinX = acPolyBB.MinPoint.X,
bbMinY = acPolyBB.MinPoint.Y,
bbMaxX = acPolyBB.MaxPoint.X,
bbMaxY = acPolyBB.MaxPoint.Y;
#region check for convex arcs touching bounding box
for (int i = 0; i < acPoly.NumberOfVertices; i++)
{
if (acPoly.GetSegmentType(i) == SegmentType.Arc)
{
double curBulge = acPoly.GetBulgeAt(i);
CircularArc2d circularArc2d = acPoly.GetArcSegment2dAt(i);
Point2d
StartPoint = circularArc2d.StartPoint,
EndPoint = circularArc2d.EndPoint,
Center = circularArc2d.Center;
double
StartAngle = (StartPoint.GetAsVector() - Center.GetAsVector()).Angle,
EndAngle = (EndPoint.GetAsVector() - Center.GetAsVector()).Angle,
Radius = circularArc2d.Radius,
totalAngle = circularArc2d.EndAngle;
#region angle correction
if (curBulge > 0)
{
if (IsEqual(StartAngle, Math.PI * 2, precision))
StartAngle = 0;
if (IsEqual(EndAngle, 0, precision))
EndAngle = Math.PI * 2;
}
else
{
if (IsEqual(StartAngle, 0, precision))
StartAngle = Math.PI * 2;
if (IsEqual(EndAngle, Math.PI * 2, precision))
EndAngle = 0;
}
#endregion angle correction
#region quadrants
double
eastQuadrant = Center.X + Radius,
northQuadrant = Center.Y + Radius,
westQuadrant = Center.X - Radius,
southQuadrant = Center.Y - Radius;
#endregion quadrants
#region check for quadrants
//0
if (!IsEqual(eastQuadrant, StartPoint.X, precision) && !IsEqual(eastQuadrant, EndPoint.X, precision) &&
((StartAngle > EndAngle && curBulge > 0) || (StartAngle < EndAngle && curBulge < 0))
&& IsEqual(eastQuadrant, bbMaxX, precision))
{
isCCW = !circularArc2d.IsClockWise;
return isCCW;
}
//90
else if
(!IsEqual(northQuadrant, StartPoint.Y, precision) && !IsEqual(northQuadrant, EndPoint.Y, precision) &&
((StartAngle < Math.PI * 0.5 && Math.PI * 0.5 < EndAngle && curBulge > 0) || (EndAngle < Math.PI * 0.5 && Math.PI * 0.5 < StartAngle && curBulge < 0)) &&
IsEqual(northQuadrant, bbMaxY, precision))
{
isCCW = !circularArc2d.IsClockWise;
return isCCW;
}
//180
else if
(!IsEqual(westQuadrant, StartPoint.X, precision) && !IsEqual(westQuadrant, EndPoint.X, precision) &&
((StartAngle < Math.PI && Math.PI < EndAngle && curBulge > 0) || (EndAngle < Math.PI && Math.PI < StartAngle && curBulge < 0)) &&
IsEqual(westQuadrant, bbMinX, precision))
{
isCCW = !circularArc2d.IsClockWise;
return isCCW;
}
//270
else if
(!IsEqual(southQuadrant, StartPoint.Y, precision) && !IsEqual(southQuadrant, EndPoint.Y, precision) &&
((StartAngle < Math.PI * 1.5 && Math.PI * 1.5 < EndAngle && curBulge > 0) || (EndAngle < Math.PI * 1.5 && Math.PI * 1.5 < StartAngle && curBulge < 0)) &&
IsEqual(southQuadrant, bbMinY, precision))
{
isCCW = !circularArc2d.IsClockWise;
return isCCW;
}
#endregion check for quadrants
}
}
#endregion check for convex arcs touching bounding box
Point2dCollection points =GetPoints2D(acPoly);
//lrpInd - Lowest Rightest Point Index
//plrpInd - previous to lrpInd
//nlrpInd - next of lrpInd
int lrpInd = GetLowestRightestPointInd(points, precision),
plrpInd = (lrpInd == 0) ? points.Count - 1 : lrpInd - 1,
nlrpInd = (lrpInd == points.Count - 1) ? 0 : lrpInd + 1;
Point2d
curPoint = acPoly.GetPoint2dAt(lrpInd),
prevPoint = acPoly.GetPoint2dAt(plrpInd),
nextPoint = acPoly.GetPoint2dAt(nlrpInd);
if (acPoly.GetSegmentType(lrpInd) == SegmentType.Line && acPoly.GetSegmentType(plrpInd) == SegmentType.Line)
{
double
curAngle = (nextPoint.GetAsVector() - curPoint.GetAsVector()).Angle,
prevAngle = (prevPoint.GetAsVector() - curPoint.GetAsVector()).Angle;
isCCW = (prevAngle > curAngle) ? true : false;
return isCCW;
}
else if (acPoly.GetSegmentType(lrpInd) == SegmentType.Line && acPoly.GetSegmentType(plrpInd) == SegmentType.Arc)
{
int indStart = plrpInd;
int indEnd = lrpInd;
int indToCheck = nlrpInd;
Boolean IsIn = IsPointBelongToLWPlineArcSegment(acPoly, indStart, indEnd, acPoly.GetPoint2dAt(indToCheck), precision);
//ed.WriteMessage("\n Is in =" + IsIn.ToString());
if (IsIn)
{
CircularArc2d circularArc2d = acPoly.GetArcSegment2dAt(indStart);
isCCW = !circularArc2d.IsClockWise;
return isCCW;
}
else
{
double
curAngle = (nextPoint.GetAsVector() - curPoint.GetAsVector()).Angle,
prevAngle = (prevPoint.GetAsVector() - curPoint.GetAsVector()).Angle;
isCCW = (prevAngle > curAngle) ? true : false;
return isCCW;
}
//ed.WriteMessage("\n isCCW =" + isCCW.ToString());
}
else if (acPoly.GetSegmentType(lrpInd) == SegmentType.Arc && acPoly.GetSegmentType(plrpInd) == SegmentType.Line)
{
int indStart = lrpInd;
int indEnd = nlrpInd;
int indToCheck = plrpInd;
Boolean IsIn = IsPointBelongToLWPlineArcSegment(acPoly, indStart, indEnd, acPoly.GetPoint2dAt(indToCheck), precision);
if (IsIn)
{
CircularArc2d circularArc2d = acPoly.GetArcSegment2dAt(indStart);
isCCW = !circularArc2d.IsClockWise;
return isCCW;
}
else
{
double
curAngle = (nextPoint.GetAsVector() - curPoint.GetAsVector()).Angle,
prevAngle = (prevPoint.GetAsVector() - curPoint.GetAsVector()).Angle;
isCCW = (prevAngle > curAngle) ? true : false;
return isCCW;
}
//ed.WriteMessage("\n isCCW =" + isCCW.ToString());
}
else if (acPoly.GetSegmentType(lrpInd) == SegmentType.Arc && acPoly.GetSegmentType(plrpInd) == SegmentType.Arc)
{
CircularArc2d curArc = acPoly.GetArcSegment2dAt(lrpInd);
Point2d curCenter = curArc.Center;
double curTangentAngle = (curCenter.GetAsVector() - curPoint.GetAsVector()).Angle - Math.PI * 0.5;
CircularArc2d prevArc = acPoly.GetArcSegment2dAt(plrpInd);
Point2d preCenter = prevArc.Center;
double preTangentAngle = (preCenter.GetAsVector() - curPoint.GetAsVector()).Angle - Math.PI * 0.5;
isCCW = (curTangentAngle < preTangentAngle) ? !curArc.IsClockWise : !prevArc.IsClockWise;
return isCCW;
}
}
return isCCW;
}
}
}Call is :
[CommandMethod("MyGroup", "MyCommand", "MyCommandLocal", CommandFlags.Modal)]
public void MyCommand() // This method can have any name
{
double precision = 0.0001;
Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
Editor ed = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor;
if (doc != null)
{
PromptEntityOptions peo = new PromptEntityOptions("\nselect polyline");
peo.SetRejectMessage("\nYou're missing, try again >>");
peo.AddAllowedClass(typeof(Polyline), true);
PromptEntityResult res = ed.GetEntity(peo);
if (res.Status != PromptStatus.OK)
return;
ObjectId plineId = res.ObjectId;
if (!plineId.IsValid && plineId.IsErased && plineId.IsNull && plineId.IsEffectivelyErased)
return;
using (Transaction acTrans = doc.TransactionManager.StartTransaction())
{
if (!CCWHub.plineCCW.IsCCWstraigtorarc(acTrans, plineId, precision))
{
//plinesToReverse.Add(plineId);
ed.WriteMessage("\nNo");
}
else
{
ed.WriteMessage("\nYes");
}
}
}
}