nestly Posted April 16, 2014 Author Posted April 16, 2014 Yeah, that drawing is far from self-explanatory. The crux, though, are the tangent lines (the red line, and the two rays). They describe the mating condition of arc/arc and line/arc. The arc/arc mate (red line) will always be at 45 degrees to its axis, no matter the rotation. A Cone is the 3d manifestation of that. I’m only interested in one elevation. A cone sliced at one elevation (an elevation parallel to the axis) will produce a hyperbola. I remembered that from the “Conic Section” section of some HS math classes. I was able to analyze a progression of tangent lines as they made their way through the circle/hyperbola to find a point where a 12” radius (for example) was available. That demo drawing leaves out much of the latter analysis – it just illustrates a way of reducing the amount of variables. I’ll compile the routine, post that and the source code sometime tomorrow. I'd have never thought about slicing a cone... most of my efforts involved a sphere. There's no hurry, I don't have any current projects with this condition, but I am looking forward to knowing how to do it right for future application, as well as updating my existing facility plans where I had to "fudge it" Quote
SEANT Posted April 16, 2014 Posted April 16, 2014 Here is the compiled routine. Unzip the file and save it to a location on the local machine. NETLOAD it into AutoCAD – depending on where the file is located, AutoCAD may warn you that a file is being run from a non-trusted location. Type BiRollOffset to initiate the command. Enter angle off of parallel Enter elevation change Enter elbow radius This is still a fairly crude routine, i.e. not a lot of error handling. It may be wise to only use it in an isolated instance of AutoCAD. If you prefer to compile the routine yourself, see the source below. // (C) Copyright 2014 by Sean Tessier // 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(BiRollOffset.MyCommands))] namespace BiRollOffset { public class MyCommands { //Fields private double m_radOfElbowBend; private CircularArc3d m_controlRevolve; private double m_elevChange; private double m_angOffColinear; private NurbCurve3d m_hyperbola; private Ray3d m_offAnglePipe; private CircularArc3d m_control; private CircularArc3d m_variable; private Vector3d m_offAngleDirection; private Vector3d m_toCirc; private double m_toCircLen; private double m_tempAng; [CommandMethod("STSCGroup", "BiRollOffset", "BiRoll", CommandFlags.Modal)] public void CommandInit() { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; PromptDoubleOptions pdo = new PromptDoubleOptions("\nEnter angle off of parallel: "); pdo.AllowArbitraryInput = false; pdo.AllowNegative = false; PromptDoubleResult pdr = ed.GetDouble(pdo); if (pdr.Status != PromptStatus.OK || pdr.Value <= 0) return; m_angOffColinear = Math.PI * (pdr.Value + 90.0) / 180; m_offAngleDirection = new Vector3d(Math.Cos(m_angOffColinear), Math.Sin(m_angOffColinear), 0.0); pdo.Message = " Enter elevation of higher pipe above lower:"; pdr = ed.GetDouble(pdo); if (pdr.Status != PromptStatus.OK || pdr.Value <= 0) return; m_elevChange = pdr.Value; pdo.Message = " Enter bend radius of elbows"; pdr = ed.GetDouble(pdo); if (pdr.Status != PromptStatus.OK || pdr.Value <= 0) return; m_radOfElbowBend = pdr.Value; m_hyperbola = CreateHyperbola(m_elevChange); double YProj; m_controlRevolve = CreateRevolve(m_radOfElbowBend, out YProj); m_control = new CircularArc3d(new Point3d(0.0, -YProj, m_radOfElbowBend), new Vector3d(1.0, 0.0, 0.0), new Vector3d(0.0, 0.0, -1.0), m_radOfElbowBend, 0.0, Math.PI / 4); PointOnCurve3d POC3D = new PointOnCurve3d(m_hyperbola, 0.5); PointOnCurve3d POC3DCirc = new PointOnCurve3d(m_controlRevolve, 0.0); Interval TargetRegion = new Interval(0.0, 0.5, .0000001); double SpanIncrement = TargetRegion.Length / 100.0; for (int i = 0; i < 100; i++) { if (CompAngles(ref TargetRegion, i, SpanIncrement, ref POC3D, ref POC3DCirc)) { SpanIncrement = TargetRegion.Length / 100.0; for (int j = 0; j < 100; j++) { if (CompAngles(ref TargetRegion, j, SpanIncrement, ref POC3D, ref POC3DCirc)) { SpanIncrement = TargetRegion.Length / 100.0; for (int k = 0; k < 100; k++) { if (CompAngles(ref TargetRegion, k, SpanIncrement, ref POC3D, ref POC3DCirc)) { m_offAnglePipe = new Ray3d(POC3D.Point, m_offAngleDirection); double Param = m_offAnglePipe.GetParameterAtLength(0.0, m_toCircLen, true, 0.003); Point3d OffAngStart = m_offAnglePipe.EvaluatePoint(Param); MakeVariableArc(ref POC3DCirc, OffAngStart); MakeDrawing(ref POC3D, ref POC3DCirc, ref db, OffAngStart); if (POC3D != null) POC3D.Dispose(); if (m_hyperbola != null) m_hyperbola.Dispose(); return; } } } } } } ed.WriteMessage("\nNo solution Found!"); } //Create hyperbola geometry based and 45 degree elbow of the given radius private NurbCurve3d CreateHyperbola(double Elev) { NurbCurve3d ToReturn = null; KnotCollection KC = new KnotCollection(); KC.Add(0.0); KC.Add(0.0); KC.Add(0.0); KC.Add(1.0); KC.Add(1.0); KC.Add(1.0); DoubleCollection DC = new DoubleCollection(3); DC.Add(1.0); DC.Add(2.0); DC.Add(1.0); Double Offset = (Elev * (Math.Sqrt(3 / 4.0))) * 2.0; Point3dCollection P3Dc = new Point3dCollection(); P3Dc.Add(new Point3d(-Offset, 2.0 * Elev, Elev)); P3Dc.Add(new Point3d(0, Elev / 2.0, Elev)); P3Dc.Add(new Point3d(Offset, 2.0 * Elev, Elev)); ToReturn = new NurbCurve3d(2, KC, P3Dc, DC, false); return ToReturn; } //Circle that represents the revoved end of 45 deg elbow private CircularArc3d CreateRevolve(double Rad, out double ArcStart) { double Offset = (1 - Math.Sqrt(1 / 2.0)) * Rad; CircularArc3d ToReturn = null; ToReturn = new CircularArc3d(new Point3d(0, Offset, 0), new Vector3d(0, -1, 0), Offset); double YProj = Rad * Math.Sqrt(1 / 2.0); ArcStart = YProj - Offset; return ToReturn; } //Determine if the angle between tangent line and off angle pipe run allows for desired elbow radius private bool CompAngles(ref Interval TargetRegion, int Step, double SpanIncrement, ref PointOnCurve3d POCHyp, ref PointOnCurve3d POCCirc) { double toModify = Step * SpanIncrement; double StartParam = TargetRegion.UpperBound; StartParam -= toModify; POCHyp.Parameter = StartParam; POCCirc = m_controlRevolve.GetClosestPointTo(POCHyp.Point); m_toCirc = POCHyp.Point.GetVectorTo(POCCirc.Point); m_tempAng = m_toCirc.GetAngleTo(m_offAngleDirection) / 2.0; m_tempAng = (Math.PI / 2) - m_tempAng; m_toCircLen = m_toCirc.Length; double TempToCirc = m_toCircLen / Math.Abs(Math.Tan(m_tempAng)); if (TempToCirc > m_radOfElbowBend) { TargetRegion = new Interval(StartParam, StartParam + SpanIncrement, .0000001); return true; } return false; } //Create geometry for arc insertion into drawing private void MakeVariableArc(ref PointOnCurve3d POCCirc, Point3d EndPoint) { Vector3d Norm = m_offAngleDirection.CrossProduct(m_toCirc).GetNormal(); Vector3d RotatedCircCopy = new Vector3d(m_toCirc.X, m_toCirc.Y, m_toCirc.Z); RotatedCircCopy = RotatedCircCopy.RotateBy(Math.PI / 2, Norm); Line3d FromCirc = new Line3d(POCCirc.Point, RotatedCircCopy); Vector3d RotatedHypCopy = new Vector3d(m_offAngleDirection.X, m_offAngleDirection.Y, m_offAngleDirection.Z); RotatedHypCopy = RotatedHypCopy.RotateBy(Math.PI / 2, Norm); Line3d FromHyp = new Line3d(EndPoint, RotatedHypCopy); Point3d Center = FromCirc.IntersectWith(FromHyp)[0]; m_variable = new CircularArc3d(Center, Norm, RotatedCircCopy, m_radOfElbowBend, 0.0, m_tempAng * 2.0); } //Insert the various elements into drawing private void MakeDrawing(ref PointOnCurve3d POC3DHyp, ref PointOnCurve3d POC3DCirc, ref Database db, Point3d OffAngStart) { Point3d Intersect = POC3DCirc.Point; double Angle = Math.Atan(Math.Abs(Intersect.X) / Math.Abs(Intersect.Z)); m_control.RotateBy(Angle, new Vector3d(0.0, -1.0, 0.0), new Point3d()); Arc ControlArc = Curve.CreateFromGeCurve(m_control) as Arc; if (ControlArc == null) return; Ray ToControl = new Ray(); ToControl.BasePoint = m_control.StartPoint; ToControl.SecondPoint = m_control.StartPoint + new Vector3d(0.0, -1.0, 0.0); Arc OffAngArc = Curve.CreateFromGeCurve(m_variable) as Arc; if (OffAngArc == null) return; Ray OffAng = new Ray(); OffAng.BasePoint = m_variable.EndPoint; OffAng.SecondPoint = m_variable.EndPoint + m_offAngleDirection; using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTableRecord btr = (BlockTableRecord)(tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite)); ControlArc.SetDatabaseDefaults(); btr.AppendEntity(ControlArc); tr.AddNewlyCreatedDBObject(ControlArc, true); ToControl.SetDatabaseDefaults(); btr.AppendEntity(ToControl); tr.AddNewlyCreatedDBObject(ToControl, true); OffAngArc.SetDatabaseDefaults(); btr.AppendEntity(OffAngArc); tr.AddNewlyCreatedDBObject(OffAngArc, true); OffAng.SetDatabaseDefaults(); btr.AppendEntity(OffAng); tr.AddNewlyCreatedDBObject(OffAng, true); tr.Commit(); } } } } BiRollOffset.zip Quote
SEANT Posted April 16, 2014 Posted April 16, 2014 I should also point out that the routine is only compatible with AutoCAD 2013, 2014, and (conceivably) 2015. And another aspect of the routines crude nature is that it only produces geometry in one orientation - with the off angle pipe centerline set counterclockwise from the positive Y axis. Quote
nestly Posted April 17, 2014 Author Posted April 17, 2014 (edited) Hi Sean, I'm not sure what I'm doing wrong, but I can't get it to load (AutoCAD2014) I put it in my Lisps folder, so it's in a Trusted location, but I get an Unknown Command error when I try to run it. Command: _NETLOAD Cannot load assembly. Error details: System.IO.FileLoadException: Could not load file or assembly 'file:///C:\AutoCAD2014 Support Files\Lisps\BiRollOffset.dll' or one of its dependencies. Operation is not supported. (Exception from HRESULT: 0x80131515) File name: 'file:///C:\AutoCAD2014 Support Files\Lisps\BiRollOffset.dll' ---> System.NotSupportedException: An attempt was made to load an assembly from a network location which would have caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the .NET Framework does not enable CAS policy by default, so this load may be dangerous. If this load is not intended to sandbox the assembly, please enable .... Edited April 17, 2014 by nestly Quote
SEANT Posted April 17, 2014 Posted April 17, 2014 Sorry, should have mentioned this earlier, you will probably have to Unblock the file (a Windows safety feature for files originating on a Computer other than the current). Right-click on the file and look for Unblock on the General tab. Quote
SEANT Posted April 17, 2014 Posted April 17, 2014 Incidentally, post back if you run into any bugs. Early on a ran into a couple of parameter sets that caused trouble, those particular sets are now working. As it stands the routine will just produce wanky geometry if the input radius is incapable of spanning the vertical rise. Down the road I'll try to correct these issues. Quote
nestly Posted April 17, 2014 Author Posted April 17, 2014 It's working perfectly without any errors so far. I can't thank you enough for your help... this solves a problem that's been bugging me for years. Quote
SEANT Posted April 17, 2014 Posted April 17, 2014 You're welcome. The problem was so intriguing, I should probably be thanking you. Quote
nestly Posted April 17, 2014 Author Posted April 17, 2014 SEANT rules! Yeah, I'll gladly buy him a beer, but I'm pretty sure he can just conjure one up at will. Quote
SEANT Posted April 17, 2014 Posted April 17, 2014 . . . . he can just conjure one up at will. That would be a neat trick. Of course, I'd always be too schnockered to code straight. Quote
nestly Posted April 22, 2014 Author Posted April 22, 2014 Update. I've used your code in several drawings now and it's working great. The only minor issue I've found is actually an AutoCAD limitation when the angle isn't an even number and I have to input a "rounded" angle as reported by AutoCAD (ie 51.60087231). None the less, it's still about 10000x more accurate and 100x times faster than how I had been doing it... Quote
SEANT Posted April 23, 2014 Posted April 23, 2014 Update. I've used your code in several drawings now and it's working great. The only minor issue I've found is actually an AutoCAD limitation when the angle isn't an even number and I have to input a "rounded" angle as reported by AutoCAD (ie 51.60087231). None the less, it's still about 10000x more accurate and 100x times faster than how I had been doing it... That's good to hear. The routine does a fair bit of rounding as well - I can see how a couple of compounding iterations could create issues. . . . . The only other condition I occasionally run into is when the "rise" is too small to use a standard 45, and both fittings need to be cut back (usually to the same angle). . . . . Let me know if this is a common enough situation. The same general procedure may be malleable enough to account for this. Quote
nestly Posted April 23, 2014 Author Posted April 23, 2014 Let me know if this is a common enough situation. The same general procedure may be malleable enough to account for this. It's not a common condition. but it does happen occasionally. I'm pretty happy with what you've done, but if you want an additional challenge, it would be cool if the angle and elevation could be established by "picking" two objects (lines) and have the result appear at that location (similar to how a FILLET would work) Quote
tzframpton Posted April 23, 2014 Posted April 23, 2014 Just wanted to slide in real quick to say that I'm glad to see you active again Nestly. You were on a bit of a hiatus for a while. Sorry to thread jack. Quote
SEANT Posted April 24, 2014 Posted April 24, 2014 It's not a common condition. but it does happen occasionally. I'm pretty happy with what you've done, but if you want an additional challenge, it would be cool if the angle and elevation could be established by "picking" two objects (lines) and have the result appear at that location (similar to how a FILLET would work) That would be very AutoCADesque. I'll give the routine some additional attention. Quote
Dadgad Posted April 25, 2014 Posted April 25, 2014 Just wanted to slide in real quick to say that I'm glad to see you active again Nestly. You were on a bit of a hiatus for a while. Sorry to thread jack. +1 Quote
SSTeele Posted May 15, 2014 Posted May 15, 2014 Please understand what follows is an opinion and I recognize it as such. The moniker "rolling offset" with regard to piping is heard constantly and confuses many- not necessary to deal with. From geometry, the axiom "two parallel lines determine a plane." Using AutoCAD, pick the 3-point ucs icon, snap to the centerline of first pipe, snap to other end of that pipe, then snap to centerline of second pipe. UCS is then set to the plane determine by pipe centerlines. Type "plan"-now you're looking straight down on the two pipes and can lay out the connection using whatever degree ells you want. The problem of connecting two pipes can also be solved with trig by using the arc-tangent of the amount of offset and the centerline radius of the offset be determined, also. The problem with this method is that the radius point of pipe ells is fixed and probably want coincide with the available ells. With round sheet metal ducts, though, trig works like a champ. Quote
JD Mather Posted May 15, 2014 Posted May 15, 2014 Please understand what follows is an opinion ..... I would caution not underestimate the true complexity of this problem. Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.