Hi pro,
Please let me know how to offset a opened polyline by pick point on side of opened polyline.
Thanks,
Solved! Go to Solution.
Solved by Alexander.Rivilis. Go to Solution.
With minimum error checking. Only as idea.
using System; using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.EditorInput; [assembly: CommandClass(typeof(Rivilis.CurveUtils))] namespace Rivilis { public class CurveUtils { [CommandMethod("OffsetCurve", CommandFlags.Modal)] public static void OffsetCurve() { Document doc = Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; PromptDoubleResult resValueOff = ed.GetDistance("\nOffset value: "); if (resValueOff.Status != PromptStatus.OK) return; PromptPointResult resPointOff = ed.GetPoint("\nOffset side: "); if (resPointOff.Status != PromptStatus.OK) return; PromptEntityOptions prCurv = new PromptEntityOptions("\nSelect curve: "); prCurv.SetRejectMessage("not a Curve"); prCurv.AddAllowedClass(typeof(Curve),false); PromptEntityResult resCurv = ed.GetEntity(prCurv); if (resCurv.Status != PromptStatus.OK) return; using (Transaction tr = doc.TransactionManager.StartTransaction()) { Curve curve = tr.GetObject(resCurv.ObjectId, OpenMode.ForRead) as Curve; if (curve != null) { BlockTableRecord btr = tr.GetObject(curve.BlockId, OpenMode.ForWrite) as BlockTableRecord; if (btr != null) { Point3d pDir = (Point3d)(Application.GetSystemVariable("VIEWDIR")); if (pDir != null) { Point3d pWCS = resPointOff.Value.TransformBy(ed.CurrentUserCoordinateSystem); double offset = IsRightDirection(curve, pWCS, pDir.GetAsVector()) ? resValueOff.Value : -resValueOff.Value; DBObjectCollection curvCols = curve.GetOffsetCurves(offset); foreach (DBObject obj in curvCols) { Curve subCurv = obj as Curve; if (subCurv != null) { btr.AppendEntity(subCurv); tr.AddNewlyCreatedDBObject(subCurv, true); } } } } } tr.Commit(); } } // Detect side of point public static bool IsRightDirection(Curve pCurv, Point3d p, Vector3d vDir) { Vector3d vNormal = Vector3d.ZAxis; if (pCurv.IsPlanar) { Plane plane = pCurv.GetPlane(); vNormal = plane.Normal; p = p.Project(plane, vDir); } Point3d pNear = pCurv.GetClosestPointTo(p, true); Vector3d vSide = p - pNear; Vector3d vDeriv = pCurv.GetFirstDerivative(pNear); if (vNormal.CrossProduct(vDeriv).DotProduct(vSide) < 0.0) return true; else return false; } } }
Відповідь корисна? Клікніть на "ВПОДОБАЙКУ" цім повідомленням! | Do you find the posts helpful? "LIKE" these posts!
Находите сообщения полезными? Поставьте "НРАВИТСЯ" этим сообщениям!
На ваше запитання відповіли? Натисніть кнопку "ПРИЙНЯТИ РІШЕННЯ" | Have your question been answered successfully? Click "ACCEPT SOLUTION" button.
На ваш вопрос успешно ответили? Нажмите кнопку "УТВЕРДИТЬ РЕШЕНИЕ"
Alexander Rivilis / Александр Ривилис / Олександр Рівіліс
Programmer & Teacher & Helper / Программист - Учитель - Помощник / Програміст - вчитель - помічник
Facebook | Twitter | LinkedIn
Nice solution
Hats off
Cheers 🙂
Hello Rivilis,
Thank you for the code, it works perfect with polyline and circle I tried but works inverse in case of line.
How can I solve this?
Regards,
From ARX docs:
> AcDbCurve::getOffsetCurves
> If the offsetDist value is negative, it is usually interpreted as being an offset to make a smaller curve (that is, for an arc it would offset to a radius that is offsetDist less than the starting curve's radius). If the negative value has no meaning in terms of making the curve smaller, a negative offsetDist may be interpreted as an offset in the direction of smaller X,Y,Z WCS coordinates. This is not enforced, so custom entities can interpret the sign of the offsetDist value however they want.
So you can not know where the offset curves will go. You have to offset, then check if the new curves are on the desired side and if not, negate the offset.
had trouble with the suggested solution(need to brush up on my math), but I have devised a method that worked with different entities:
public static bool IsRightDirection2(Curve pCurv, Point3d picked) //picked - picked point (not point on curve) { Point3d pOnCurve = pCurv.GetClosestPointTo(picked, false); var dist = picked.DistanceTo(pOnCurve); double distCheck = new double(); DBObjectCollection acDbObjColl = pCurv.GetOffsetCurves(dist); foreach (Entity acEnt in acDbObjColl) { Curve testCurve = acEnt as Curve; distCheck = picked.DistanceTo(testCurve.GetClosestPointTo(picked, false)); } if (distCheck == 0) return true; else return false; }
I still need to do some bug testing, and to be safe, you can implement the planar check from previous solution.
basically, it creates a temporary object offset-ed at small distance, and checks if distance from the new object to that point is 0. (if not 0, means its on the opposite side of the object).
Hate to wake up an old post, but it is one of the closer relevant posts to what I need. I'm a newer programmer trying to work a set of tools and was wondering if anyone could help me do this same style of function in VB.NET rather than C#.NET?
I already have it set to create a line from point a (User Selected) to point b (Also user selected), and have it offset 1 foot, but I need it to offset in a specific direction.
Namespace Tools Public Class Commands <CommandMethod("OffSeC")> Public Sub OffSeC() '' Get the current document and database Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument Dim acCurDb As Database = acDoc.Database Dim pPtRes As PromptPointResult Dim pPtOpts As PromptPointOptions = New PromptPointOptions("") '' This asks for the Dead. pPtOpts.Message = vbLf & "Choose Dead." pPtRes = acDoc.Editor.GetPoint(pPtOpts) Dim ptDead As Point3d = pPtRes.Value '' Emergency Exit Here! If pPtRes.Status = PromptStatus.Cancel Then Exit Sub '' This asks for the Live. pPtOpts.Message = vbLf & "Live?" pPtOpts.UseBasePoint = True pPtOpts.BasePoint = ptDead pPtRes = acDoc.Editor.GetPoint(pPtOpts) Dim ptLive As Point3d = pPtRes.Value '' Emergency Exit Numero Dos! If pPtRes.Status = PromptStatus.Cancel Then Exit Sub Using acTrans As Transaction = acCurDb.TransactionManager.StartTransaction() '' Open the Block Table for read. Dim acBlkTbl As BlockTable acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead) '' Open the Block table record Model space for write Dim acBlkTblRec As BlockTableRecord acBlkTblRec = acTrans.GetObject(acBlkTbl(BlockTableRecord.ModelSpace), OpenMode.ForWrite) Using acLine As Line = New Line(ptDead, ptLive) acBlkTblRec.AppendEntity(acLine) acTrans.AddNewlyCreatedDBObject(acLine, True) Dim acDbObjColl As DBObjectCollection = acLine.GetOffsetCurves(12) For Each acEnt As Entity In acDbObjColl acBlkTblRec.AppendEntity(acEnt) acTrans.AddNewlyCreatedDBObject(acEnt, True) Next End Using ' acTrans.Commit() End Using End Sub End Class End Namespace
To offset a line, you only need to translate the two end points, it's trivial. The GetSecondDerivative function should return you a vector perpendicular to your line. Not sure, but it should be an unit vector. After that, you multiply this vector with your offset and you add it to the end points:
Dim v As Vector3d = line.GetSecondDerivative(0) * offset
Dim newLine As New Line(line.StartPoint + v, line.EndPoint + v)
But wouldn't that Offset the line to the point clicked? Rather than offset it the one foot as it already does?
To check the side, you can use a cross product between a vector starting from the start of your line to the point clicked by the user and the line direction vector. The Z coordinate will be positive if the point clicked by the user is on the left of the line, negative if it is on the right.
Well, now the issue I keep running into is Line.GetSecondDerivative(0). where GetSecondDerivative spits out the "Cannot access function" error. Would I need to import anything other than these?
Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.Geometry
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.EditorInput
GetSecondDerivative is an instance method of the Autodesk.AutoCAD.DatabaseServices.Curve class, so if you have a Autodesk.AutoCAD.DatabaseServices import, it should be OK.
@FRFR1426 wrote:
To offset a line, you only need to translate the two end points, it's trivial. The GetSecondDerivative function should return you a vector perpendicular to your line. Not sure, but it should be an unit vector. After that, you multiply this vector with your offset and you add it to the end points:
Dim v As Vector3d = line.GetSecondDerivative(0) * offset
Dim newLine As New Line(line.StartPoint + v, line.EndPoint + v)
As far as I remember GetSecondDerivative for Line and stretch segment of polyline return zero-vector.
On this picture 1 - First Derivative, 2 - Second Derivative:
Відповідь корисна? Клікніть на "ВПОДОБАЙКУ" цім повідомленням! | Do you find the posts helpful? "LIKE" these posts!
Находите сообщения полезными? Поставьте "НРАВИТСЯ" этим сообщениям!
На ваше запитання відповіли? Натисніть кнопку "ПРИЙНЯТИ РІШЕННЯ" | Have your question been answered successfully? Click "ACCEPT SOLUTION" button.
На ваш вопрос успешно ответили? Нажмите кнопку "УТВЕРДИТЬ РЕШЕНИЕ"
Alexander Rivilis / Александр Ривилис / Олександр Рівіліс
Programmer & Teacher & Helper / Программист - Учитель - Помощник / Програміст - вчитель - помічник
Facebook | Twitter | LinkedIn
Yes, it's because lines have no curvature, so there is an infinity of second derivatives. You can use instead:
(line.EndPoint - line.StartPoint).GetPerpendicularVector()
I cant help you with VB, but I can give you my complete code for c#.
there are online translators C# to VB that could do the translating for ya. http://converter.telerik.com/ 4 example.
this was a jig class that would do the realtime offset preview. I was newb back then(still am), and I'm not sure if I finished everything I had planned, but here's the code I have.
basically, it would
a) project the offset line
b) check to see if the offset line is there where it's supposed to be
c) if its not there, flip the direction
using System; using System.Collections.Generic; //using System.Linq; //using System.Text; namespace myOffset { public class myOffsetJig : DrawJig { #region fields private Point3d jigOnLinePt; private Point3d selectedPt; private Polyline myPoly; //private double myAngle; private double myDist; private Entity theEntity; public Entity testCurve; private List<double> myOffsets = new List<double>() { 0, 5, 10, 15 }; private Matrix3d myPointUCS; #endregion #region constructor public myOffsetJig(Point3d onLinePt, Point3d picked, Polyline myPolyline, Matrix3d pointUCS) { jigOnLinePt = onLinePt; selectedPt = picked.TransformBy(UCS); myPoly = myPolyline; myPointUCS = pointUCS; //myAngle = angle; } #endregion #region properties public double Distance { get { return myDist; } set { myDist = value; } } public Matrix3d UCS { get { return Application.DocumentManager.MdiActiveDocument.Editor.CurrentUserCoordinateSystem; } } #endregion #region offet public Entity offsetPoly { get { DBObjectCollection acDbObjColl = myPoly.GetOffsetCurves(myDist); foreach (Entity acEnt in acDbObjColl) { theEntity = acEnt; } theEntity.ColorIndex = 4; return theEntity; } } #endregion #region // Detect side of point¸2 public static bool IsRightDirection2(Curve pCurv, Point3d picked/*, Vector3d vDir*/) { Point3d pOnCurve = pCurv.GetClosestPointTo(picked, false); var dist = picked.DistanceTo(pOnCurve); DBObjectCollection acDbObjColl = pCurv.GetOffsetCurves(dist); double distCheck = new double(); foreach (Entity acEnt in acDbObjColl) { Curve testCurve = acEnt as Curve; distCheck = picked.DistanceTo(testCurve.GetClosestPointTo(picked, false)); } if (distCheck == 0) return true; else return false; } #endregion #region WorldDraw protected override bool WorldDraw(Autodesk.AutoCAD.GraphicsInterface.WorldDraw draw) { //Matrix3d mat = Matrix3d.Displacement(mBase.GetVectorTo(mLocation)); Autodesk.AutoCAD.GraphicsInterface.WorldGeometry geo = draw.Geometry; if (geo != null) { geo.Draw(offsetPoly); foreach (var offset in myOffsets) { var myEnt = offsetWidget.drawLine(offset); myEnt.TransformBy(myPointUCS); myEnt.ColorIndex = 4; myEnt.IsContentSnappable(); geo.Draw(myEnt); } //geo.PushModelTransform(mat); //geo.PopModelTransform(); } return true; } #endregion #region SamplerStatus protected override SamplerStatus Sampler(JigPrompts prompts) { JigPromptPointOptions prDistOpt = new JigPromptPointOptions("\nInput offset"); prDistOpt.BasePoint = jigOnLinePt; prDistOpt.UseBasePoint = true; //prDistOpt.Cursor = CursorType.Parallelogram; var prDist = prompts.AcquirePoint((JigPromptPointOptions)prDistOpt); if (prDist.Status == PromptStatus.Cancel || prDist.Status == PromptStatus.Error) return SamplerStatus.Cancel; if (!prDist.Equals(0)) { myDist = prDist.Value.TransformBy(myPointUCS.Inverse()).Y; myDist = IsRightDirection2(myPoly, selectedPt /*, pDir.GetAsVector()*/) ? myDist : -myDist; return SamplerStatus.OK; } else return SamplerStatus.NoChange; } #endregion #region Initialize Jig //[CommandMethod("OffsetJig")] public void OffsetJigCommand() { try { Database db = HostApplicationServices.WorkingDatabase; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; using (Transaction tr = db.TransactionManager.StartTransaction()) { PromptResult jigRes = Application.DocumentManager.MdiActiveDocument.Editor.Drag(this); if (jigRes.Status == PromptStatus.OK) { BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); DBObjectCollection acDbObjColl = myPoly.GetOffsetCurves(myDist); foreach (Entity acEnt in acDbObjColl) { // Add each offset object btr.AppendEntity(acEnt); tr.AddNewlyCreatedDBObject(acEnt, true); } tr.Commit(); } else tr.Abort(); } } #endregion #region exception catch (System.Exception ex) { Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(ex.ToString()); } #endregion } } }
p.s. thanks giles for transformation matrix
@Alexander.Rivilis wrote:With minimum error checking. Only as idea.
using System; using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.EditorInput; [assembly: CommandClass(typeof(Rivilis.CurveUtils))] namespace Rivilis { public class CurveUtils { [CommandMethod("OffsetCurve", CommandFlags.Modal)] public static void OffsetCurve() { Document doc = Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; PromptDoubleResult resValueOff = ed.GetDistance("\nOffset value: "); if (resValueOff.Status != PromptStatus.OK) return; PromptPointResult resPointOff = ed.GetPoint("\nOffset side: "); if (resPointOff.Status != PromptStatus.OK) return; PromptEntityOptions prCurv = new PromptEntityOptions("\nSelect curve: "); prCurv.SetRejectMessage("not a Curve"); prCurv.AddAllowedClass(typeof(Curve),false); PromptEntityResult resCurv = ed.GetEntity(prCurv); if (resCurv.Status != PromptStatus.OK) return; using (Transaction tr = doc.TransactionManager.StartTransaction()) { Curve curve = tr.GetObject(resCurv.ObjectId, OpenMode.ForRead) as Curve; if (curve != null) { BlockTableRecord btr = tr.GetObject(curve.BlockId, OpenMode.ForWrite) as BlockTableRecord; if (btr != null) { Point3d pDir = (Point3d)(Application.GetSystemVariable("VIEWDIR")); if (pDir != null) { Point3d pWCS = resPointOff.Value.TransformBy(ed.CurrentUserCoordinateSystem); double offset = IsRightDirection(curve, pWCS, pDir.GetAsVector()) ? resValueOff.Value : -resValueOff.Value; DBObjectCollection curvCols = curve.GetOffsetCurves(offset); foreach (DBObject obj in curvCols) { Curve subCurv = obj as Curve; if (subCurv != null) { btr.AppendEntity(subCurv); tr.AddNewlyCreatedDBObject(subCurv, true); } } } } } tr.Commit(); } } // Detect side of point public static bool IsRightDirection(Curve pCurv, Point3d p, Vector3d vDir) { Vector3d vNormal = Vector3d.ZAxis; if (pCurv.IsPlanar) { Plane plane = pCurv.GetPlane(); vNormal = plane.Normal; p = p.Project(plane, vDir); } Point3d pNear = pCurv.GetClosestPointTo(p, true); Vector3d vSide = p - pNear; Vector3d vDeriv = pCurv.GetFirstDerivative(pNear); if (vNormal.CrossProduct(vDeriv).DotProduct(vSide) < 0.0) return true; else return false; } } }
So, I ended up using this, kind of, but I have to click the opposite side for it to offset properly, is there anyway to counteract this?
got some time to play, test the code below, hth.
double offset = IsRightDirection(curve, pWCS, pDir.GetAsVector()) ? resValueOff.Value : -resValueOff.Value; var curve1 = curve.GetOffsetCurves(-resValueOff.Value)[0] as Curve; var curve2 = curve.GetOffsetCurves(resValueOff.Value)[0] as Curve; if (curve1 != null && curve2 != null) { var givenPoint = resPointOff.Value; if (curve1.GetClosestPointTo(givenPoint, false).DistanceTo(givenPoint) < curve2.GetClosestPointTo(givenPoint, false).DistanceTo(givenPoint)) { btr.AppendEntity(curve1); tr.AddNewlyCreatedDBObject(curve1, true); } else { btr.AppendEntity(curve2); tr.AddNewlyCreatedDBObject(curve2, true); } } //DBObjectCollection curvCols = curve.GetOffsetCurves(offset); //foreach (DBObject obj in curvCols) //{ // Curve subCurv = obj as Curve; // if (subCurv != null) // { // btr.AppendEntity(subCurv); // tr.AddNewlyCreatedDBObject(subCurv, true); // } //}
This is the shortest code and easiest way for me to make it work!
[CommandMethod("OffsetPolyLine")]
public void OffsetPlines(object obj)
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
Database db = doc.Database;
CivilDocument cdoc = CivilApplication.ActiveDocument;
//select ROW polyline prompt
PromptEntityOptions peo = new PromptEntityOptions("\nSelect ROW polyline:");
peo.SetRejectMessage("\nSelected entity is not a curve");
peo.AddAllowedClass(typeof(Curve), false);
peo.AllowNone = true;
PromptEntityResult per = ed.GetEntity(peo);
if (per.Status == PromptStatus.Cancel) return;
while (per.Status == PromptStatus.OK)
{
using (DocumentLock dl = doc.LockDocument())
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
//prompt to specify offset distance
PromptDoubleOptions pdo = new PromptDoubleOptions("\nSpecify the offset distance:");
pdo.AllowNegative = false;
pdo.AllowZero = false;
pdo.DefaultValue = 10;
pdo.UseDefaultValue = true;
PromptDoubleResult pdr = ed.GetDouble(pdo);
double distance = pdr.Value;
if (pdr.Status == PromptStatus.Cancel) return;
//prompt to select side
PromptPointResult ppr = ed.GetPoint("\nSelect side to offset to:");
if (ppr.Status == PromptStatus.Cancel) return;
Polyline pl = (Polyline) tr.GetObject(per.ObjectId, OpenMode.ForRead);
Point3d cp = pl.GetClosestPointTo(ppr.Value, false);
DBObjectCollection offsetCurvesCollection1 = pl.GetOffsetCurves(distance);
DBObjectCollection offsetCurvesCollection2 = pl.GetOffsetCurves(-distance);
Curve curve1 = null;
Curve curve2 = null;
Point3d p1 = new Point3d();
Point3d p2 = new Point3d();
double dist1 = 0;
double dist2 = 0;
for (int i = 0; i < offsetCurvesCollection1.Count; i++)
{
curve1 = (Curve) offsetCurvesCollection1[i];
curve2 = (Curve) offsetCurvesCollection2[i];
p1 = curve1.GetClosestPointTo(ppr.Value, false);
p2 = curve2.GetClosestPointTo(ppr.Value, false);
dist1 = p1.DistanceTo(ppr.Value);
dist2 = p2.DistanceTo(ppr.Value);
if (dist1 < dist2)
{
AcadUtilities.PlaceEntityinDrawing(curve1);
}
else
{
AcadUtilities.PlaceEntityinDrawing(curve2);
}
}
tr.Commit();
}
}
per = ed.GetEntity(peo);
}
}