I've modified my offset quantity using GetOffsetCurves to be a negative number so as to ensure that my offset polyline is always inside the original but it is working erratically. Sometimes it is outside with a negative number and other times inside. I was thinking of testing the resulting Plines length to ensure that it is smaller (I realise there could possibly be morew than one but this would not happen in my case.)
Does this sound overly cumbersome? Would a rollback be necessary to "undo" the polyline if it is longer and therefore outside the original?
Any suggestions would be welcome.
Dale
We had this problem as well - we wanted to offset a pline inside. We found that the direction the pline was created determined which way a negative offset went. I can't remember which is which now - I'd guess that a counter clockwise pline and a negative offset would result in "inside". I like your idea that it should be smaller.
Are you using a transaction? If so, you could test that the new pline is shorter and only committ the transaction if it passes. There is probably several ways.
Thanks for the idea!
I have a function in which I had exactly the same problem. My solution at the time was to do what the OP asked about, and check the length of the resultant polyline, and just dispose it if it was wrong.
I have been fiddling with it today, and discovered that it did seem to be related to the direction the polyline was drawn, so I wrote a simple routine to try to determine what direction the polyline was drawn, and now I'm going to try to utilize that in my OffsetPoly function.
FYI my simple routine to CheckPolyDir works for me because I can expect a certain level of simplicity in the polylines I'm dealing with. The more complicated the polyline, the more difficult it would be to figure out the direction.
No matter how complex the closed polygon, the total angle subtended by the segments will tell you whether the polyline runs clockwise or counterclockwise. Here's an ObjectARX DevNote from the ADN website. This should be suitable pseudo-code for creating a .NET equivalent. You'll have to test whether running this test is faster than performing the offset (I suspect it is).
>>>
How to test the winding (turn) of a closed polyline?
How can I test whether a polyline winds clockwise or counterclockwise?
This can be determined by tracking round the polyline, and checking if each segment bends to the left or the right. The following code snippet does this. It sums up the total turn of each segment. This can be within the segment ( if its an arc ) or between one segment and the next. The result of the function is a winding number. If it winds to the left, it will be positive; if it winds to the right, it will be negative. Note that the test does not determine whether the polyline is self-intersecting.
One detail to note is that the algorithm will fail if the pline cuts back on itself along the same line.
Adesk::Boolean polyWindingNumber( AcDbPolyline*pPoly, int&winding )
{
if( !pPoly->isClosed() )
{
return Adesk::kFalse;
}
double turn = 0;
int nSegs = pPoly->numVerts();
AcGeVector3dArray startTans(nSegs);
AcGeVector3dArray endTans(nSegs);
double TWOPI = 6.28318530718;
double PIBYTWO = 1.570796326795;
for( int i=0;i<nSegs; i++ )
{
if( pPoly->segType(i) == AcDbPolyline::kArc )
{
AcGeCircArc2d arc;
pPoly->getArcSegAt(i, arc );
AcGeVector2d startTan;
AcGeVector2d endTan;
AcGeVector2dArray startDerivs;
arc.evalPoint( arc.startAng(), 1, startDerivs );
startTan = startDerivs[0];
AcGeVector2dArray endDerivs;
arc.evalPoint( arc.endAng(), 1, endDerivs);
endTan = endDerivs[0];
startTans.append(AcGeVector3d(startTan.x, startTan.y,0.0));
endTans.append(AcGeVector3d(endTan.x, endTan.y, 0.0));
double ang = arc.endAng() - arc.startAng();
turn += arc.isClockWise() ? -ang : ang;
}
else if(pPoly->segType(i) == AcDbPolyline::kLine )
{
AcGeLineSeg2d line;
pPoly->getLineSegAt(i, line );
AcGeVector2d tan2d = line.endPoint() -line.startPoint();
AcGeVector3d tan = AcGeVector3d( tan2d.x, tan2d.y, 0.0);
startTans.append(tan);
endTans.append(tan);
}
}
nSegs = startTans.length();
for(int i=0;i<nSegs; i++ )
{
double angle = endTans[i].angleTo( startTans[(i+1)%nSegs] );
AcGeVector3d norm = endTans[i].crossProduct(startTans[(i+1)%nSegs] );
angle = norm.z > 0 ? angle: -angle;
turn += angle;
}
turn = turn / TWOPI;
double lower = floor( turn );
double tol = 1e-6;
if( (turn - lower ) < tol)
winding = (int)lower;
else if( (( lower + 1 ) - turn ) < tol )
winding = (int)(lower + 1);
else
return Adesk::kFalse;
return Adesk::kTrue;
}
static void AsdkUtilsPolyWind(void)
{
// TODO: Implement the command
ads_name eName;
ads_point pt;
if( RTNORM != acedEntSel(L"\nPlease pick a polyline to test", eName, pt) )
return;
AcDbObjectId id;
acdbGetObjectId( id, eName );
AcDbPolyline*pPoly;
acdbOpenObject(pPoly, id, AcDb::kForRead );
if(pPoly == NULL )
return;
int winding =0;
if( polyWindingNumber(pPoly, winding ) )
{
acutPrintf(L"\nPolygon has winding number %d", winding );
}
else
{
acutPrintf(L"\nCannot calculate winding number for polyline");
}
pPoly->close();
// end of function
}
<<<
Thanks for chiming in Stephen. I watch this group via RSS feed, and I've been pleased by the recent increase in posts by the ADN team. You in particular, but I have noticed a couple of other names I recognize as well.
My algorithm does the same thing (I shortened the process with an assumption that is safe for my particular purpose, but not for all purposes), but I tend to only provide answers here that relate to the AutoCAD API's, or occasionally basic programming stuff, but not Mathematics, Trigonometry, or Geometry, which is where I feel this particular problem lies.
Thanks alot Stephen! I suspected it was something to do with the way the polyline was created (Winding). I just finished reading "Creating Geometric and Dimensional Constraints..." not 5 minutes ago. A little above my head but a little stretching never hurts. I'll get some much needed training by translating this code into VB .net!
Thank you again very much.
Dale
Hi,
Other way to test CCW/CW of a polyline is to calculate the "oriented area" of the pline, it sign will tell if is it ccw or cw, like this :
Public Function IsCCW(ByVal pl As Polyline) As Boolean
Dim pc As New Point3dCollection
Dim sp, ep As Double
sp = pl.StartParam
ep = pl.EndParam
For i = sp To ep
pc.Add(pl.GetPointAtParameter(i))
Next
If Not pl.Closed Then
pc.Add(pl.GetPointAtParameter(sp))
End If
Dim s As Double = 0
Dim n As Integer = pc.Count - 2
For i = 0 To n
Dim p1 As Point3d = pc(i)
Dim p2 As Point3d = pc(i + 1)
s = s + (p1.X * p2.Y - p2.X * p1.Y)
Next
If s > 0 Then
Return True
Else
Return False
End If
End Function
FYI - or,
a practical solution is to set a datum, offset with positive value and if the distance to datum is larger/smaller than from the original then offset by negative value.
(start- and endpoints of a polyline, and therefore its overall direction, may be checked with the associated start and endparam property. if the poly is closed, a third point and param could be retrieved.)
felix
Can't find what you're looking for? Ask the community or share your knowledge.