0 2 2 1 0 7 1c961c8b-9745-4a92-a43d-080de1ead765 Shaded 1 100;150;0;0 100;0;150;0 635273898765795129 elastica_curve_examples - Copy.ghx 0 -102 40 1 0 0 149 c552a431-af5b-46a9-a8a4-0fcbc27ef596 Group 1 150;170;135;255 A group of Grasshopper objects d013dc08-a8cd-4383-aa4a-7e9f0b202f67 19d4e5e6-a3fb-4e4d-b426-93c0b41f974c ce2f14ec-483c-4899-a8cb-784a62168957 b2a67d0f-c66e-46a9-8efd-f7442d233d5d 32bb1a9f-9575-4b8c-8a60-a65a7b9dd15f 98102773-859e-4cf3-83a5-41f68379af66 d68f5884-1ed1-4bd5-ab64-b7040370d59b 8cd6ad76-7f71-4948-8c5e-9a3e2549985f d53a1087-053a-44d5-b485-68a8b5d09ce4 cc8dfb80-5022-4b13-83c9-a787888900e8 072c5f2f-5efd-4587-8eb9-f4eacb6f59a9 25d0b3b4-fc42-4433-a4bf-e70bfa828143 5137ef09-783f-4981-a9ec-aa4f2fc8e019 225afdb2-480f-435b-a1cb-84170b3afd2f 20228f31-e357-4d65-8747-46e12348391c 7c2a1ac2-4916-4aa3-9b0a-566a67f36e60 95f9fd7f-37dc-4bd8-8105-7301ef052bdd 17 07a70634-4e1a-4226-b5d3-17b0a4e0f460 Group 079bd9bd-54a0-41d4-98af-db999015f63d VB Script Private Function IsSet(ByVal param As String) As Boolean ' Check if an input parameter has data Dim i As Integer = Component.Params.IndexOfInputParam(param) If i > -1 Then Return Component.Params.Input.ElementAt(i).DataType > 1 ' input parameter DataType of 1 means it's not receiving input (internal or external) Else Msg("error", "Input parameter '" & param & "' not found") Return False End If End Function Private Sub Msg(ByVal type As String, ByVal msg As String) ' Output an error, warning, or informational message Select Case type Case "error" Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, msg) Print("Error: " & msg) Case "warning" Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, msg) Print("Warning: " & msg) Case "info" Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, msg) Print(msg) End Select End Sub ' Solve for the m parameter from length and width (reference {1} equation (34), except b = width and K(k) and E(k) should be K(m) and E(m)) Private Function SolveMFromLenWid(ByVal L As Double, ByVal w As Double) As Double If w = 0 Then Return Defined.M_ZERO_W ' for the boundry condition width = 0, bypass the function and return the known m value End If Dim n As Integer = 1 ' Iteration counter (quit if >MAXIT) Dim lower As Double = 0 ' m must be within this range Dim upper As Double = 1 Dim m As Double Dim cwl As Double Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 cwl = 2 * EllipticE(m) / EllipticK(m) - 1 ' calculate w/L with the test value of m If cwl < w / L Then ' compares the calculated w/L with the actual w/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop Return m End Function ' Solve for the m parameter from length and height (reference {1} equation (33), except K(k) should be K(m) and k = sqrt(m)) ' Note that it's actually possible to find 2 valid values for m (hence 2 width values) at certain height values Private Function SolveMFromLenHt(ByVal L As Double, ByVal h As Double) As List(Of Double) Dim n As Integer = 1 ' Iteration counter (quit if >MAXIT) Dim lower As Double = 0 ' m must be within this range Dim upper As Double = 1 Dim twoWidths As Boolean = h / L >= Defined.DOUBLE_W_HL_RATIO And h / L < Defined.MAX_HL_RATIO ' check to see if h/L is within the range where 2 solutions for the width are possible Dim m As Double Dim mult_m As New List(Of Double) Dim chl As Double If twoWidths Then ' find the first of two possible solutions for m with the following limits: lower = Defined.M_DOUBLE_W ' see constants at bottom of script upper = Defined.M_MAXHEIGHT ' see constants at bottom of script Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 chl = Math.Sqrt(m) / EllipticK(m) ' calculate h/L with the test value of m If chl > h / L Then ' compares the calculated h/L with the actual h/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop mult_m.Add(m) ' then find the second of two possible solutions for m with the following limits: lower = Defined.M_MAXHEIGHT ' see constants at bottom of script upper = 1 Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 chl = Math.Sqrt(m) / EllipticK(m) ' calculate h/L with the test value of m If chl < h / L Then ' compares the calculated h/L with the actual h/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop If m <= Defined.M_MAX Then ' return this m parameter only if it falls within the maximum useful value (above which the curve breaks down) mult_m.Add(m) End If Else ' find the one possible solution for the m parameter upper = Defined.M_DOUBLE_W ' limit the upper end of the search to the maximum value of m for which only one solution exists Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 chl = Math.Sqrt(m) / EllipticK(m) ' calculate h/L with the test value of m If chl > h / L Then ' compares the calculated h/L with the actual h/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop mult_m.Add(m) End If Return mult_m End Function ' Solve for the m parameter from width and height (derived from reference {1} equations (33) and (34) with same notes as above) Private Function SolveMFromWidHt(ByVal w As Double, ByVal h As Double) As Double Dim n As Integer = 1 ' Iteration counter (quit if >MAXIT) Dim lower As Double = 0 ' m must be within this range Dim upper As Double = 1 Dim m As Double Dim cwh As Double Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 cwh = (2 * EllipticE(m) - EllipticK(m)) / Math.Sqrt(m) ' calculate w/h with the test value of m If cwh < w / h Then ' compares the calculated w/h with the actual w/h then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop Return m End Function ' Calculate length based on height and an m parameter, derived from reference {1} equation (33), except K(k) should be K(m) and k = sqrt(m) Private Function Cal_L(ByVal h As Double, ByVal m As Double) As Double Return h * EllipticK(m) / Math.Sqrt(m) End Function ' Calculate width based on length and an m parameter, derived from reference {1} equation (34), except b = width and K(k) and E(k) should be K(m) and E(m) Private Function Cal_W(ByVal L As Double, ByVal m As Double) As Double Return L * (2 * EllipticE(m) / EllipticK(m) - 1) End Function ' Calculate height based on length and an m parameter, from reference {1} equation (33), except K(k) should be K(m) and k = sqrt(m) Private Function Cal_H(ByVal L As Double, ByVal m As Double) As Double Return L * Math.Sqrt(m) / EllipticK(m) End Function ' Calculate the unique m parameter based on a start tangent angle, from reference {2}, just above equation (9a), that states k = Sin(angle / 2 + Pi / 4), ' but as m = k^2 and due to this script's need for an angle rotated 90° versus the one in reference {1}, the following formula is the result ' New note: verified by reference {4}, pg. 78 at the bottom Private Function Cal_M(ByVal a As Double) As Double Return (1 - Math.Cos(a)) / 2 ' equal to Sin^2(a/2) too End Function ' Calculate start tangent angle based on an m parameter, derived from above formula Private Function Cal_A(ByVal m As Double) As Double Return Math.Acos(1 - 2 * m) End Function ' This is the heart of this script, taking the found (or specified) length, width, and angle values along with the found m parameter to create ' a list of points that approximate the shape or form of the elastica. It works by finding the x and y coordinates (which are reversed versus ' the original equations (12a) and (12b) from reference {2} due to the 90° difference in orientation) based on the tangent angle along the curve. ' See reference {2} for more details on how they derived it. Note that to simplify things, the algorithm only calculates the points for half of the ' curve, then mirrors those points along the y-axis. Private Function FindBendForm(ByVal L As Double, ByVal w As Double, ByVal m As Double, ByVal ang As Double, ByVal refPln As Plane) As List(Of Point3d) L = L / 2 ' because the below algorithm is based on the formulas in reference {2} for only half of the curve w = w / 2 ' same If ang = 0 Then ' if angle (and height) = 0, then simply return the start and end points of the straight line Dim out As New List(Of Point3d) out.Add(refPln.PointAt(w, 0, 0)) out.Add(refPln.PointAt(-w, 0, 0)) Return out End If Dim x As Double Dim y As Double Dim halfCurvePts As New List(Of Point3d) Dim fullCurvePts As New List(Of Point3d) Dim translatedPts As New List(Of Point3d) ang -= Math.PI / 2 ' a hack to allow this algorithm to work, since the original curve in paper {2} was rotated 90° Dim angB As Double = ang + (-Math.PI / 2 - ang) / Defined.CURVEDIVS ' angB is the 'lowercase theta' which should be in formula {2}(12b) as the interval ' start [a typo...see equation(3)]. It's necessary to start angB at ang + [interval] instead of just ang due to integration failing at angB = ang halfCurvePts.Add(New Point3d(w, 0, 0)) ' start with this known initial point, as integration will fail when angB = ang ' each point {x, y} is calculated from the tangent angle, angB, that occurs at each point (which is why this iterates from ~ang to -pi/2, the known end condition) Do While Math.Round(angB, Defined.ROUNDTO) >= Math.Round(-Math.PI / 2, Defined.ROUNDTO) y = (Math.Sqrt(2) * Math.Sqrt(Math.Sin(ang) - Math.Sin(angB)) * (w + L)) / (2 * EllipticE(m)) ' note that x and y are swapped vs. (12a) and (12b) x = (L / (Math.Sqrt(2) * EllipticK(m))) * Simpson(angB, -Math.PI / 2, 500, ang) ' calculate the Simpson approximation of the integral (function f below) ' over the interval angB ('lowercase theta') to -pi/2. side note: is 500 too few iterations for the Simson algorithm? If Math.Round(x, Defined.ROUNDTO) = 0 Then x = 0 halfCurvePts.Add(New Point3d(x, y, 0)) angB += (-Math.PI / 2 - ang) / Defined.CURVEDIVS ' onto the next tangent angle Loop ' After finding the x and y values for half of the curve, add the {-x, y} values for the rest of the curve For Each point As Point3d In halfCurvePts If Math.Round(point.X, Defined.ROUNDTO) = 0 Then If Math.Round(point.Y, Defined.ROUNDTO) = 0 Then fullCurvePts.Add(New Point3d(0, 0, 0)) ' special case when width = 0: when x = 0, only duplicate the point when y = 0 too End If Else fullCurvePts.Add(New Point3d(-point.X, point.Y, 0)) End If Next halfCurvePts.Reverse fullCurvePts.AddRange(halfCurvePts) For Each p As Point3d In fullCurvePts translatedPts.Add(refPln.PointAt(p.X, p.Y, p.Z)) ' translate the points from the reference plane to the world plane Next Return translatedPts End Function ' Interpolates the points from FindBendForm to create the Elastica curve. Uses start & end tangents for greater accuracy. Private Function MakeCurve(ByVal pts As List(Of Point3d), ByVal ang As Double, ByVal refPln As Plane) As Curve If ang <> 0 Then Dim ts, te As New Vector3d(refPln.XAxis) ts.Rotate(ang, refPln.ZAxis) te.Rotate(-ang, refPln.ZAxis) Return Curve.CreateInterpolatedCurve(pts, 3, CurveKnotStyle.Chord, ts, te) ' 3rd degree curve with 'Chord' Knot Style Else Return Curve.CreateInterpolatedCurve(pts, 3) ' if angle (and height) = 0, then simply interpolate the straight line (no start/end tangents) End If End Function ' Implements the Simpson approximation for an integral of function f below Public Function Simpson(a As Double, b As Double, n As Integer, theta As Double) As Double 'n should be an even number Dim j As Integer, s1 As Double, s2 As Double, h As Double h = (b - a) / n s1 = 0 s2 = 0 For j = 1 To n - 1 Step 2 s1 = s1 + fn(a + j * h, theta) Next j For j = 2 To n - 2 Step 2 s2 = s2 + fn(a + j * h, theta) Next j Simpson = h / 3 * (fn(a, theta) + 4 * s1 + 2 * s2 + fn(b, theta)) End Function ' Specific calculation for the above integration Public Function fn(x As Double, theta As Double) As Double fn = Math.Sin(x) / (Math.Sqrt(Math.Sin(theta) - Math.Sin(x))) ' from reference {2} formula (12b) End Function ' Return the Complete Elliptic integral of the 1st kind ' Abramowitz and Stegun p.591, formula 17.3.11 ' Code from http://www.codeproject.com/Articles/566614/Elliptic-integrals Public Function EllipticK(ByVal m As Double) As Double Dim sum, term, above, below As Double sum = 1 term = 1 above = 1 below = 2 For i As Integer = 1 To 100 term *= above / below sum += Math.Pow(m, i) * Math.Pow(term, 2) above += 2 below += 2 Next sum *= 0.5 * Math.PI Return sum End Function ' Return the Complete Elliptic integral of the 2nd kind ' Abramowitz and Stegun p.591, formula 17.3.12 ' Code from http://www.codeproject.com/Articles/566614/Elliptic-integrals Public Function EllipticE(ByVal m As Double) As Double Dim sum, term, above, below As Double sum = 1 term = 1 above = 1 below = 2 For i As Integer = 1 To 100 term *= above / below sum -= Math.Pow(m, i) * Math.Pow(term, 2) / above above += 2 below += 2 Next sum *= 0.5 * Math.PI Return sum End Function Friend Partial NotInheritable Class Defined Private Sub New() End Sub ' Note: most of these values for m and h/L ratio were found with Wolfram Alpha and either specific intercepts (x=0) or local minima/maxima. They should be constant. Public Const M_SKETCHY As Double = 0.95 ' value of the m parameter where the curvature near the ends of the curve gets wonky Public Const M_MAX As Double = 0.993 ' maximum useful value of the m parameter, above which this algorithm for the form of the curve breaks down Public Const M_ZERO_W As Double = 0.826114765984970336 ' value of the m parameter when width = 0 Public Const M_MAXHEIGHT As Double = 0.701327460663101223 ' value of the m parameter at maximum possible height of the bent rod/wire Public Const M_DOUBLE_W As Double = 0.180254422335013983 ' minimum value of the m parameter when two width values are possible for a given height and length Public Const DOUBLE_W_HL_RATIO As Double = 0.257342117984635757 ' value of the height/length ratio above which there are two possible width values Public Const MAX_HL_RATIO As Double = 0.403140189705650243 ' maximum possible value of the height/length ratio Public Const MAXERR As Double = 0.0000000001 ' error tolerance Public Const MAXIT As Integer = 100 ' maximum number of iterations Public Const ROUNDTO As Integer = 10 ' number of decimal places to round off to Public Const CURVEDIVS As Integer = 50 ' number of sample points for building the curve (or half-curve as it were) End Class A VB.NET scriptable component 98 86 true d013dc08-a8cd-4383-aa4a-7e9f0b202f67 VB Script VB true 0 ' ----------------------------------------------------------------- ' Elastic Bending Script by Will McElwain ' Created February 2014 ' ' DESCRIPTION: ' This beast creates the so-called 'elastica curve', the shape a long, thin rod or wire makes when it is bent elastically (i.e. not permanently). In this case, force ' is assumed to only be applied horizontally (which would be in line with the rod at rest) and both ends are assumed to be pinned or hinged meaning they are free ' to rotate (as opposed to clamped, when the end tangent angle is fixed, usually horizontally). An interesting finding is that it doesn't matter what the material or ' cross-sectional area is, as long as they're uniform along the entire length. Everything makes the same shape when bent as long as it doesn't cross the threshold ' from elastic to plastic (permanent) deformation (I don't bother to find that limit here, but can be found if the yield stress for a material is known). ' ' Key to the formulas used in this script are elliptic integrals, specifically K(m), the complete elliptic integral of the first kind, and E(m), the complete elliptic ' integral of the second kind. There was a lot of confusion over the 'm' and 'k' parameters for these functions, as some people use them interchangeably, but they are ' not the same. m = k^2 (thus k = Sqrt(m)). I try to use the 'm' parameter exclusively to avoid this confusion. Note that there is a unique 'm' parameter for every ' configuration/shape of the elastica curve. ' ' This script tries to find that unique 'm' parameter based on the inputs. The algorithm starts with a test version of m, evaluates an expression, say 2*E(m)/K(m)-1, ' then compares the result to what it should be (in this case, a known width/length ratio). Iterate until the correct m is found. Once we have m, we can then calculate ' all of the other unknowns, then find points that lie on that curve, then interpolate those points for the actual curve. You can also use Wolfram|Alpha as I did to ' find the m parameter based on the equations in this script (example here: http://tiny.cc/t4tpbx for when say width=45.2 and length=67.1). ' ' Other notes: ' * This script works with negative values for width, which will creat a self-intersecting curve (as it should). The curvature of the elastica starts to break down around ' m=0.95 (~154°), but this script will continue to work until M_MAX, m=0.993 (~169°). If you wish to ignore self-intersecting curves, set ignoreSelfIntersecting to True ' * When the only known values are length and height, it is actually possible for certain ratios of height to length to have two valid m values (thus 2 possible widths ' and angles). This script will return them both. ' * Only the first two valid parameters (of the required ones) will be used, meaning if all four are connected (length, width or a PtB, height, and angle), this script will ' only use length and width (or a PtB). ' * Depending on the magnitude of your inputs (say if they're really small, like if length < 10), you might have to increase the constant ROUNDTO at the bottom ' ' REFERENCES: ' {1} "The elastic rod" by M.E. Pacheco Q. & E. Pina, http://www.scielo.org.mx/pdf/rmfe/v53n2/v53n2a8.pdf ' {2} "An experiment in nonlinear beam theory" by A. Valiente, http://www.deepdyve.com/lp/doc/I3lwnxdfGz , also here: http://tiny.cc/Valiente_AEiNBT ' {3} "Snap buckling, writhing and Loop formation In twisted rods" by V.G.A. GOSS, http://myweb.lsbu.ac.uk/~gossga/thesisFinal.pdf ' {4} "Theory of Elastic Stability" by Stephen Timoshenko, http://www.scribd.com/doc/50402462/Timoshenko-Theory-of-Elastic-Stability (start on p. 76) ' ' INPUT: ' PtA - First anchor point (required) ' PtB - Second anchor point (optional, though 2 out of the 4--length, width, height, angle--need to be specified) ' [note that PtB can be the same as PtA (meaning width would be zero)] ' [also note that if a different width is additionally specified that's not equal to the distance between PtA and PtB, then the end point will not equal PtB anymore] ' Pln - Plane of the bent rod/wire, which bends up in the +y direction. The line between PtA and PtB (if specified) must be parallel to the x-axis of this plane ' ' ** 2 of the following 4 need to be specified ** ' Len - Length of the rod/wire, which needs to be > 0 ' Wid - Width between the endpoints of the curve [note: if PtB is specified in addition, and distance between PtA and PtB <> width, the end point will be relocated ' Ht - Height of the bent rod/wire (when negative, curve will bend downward, relative to the input plane, instead) ' Ang - Inner departure angle or tangent angle (in radians) at the ends of the bent rod/wire. Set up so as width approaches length (thus height approaches zero), angle approaches zero ' ' * Following variables only needed for optional calculating of bending force, not for shape of curve. ' E - Young's modulus (modulus of elasticity) in GPa (=N/m^2) (material-specific. for example, 7075 aluminum is roughly 71.7 GPa) ' I - Second moment of area (or area moment of inertia) in m^4 (cross-section-specific. for example, a hollow rod ' would have I = pi * (outer_diameter^4 - inner_diameter^4) / 32 ' Note: E*I is also known as flexural rigidity or bending stiffness ' ' OUTPUT: ' out - only for debugging messages ' Pts - the list of points that approximate the shape of the elastica ' Crv - the 3rd-degree curve interpolated from those points (with accurate start & end tangents) ' L - the length of the rod/wire ' W - the distance (width) between the endpoints of the rod/wire ' H - the height of the bent rod/wire ' A - the tangent angle at the (start) end of the rod/wire ' F - the force needed to hold the rod/wire in a specific shape (based on the material properties & cross-section) **be sure your units for 'I' match your units for the ' rest of your inputs (length, width, etc.). Also note that the critical buckling load (force) that makes the rod/wire start to bend can be found at height=0 ' ' THANKS TO: ' Mårten Nettelbladt (thegeometryofbending.blogspot.com) ' Daniel Piker (Kangaroo plugin) ' David Rutten (Grasshopper guru) ' Euler & Bernoulli (the O.G.'s) ' ' ----------------------------------------------------------------- Dim ignoreSelfIntersecting As Boolean = False ' set to True if you don't want to output curves where width < 0, which creates a self-intersecting curve Dim inCt As Integer = 0 ' count the number of required parameters that are receiving data Dim length As Double Dim width As System.Object = Nothing ' need to set as Nothing so we can check if it has been assigned a value later Dim height As Double Dim angle As Double Dim m As Double Dim multiple_m As New List(Of Double) Dim AtoB As Line Dim flip_H As Boolean = False ' if height is negative, this flag will be set Dim flip_A As Boolean = False ' if angle is negative, this flag will be set If Not IsSet("Pln") Then Msg("error", "Base plane is not set") Return End If If Not IsSet("PtA") Then Msg("error", "Point A is not set") Return End If If Math.Round(Pln.DistanceTo(PtA), Defined.ROUNDTO) <> 0 Then Msg("error", "Point A is not on the base plane") Return End If Dim refPlane As Plane = Pln ' create a reference plane = input plane and set the origin of it to PtA in case PtA isn't the origin already refPlane.Origin = PtA If IsSet("PtB") Then If Math.Round(Pln.DistanceTo(PtB), Defined.ROUNDTO) <> 0 Then Msg("error", "Point B is not on the base plane") Return End If AtoB = New Line(PtA, PtB) If AtoB.Length <> 0 And Not AtoB.Direction.IsPerpendicularTo(Pln.YAxis) Then Msg("error", "The line between PtA and PtB is not perpendicular to the Y-axis of the specified plane") Return End If inCt += 1 If IsSet("Wid") Then Msg("info", "Wid will override the distance between PtA and PtB. If you do not want this to happen, disconnect PtB or Wid.") width = PtA.DistanceTo(PtB) ' get the width (distance) between PtA and PtB Dim refPtB As Point3d refPlane.RemapToPlaneSpace(PtB, refPtB) If refPtB.X < 0 Then width = -width ' check if PtB is to the left of PtA...if so, width is negative End If If IsSet("Len") Then inCt += 1 If IsSet("Wid") Then inCt += 1 If IsSet("Ht") Then inCt += 1 If IsSet("Ang") Then inCt += 1 If inCt > 2 Then Msg("info", "More parameters set than are required (out of length, width, height, angle). Only using the first two valid ones.") ' check for connected/specified inputs. note: only the first two that it comes across will be used If IsSet("Len") Then ' if length is specified then... If Len <= 0 Then Msg("error", "Length cannot be negative or zero") Return End If If IsSet("Wid") Then ' find height & angle based on length and specified width If Wid > Len Then Msg("error", "Width is greater than length") Return End If If Wid = Len Then ' skip the solver and set the known values height = 0 m = 0 angle = 0 width = Wid Else m = SolveMFromLenWid(Len, Wid) height = Cal_H(Len, m) ' L * Sqrt(m) / K(m) angle = Cal_A(m) ' Acos(1 - 2 * m) width = Wid End If Else If width IsNot Nothing Then ' find height & angle based on length and calculated width (distance between PtA and PtB) If width > Len Then Msg("error", "Width is greater than length") Return End If If width = Len Then ' skip the solver and set the known values height = 0 m = 0 angle = 0 Else m = SolveMFromLenWid(Len, width) height = Cal_H(Len, m) ' L * Sqrt(m) / K(m) angle = Cal_A(m) ' Acos(1 - 2 * m) End If Else If IsSet("Ht") Then ' find width & angle based on length and height ** possible to return 2 results ** If Math.Abs(Ht / Len) > Defined.MAX_HL_RATIO Then Msg("error", "Height not possible with given length") Return End If If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If If Ht = 0 Then ' skip the solver and set the known values width = Len angle = 0 Else multiple_m = SolveMFromLenHt(Len, Ht) ' note that it's possible for two values of m to be found if height is close to max height If multiple_m.Count = 1 Then ' if there's only one m value returned, calculate the width & angle here. we'll deal with multiple m values later m = multiple_m.Item(0) width = Cal_W(Len, m) ' L * (2 * E(m) / K(m) - 1) angle = Cal_A(m) ' Acos(1 - 2 * m) End If End If height = Ht Else If IsSet("Ang") Then ' find width & height based on length and angle If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then ' skip the solver and set the known values width = Len height = 0 Else width = Cal_W(Len, m) ' L * (2 * E(m) / K(m) - 1) height = Cal_H(Len, m) ' L * Sqrt(m) / K(m) End If angle = Ang Else Msg("error", "Need to specify one more parameter in addition to length") Return End If length = Len Else If IsSet("Wid") Then ' if width is specified then... If IsSet("Ht") Then ' find length & angle based on specified width and height If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If If Ht = 0 Then ' skip the solver and set the known values length = Wid angle = 0 Else m = SolveMFromWidHt(Wid, Ht) length = Cal_L(Ht, m) ' h * K(m) / Sqrt(m) angle = Cal_A(m) ' Acos(1 - 2 * m) End If height = Ht Else If IsSet("Ang") Then ' find length & height based on specified width and angle If Wid = 0 Then Msg("error", "Curve not possible with width = 0 and an angle as inputs") Return End If If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then ' skip the solver and set the known values length = Wid height = 0 Else length = Wid / (2 * EllipticE(m) / EllipticK(m) - 1) If length < 0 Then Msg("error", "Curve not possible at specified width and angle (calculated length is negative)") Return End If height = Cal_H(length, m) ' L * Sqrt(m) / K(m) End If angle = Ang Else Msg("error", "Need to specify one more parameter in addition to width (Wid)") Return End If width = Wid Else If width IsNot Nothing Then ' if width is determined by PtA and PtB then... If IsSet("Ht") Then ' find length & angle based on calculated width and height If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If If Ht = 0 Then ' skip the solver and set the known values length = width angle = 0 Else m = SolveMFromWidHt(width, Ht) length = Cal_L(Ht, m) ' h * K(m) / Sqrt(m) angle = Cal_A(m) ' Acos(1 - 2 * m) End If height = Ht Else If IsSet("Ang") Then ' find length & height based on calculated width and angle If width = 0 Then Msg("error", "Curve not possible with width = 0 and an angle as inputs") Return End If If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then ' skip the solver and set the known values length = width height = 0 Else length = width / (2 * EllipticE(m) / EllipticK(m) - 1) If length < 0 Then Msg("error", "Curve not possible at specified width and angle (calculated length is negative)") Return End If height = Cal_H(length, m) ' L * Sqrt(m) / K(m) End If angle = Ang Else Msg("error", "Need to specify one more parameter in addition to PtA and PtB") Return End If Else If IsSet("Ht") Then ' if height is specified then... If IsSet("Ang") Then ' find length & width based on height and angle If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_H = True flip_A = True End If If Ht = 0 Then Msg("error", "Height can't = 0 if only height and angle are specified") Return Else If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = Not flip_A flip_H = Not flip_H End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then Msg("error", "Angle can't = 0 if only height and angle are specified") Return Else length = Cal_L(Ht, m) ' h * K(m) / Sqrt(m) width = Cal_W(length, m) ' L * (2 * E(m) / K(m) - 1) End If angle = Ang End If height = Ht Else Msg("error", "Need to specify one more parameter in addition to height") Return End If Else If IsSet("Ang") Then Msg("error", "Need to specify one more parameter in addition to angle") Return Else Msg("error", "Need to specify two of the four parameters: length, width (or PtB), height, and angle") Return End If If m > Defined.M_MAX Then Msg("error", "Form of curve not solvable with current algorithm and given inputs") Return End If refPlane.Origin = refPlane.PointAt(width / 2, 0, 0) ' adjust the origin of the reference plane so that the curve is centered about the y-axis (start of the curve is at x = -width/2) If multiple_m.Count > 1 Then ' if there is more than one m value returned, calculate the width, angle, and curve for each Dim multi_pts As New DataTree(Of Point3d) Dim multi_crv As New List(Of Curve) Dim tmp_pts As New List(Of Point3d) Dim multi_W, multi_A, multi_F As New List(Of Double) Dim j As Integer = 0 ' used for creating a new branch (GH_Path) for storing pts which is itself a list of points For Each m_val As Double In multiple_m width = Cal_W(length, m_val) 'length * (2 * EllipticE(m_val) / EllipticK(m_val) - 1) If width < 0 And ignoreSelfIntersecting Then Msg("warning", "One curve is self-intersecting. To enable these, set ignoreSelfIntersecting to False") Continue For End If If m_val >= Defined.M_SKETCHY Then Msg("info", "Accuracy of the curve whose width = " & Math.Round(width, 4) & " is not guaranteed") angle = Cal_A(m_val) 'Math.Asin(2 * m_val - 1) refPlane.Origin = refPlane.PointAt(width / 2, 0, 0) ' adjust the origin of the reference plane so that the curve is centered about the y-axis (start of the curve is at x = -width/2) tmp_pts = FindBendForm(length, width, m_val, angle, refPlane) multi_pts.AddRange(tmp_pts, New GH_Path(j)) multi_crv.Add(MakeCurve(tmp_pts, angle, refPlane)) multi_W.Add(width) If flip_A Then angle = -angle multi_A.Add(angle) E = E * 10 ^ 9 ' Young's modulus input E is in GPa, so we convert to Pa here (= N/m^2) multi_F.Add(EllipticK(m_val) ^ 2 * E * I / length ^ 2) ' from reference {4} pg. 79 j += 1 refPlane.Origin = PtA ' reset the reference plane origin to PtA for the next m_val 'Print("length=" & length & ", width=" & width & ", height=" & height & ", angle=" & angle & ", m=" & m_val & ", k=" & Math.Sqrt(m_val) & ", w/L=" & width / length & ", h/L=" & height / length & ", w/h=" & width / height) Next ' assign the outputs Pts = multi_pts Crv = multi_crv L = length W = multi_W If flip_H Then height = -height H = height A = multi_A F = multi_F Else ' only deal with the single m value If m >= Defined.M_SKETCHY Then Msg("info", "Accuracy of the curve at these parameters is not guaranteed") If width < 0 And ignoreSelfIntersecting Then Msg("error", "Curve is self-intersecting. To enable these, set ignoreSelfIntersecting to False") Return End If Pts = FindBendForm(length, width, m, angle, refPlane) Crv = MakeCurve(pts, angle, refPlane) L = length W = width If flip_H Then height = -height H = height If flip_A Then angle = -angle A = angle E = E * 10 ^ 9 ' Young's modulus input E is in GPa, so we convert to Pa here (= N/m^2) F = EllipticK(m) ^ 2 * E * I / length ^ 2 ' from reference {4} pg. 79. Note: the critical buckling (that makes the rod/wire start to bend) can be found at height=0 (width=length) 'height = Math.Sqrt(((2 * Len / 5) ^ 2 - ((Wid - Len / 5) / 2) ^ 2) ' quick approximation discovered by Mårten of 'Geometry of Bending' fame ( http://tiny.cc/it2pbx ) 'width = (Len +/- 2 * Math.Sqrt(4 * Len ^ 2 - 25 * Ht ^ 2)) / 5 ' derived from above 'length = (2 * Math.Sqrt(15 * Ht ^ 2 + 4 * Wid ^ 2) - Wid) / 3 ' derived from above 'Print("length=" & length & ", width=" & width & ", height=" & height & ", angle=" & angle & ", m=" & m & ", k=" & Math.Sqrt(m) & ", w/L=" & width / length & ", h/L=" & height / length & ", w/h=" & width / height) End If 612 233 84 184 654 325 9 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 8 3ede854e-c753-40eb-84cb-b48008f14fd4 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 true Script Variable PtA 920df659-6d29-453d-9295-577245828ba6 PtA PtA true 0 true 7451bc70-5fc3-43a3-bb48-ff10952414e7 1 e1937b56-b1da-4c12-8bd8-e34ee81746ef 614 235 25 20 628 245 true Script Variable PtB eeb8ccaa-8966-4eab-8949-3eb384a12d84 PtB PtB true 0 true d5104343-e872-4369-9a14-a75a852c1a15 1 e1937b56-b1da-4c12-8bd8-e34ee81746ef 614 255 25 20 628 265 true Script Variable Pln ee4f4d3f-f195-437b-88af-35d3a73d66ad Pln Pln true 0 true df7d1e6a-049f-4594-9fb2-7dda33d26e57 1 3897522d-58e9-4d60-b38c-978ddacfedd8 614 275 25 20 628 285 true Script Variable Len 999531d8-8fc7-4421-8afb-076eb4ce3f6e Len Len true 0 true ce2f14ec-483c-4899-a8cb-784a62168957 1 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 614 295 25 20 628 305 true Script Variable Wid 8aba3acb-d87c-46a0-aef3-179156140406 Wid Wid true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 614 315 25 20 628 325 true Script Variable Ht 54082c0a-ad9c-49e6-97c2-34b9d8c0e605 Ht Ht true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 614 335 25 20 628 345 true Script Variable Ang 998111e9-4c7d-4b27-88a9-01982081691a Ang Ang true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 614 355 25 20 628 365 true Script Variable E b0ed1e4b-bad1-4910-9cd3-3178fc1a708a E E true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 614 375 25 20 628 385 true Script Variable I 8058a335-90ef-4152-920c-88e68de69acc I I true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 614 395 25 20 628 405 1 Print, Reflect and Error streams 17655ef3-6a9d-43b9-a699-bb23d2e2baff out out false 0 669 235 25 22 681.5 246.25 Output parameter Pts d4f9a77d-b3a2-46c6-91de-6ce275666f2d Pts Pts false 0 669 257 25 23 681.5 268.75 Output parameter Crv 1b8d948b-eedb-4c3c-bfba-ceaee74ff110 Crv Crv false 0 669 280 25 22 681.5 291.25 Output parameter L 0d6fe24b-f96e-4e86-bad3-d29ae034394e L L false 0 669 302 25 23 681.5 313.75 Output parameter W 816f49f4-39a6-4705-80c7-e2a924ac1e0c W W false 0 669 325 25 22 681.5 336.25 Output parameter H b00909dc-b385-4e3e-a3a8-9e76efdaadeb H H false 0 669 347 25 23 681.5 358.75 Output parameter A 9632d9b7-ad5c-4b42-bc41-5bf9a4af0115 A A false 0 669 370 25 22 681.5 381.25 Output parameter F 28c91c87-29e8-4bc8-a5ff-18aadc6f0ecd F F false 0 669 392 25 23 681.5 403.75 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values 19d4e5e6-a3fb-4e4d-b426-93c0b41f974c Number Slider width false 0 158 312 384 20 158.3465 312.3785 2 1 0 400 -130 0 183.21 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values ce2f14ec-483c-4899-a8cb-784a62168957 Number Slider length false 0 158 285 385 20 158.0028 285.5286 2 1 0 400 0 0 300 fbac3e32-f100-4292-8692-77240a42fd1a Point Contains a collection of three-dimensional points true b2a67d0f-c66e-46a9-8efd-f7442d233d5d Point Pt false d4f9a77d-b3a2-46c6-91de-6ce275666f2d 1 782 194 50 24 807.4574 206.2478 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 32bb1a9f-9575-4b8c-8a60-a65a7b9dd15f Panel false 0 2296b093-286c-438d-aa59-465d23147f1c 1 Double click to edit panel content… 855 408 105 55 0 0 0 855.6731 408.6088 255;255;250;90 true true true false false true 0d77c51e-584f-44e8-aed2-c2ddf4803888 Degrees Convert an angle specified in radians to degrees 98102773-859e-4cf3-83a5-41f68379af66 Degrees Deg 754 421 64 28 784 435 Angle in radians f013de98-8461-42d6-94e2-d4f473814c3f Radians R false 9632d9b7-ad5c-4b42-bc41-5bf9a4af0115 1 756 423 13 24 764 435 Angle in degrees 2296b093-286c-438d-aa59-465d23147f1c Degrees D false 0 799 423 17 24 807.5 435 3581f42a-9592-4549-bd6b-1c0fc39d067b Construct Point Construct a point from {xyz} coordinates. d68f5884-1ed1-4bd5-ab64-b7040370d59b Construct Point Pt 392 101 67 64 423 133 {x} coordinate b9e9716b-aaed-4e63-90f0-fd69bffec388 X coordinate X false 0 394 103 14 20 402.5 113 1 1 {0} 0 {y} coordinate 961e58c0-1cd5-49a0-8fd1-0e419a2c0b34 Y coordinate Y false 0 394 123 14 20 402.5 133 1 1 {0} 0 {z} coordinate 54bdcf81-cd9a-447c-bad3-a55fe7ef5dc1 Z coordinate Z false 0 394 143 14 20 402.5 153 1 1 {0} 0 Point coordinate 7451bc70-5fc3-43a3-bb48-ff10952414e7 Point Pt false 0 438 103 19 60 447.5 133 3581f42a-9592-4549-bd6b-1c0fc39d067b Construct Point Construct a point from {xyz} coordinates. 8cd6ad76-7f71-4948-8c5e-9a3e2549985f Construct Point Pt 392 173 67 64 423 205 {x} coordinate cc48fd1f-8953-40bd-a5f6-a9a203bcabd4 X coordinate X false 95f9fd7f-37dc-4bd8-8105-7301ef052bdd 1 394 175 14 20 402.5 185 1 1 {0} 80 {y} coordinate a1e74152-bea6-4daf-a4cf-bbaa027d9769 Y coordinate Y false 0 394 195 14 20 402.5 205 1 1 {0} 0 {z} coordinate e34fa320-f5a3-4aaf-beaf-4207678e6e88 Z coordinate Z false 0 394 215 14 20 402.5 225 1 1 {0} 0 Point coordinate d5104343-e872-4369-9a14-a75a852c1a15 Point Pt false 0 438 175 19 60 447.5 205 d5967b9f-e8ee-436b-a8ad-29fdcecf32d5 Curve Contains a collection of generic curves d53a1087-053a-44d5-b485-68a8b5d09ce4 Curve Crv false 1b8d948b-eedb-4c3c-bfba-ceaee74ff110 1 782 236 50 24 807.4463 248.7776 17b7152b-d30d-4d50-b9ef-c9fe25576fc2 XY Plane World XY plane. true cc8dfb80-5022-4b13-83c9-a787888900e8 XY Plane XY 474 246 64 28 505 260 Origin of plane 36223dd6-39d8-43d1-85f3-16523a2c0069 Origin O false 0 476 248 14 24 484.5 260 1 1 {0} 0 0 0 World XY plane df7d1e6a-049f-4594-9fb2-7dda33d26e57 Plane P false 0 520 248 16 24 528 260 a4cd2751-414d-42ec-8916-476ebf62d7fe Radians Convert an angle specified in degrees to radians 072c5f2f-5efd-4587-8eb9-f4eacb6f59a9 Radians Rad 478 365 64 28 509 379 Angle in degrees 7201253d-ef74-40ae-8173-4b941b44547b Degrees D false 25d0b3b4-fc42-4433-a4bf-e70bfa828143 1 480 367 14 24 488.5 379 Angle in radians c64ce2fc-64f4-4ffc-abb1-8343f67d6c30 Radians R false 0 524 367 16 24 532 379 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values 25d0b3b4-fc42-4433-a4bf-e70bfa828143 Number Slider angle ° false 0 160 370 295 20 160.2554 370.2197 2 1 0 170 0 0 110.7 c98a6015-7a2f-423c-bc66-bdc505249b45 Plane 3Pt Create a plane through three points. true 78631b64-b599-490f-b493-9d449559b6c0 Plane 3Pt Pl 3Pt -129 197 66 64 -98 229 Origin point c03438e8-8e89-47d2-b5c9-ca10c981173e Point A A false c318333b-fa9e-426b-b869-991ed69f1b64 1 -127 199 14 20 -118.5 209 X-direction point 88046baa-f5c6-4fbc-97be-c88ee826709d Point B B false 5252af42-8b65-437c-a487-5eac1156e2cc 1 -127 219 14 20 -118.5 229 Orientation point 9ece8cd3-edbb-4986-9f6d-886138f5abd4 Point C C false f4cf1901-0cd0-4475-9dd6-bf34f5ebc6e1 1 -127 239 14 20 -118.5 249 Plane definition af793683-207b-4276-a8a1-cc931d61589e Plane Pl false 0 -83 199 18 60 -74 229 3581f42a-9592-4549-bd6b-1c0fc39d067b Construct Point Construct a point from {xyz} coordinates. true 03b41596-1093-42df-8cb9-728ff4c83a73 Construct Point Pt -281 116 67 64 -250 148 {x} coordinate 46b26c29-1295-4a56-90de-e2187690ca0a X coordinate X false 39c22776-a388-46d7-abe4-caaffd8004f9 1 -279 118 14 20 -270.5 128 1 1 {0} 0 {y} coordinate 05cdff1f-dab0-4d5c-8b53-f69ce1483ebc Y coordinate Y false 108fecb2-82c4-4ef7-b31a-2d6517c32268 1 -279 138 14 20 -270.5 148 1 1 {0} 0 {z} coordinate 1cd02656-25ae-4866-8098-c3757e985576 Z coordinate Z false ca67e373-c39e-4d29-980d-1c76d91a2de9 1 -279 158 14 20 -270.5 168 1 1 {0} 0 Point coordinate c318333b-fa9e-426b-b869-991ed69f1b64 Point Pt false 0 -235 118 19 60 -225.5 148 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values 39c22776-a388-46d7-abe4-caaffd8004f9 Number Slider false 0 -594 112 260 20 -593.6757 112.793 2 1 0 50 -50 0 24.03 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values 108fecb2-82c4-4ef7-b31a-2d6517c32268 Number Slider false 0 -593 139 258 20 -592.4278 139.043 2 1 0 50 -50 0 -17.28 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values ca67e373-c39e-4d29-980d-1c76d91a2de9 Number Slider false 0 -592 164 258 20 -591.1778 164.043 2 1 0 50 -50 0 -9.72 3581f42a-9592-4549-bd6b-1c0fc39d067b Construct Point Construct a point from {xyz} coordinates. true 7d81920e-fe3b-4fcc-856a-7444755fcc4a Construct Point Pt -284 208 67 64 -253 240 {x} coordinate 5e590f17-28b6-4fcb-aeb8-acdb345f0c4b X coordinate X false abfd65c5-05aa-4b81-a108-6a5355e6816a 1 -282 210 14 20 -273.5 220 1 1 {0} 0 {y} coordinate 7d22d623-e49c-4769-b49e-88c16b2a4f4e Y coordinate Y false 81a1d1b0-c109-4a52-8403-b06d094902c8 1 -282 230 14 20 -273.5 240 1 1 {0} 0 {z} coordinate 0fafe809-64a8-4448-af4b-21caa924776e Z coordinate Z false b53d373c-ad33-4aaa-a8de-83a088e127a5 1 -282 250 14 20 -273.5 260 1 1 {0} 0 Point coordinate 5252af42-8b65-437c-a487-5eac1156e2cc Point Pt false 0 -238 210 19 60 -228.5 240 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values abfd65c5-05aa-4b81-a108-6a5355e6816a Number Slider false 0 -597 205 260 20 -596.1757 205.293 2 1 0 50 -50 0 3.28 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values 81a1d1b0-c109-4a52-8403-b06d094902c8 Number Slider false 0 -595 230 258 20 -594.9278 230.293 2 1 0 50 -50 0 0.25 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values b53d373c-ad33-4aaa-a8de-83a088e127a5 Number Slider false 0 -594 256 258 20 -593.6778 256.543 2 1 0 50 -50 0 18.96 3581f42a-9592-4549-bd6b-1c0fc39d067b Construct Point Construct a point from {xyz} coordinates. true 759d1f2d-c1de-45b6-9548-4d3ad4b27781 Construct Point Pt -286 296 67 64 -255 328 {x} coordinate 0ec6b789-6c06-4ce4-a442-c0cdb61b63ea X coordinate X false f24c7c6f-3341-48d7-b5dd-11493225c086 1 -284 298 14 20 -275.5 308 1 1 {0} 0 {y} coordinate cadc8592-52b0-4c3f-9cd8-6474d8000ca7 Y coordinate Y false 2313a88f-2ff0-48dc-b09b-dee536af8862 1 -284 318 14 20 -275.5 328 1 1 {0} 0 {z} coordinate 003f761f-6321-417a-9a6f-83613eba8698 Z coordinate Z false da887b2c-91f5-4fc7-b9b2-dd3c06bf079f 1 -284 338 14 20 -275.5 348 1 1 {0} 0 Point coordinate f4cf1901-0cd0-4475-9dd6-bf34f5ebc6e1 Point Pt false 0 -240 298 19 60 -230.5 328 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values f24c7c6f-3341-48d7-b5dd-11493225c086 Number Slider false 0 -598 294 260 20 -597.4257 294.043 2 1 0 50 -50 0 -7.63 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values 2313a88f-2ff0-48dc-b09b-dee536af8862 Number Slider false 0 -597 319 258 20 -596.1778 319.043 2 1 0 50 -50 0 -14.68 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values da887b2c-91f5-4fc7-b9b2-dd3c06bf079f Number Slider false 0 -595 345 258 20 -594.9278 345.293 2 1 0 50 -50 0 -6.5 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values 5137ef09-783f-4981-a9ec-aa4f2fc8e019 Number Slider height false 0 159 339 384 20 159.0849 339.4185 2 1 0 200 0 0 112.83 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 225afdb2-480f-435b-a1cb-84170b3afd2f Panel false 0 0d6fe24b-f96e-4e86-bad3-d29ae034394e 1 Double click to edit panel content… 737 288 106 38 0 0 0 737.476 288.175 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 20228f31-e357-4d65-8747-46e12348391c Panel false 0 816f49f4-39a6-4705-80c7-e2a924ac1e0c 1 Double click to edit panel content… 856 313 105 55 0 0 0 856.035 313.0428 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 7c2a1ac2-4916-4aa3-9b0a-566a67f36e60 Panel false 0 b00909dc-b385-4e3e-a3a8-9e76efdaadeb 1 Double click to edit panel content… 736 348 108 38 0 0 0 736.4249 348.559 255;255;250;90 true true true false false true aaa665bd-fd6e-4ccb-8d2c-c5b33072125d Curvature Evaluate the curvature of a curve at a specified parameter. true 40932656-6c80-4daa-9ad2-8cf8b338fc8d Curvature Curvature 1151 798 65 64 1182 830 Curve to evaluate ef98c1a5-25c1-49b1-b59b-6afb9264b710 Curve C false 12b4fe3c-7f43-444b-be77-19b89104763b 1 1153 800 14 30 1161.5 815 Parameter on curve domain to evaluate bb19201b-82f6-46a3-968e-8457acb1864d Parameter t false 3dad486f-e4dc-4c1a-a3e3-edd2e0fd1459 1 1153 830 14 30 1161.5 845 Point on curve at {t} 6666272a-5753-4a2f-bade-d2d52bc5e52d Point P false 0 1197 800 17 20 1205.5 810 Curvature vector at {t} aa220db5-3068-4ca2-8821-8b9c90d1979a Curvature K false 0 1197 820 17 20 1205.5 830 Curvature circle at {t} 10cf8192-1856-476a-b1e6-4b0f3c70a20b Curvature C false 0 1197 840 17 20 1205.5 850 6b021f56-b194-4210-b9a1-6cef3b7d0848 Evaluate Length Evaluate a curve at a certain factor along its length. Length factors can be supplied both in curve units and normalized units. Change the [N] parameter to toggle between the two modes. true 7a3bf5ad-d8df-4c78-ab33-33f2a1b8fdd3 Evaluate Length Eval 1057 823 64 64 1088 855 Curve to evaluate 315efe79-3e18-4f12-9a9f-48c3fd619f70 Curve C false 12b4fe3c-7f43-444b-be77-19b89104763b 1 1059 825 14 20 1067.5 835 Length factor for curve evaluation 2bfe1d34-4f3b-4bb0-be9e-0296d2d11a85 Length L false 6f2330d2-0e46-4e09-9ac5-7b7d2a1d3954 1 1059 845 14 20 1067.5 855 1 1 {0} 0.5 If True, the Length factor is normalized (0.0 ~ 1.0) 768df78b-ea9f-4024-8585-81b6a0c10891 Normalized N false 0 1059 865 14 20 1067.5 875 1 1 {0} true Point at the specified length d2445ed4-e075-4562-b765-47e17a5185ca Point P false 0 1103 825 16 20 1111 835 Tangent vector at the specified length 5a606323-3c66-46dd-a374-a92dc7ab1c86 Tangent T false 0 1103 845 16 20 1111 855 Curve parameter at the specified length 3dad486f-e4dc-4c1a-a3e3-edd2e0fd1459 Parameter t false 0 1103 865 16 20 1111 875 23862862-049a-40be-b558-2418aacbd916 Deconstruct Arc Retrieve the base plane, radius and angle domain of an arc. true 55c865ff-19b4-4378-9996-badcb44a9589 Deconstruct Arc DArc 1267 847 65 64 1298 879 Arc or Circle to deconstruct e0a2a9c6-de9b-4ae3-854d-8bf414396827 Arc A false 10cf8192-1856-476a-b1e6-4b0f3c70a20b 1 1269 849 14 60 1277.5 879 Base plane of arc or circle 81d935f9-4a1b-49ca-b92c-718561d9b488 Base Plane B false 0 1313 849 17 20 1321.5 859 Radius of arc or circle 64504e23-6a31-41c8-aef7-9ea0c21e3465 Radius R false 0 1313 869 17 20 1321.5 879 Angle domain (in radians) of arc 3c6d7169-cc80-4199-8fef-ce0a92463c3a Angle A false 0 1313 889 17 20 1321.5 899 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values a04f3471-a7fc-4cd9-b101-bb6f71dec549 Panel false 0 64504e23-6a31-41c8-aef7-9ea0c21e3465 1 Double click to edit panel content… 1377 830 96 42 0 0 0 1377.688 830.6463 255;255;250;90 true true true false false true f12daa2f-4fd5-48c1-8ac3-5dea476912ca Mirror Mirror an object. 864b9457-bf65-45dc-a49e-8e972abffb2b Mirror Mirror 1277 665 65 44 1308 687 Base geometry ef1ac667-f8c6-4a26-800e-9f64490f8b70 Geometry G true 12b4fe3c-7f43-444b-be77-19b89104763b 10cf8192-1856-476a-b1e6-4b0f3c70a20b 2 1279 667 14 20 1287.5 677 Mirror plane fb82719d-d33e-4bcc-ab84-9035150adc96 Plane P false 0654eb92-001e-43e0-b38a-58a5d0d80f8e 1 1279 687 14 20 1287.5 697 1 1 {0} 0 0 0 0 0 1 1 0 0 Mirrored geometry fef45908-0b4b-4554-b56b-334d7b9a07c0 Geometry G false 0 1323 667 17 20 1331.5 677 Transformation data b1d2e017-d6c1-487a-925f-7e6b3170b50c Transform X false 0 1323 687 17 20 1331.5 697 9df5e896-552d-4c8c-b9ca-4fc147ffa022 Expression Evaluate an expression x*2 8a2af1f2-b070-42bc-acff-393a560d94c6 Expression Expression 1379 892 84 28 1419 906 1 ba80fd98-91a1-4958-b6a7-a94e40e52bdb 1 8ec86459-bf01-4409-baee-174d0d2b13d0 Expression variable 9378e904-7029-4099-9a7d-0a61740c8db5 Variable x x true 64504e23-6a31-41c8-aef7-9ea0c21e3465 1 1381 894 12 24 1388.5 906 Result of expression ae803c56-82b6-4b49-9bf4-1997a330a545 Result R false 0 1445 894 16 24 1453 906 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 7ff6881c-7677-4071-a632-b6c8ea1ac4d3 Panel false 0 ae803c56-82b6-4b49-9bf4-1997a330a545 1 Double click to edit panel content… 1489 887 96 42 0 0 0 1489.256 887.1902 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 0068d51d-c81e-4187-8df3-5835ab363a73 Panel false 0 0 71.7 486 1016 50 20 0 0 0 486.351 1016.602 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 65023053-5666-4c0b-b753-08fe81bad98b Panel false 0 0 0.09 51 1103 50 20 0 0 0 51.64487 1103.176 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values b958296d-1f29-45e4-a204-834500f3a2c8 Panel false 0 0 0.0055 50 1130 62 20 0 0 0 50.66486 1130.595 255;255;250;90 true true true false false true 9df5e896-552d-4c8c-b9ca-4fc147ffa022 Expression Evaluate an expression π*(outer^4-(outer-thick)^4)/32 c6fb6177-2d5c-4ea6-801e-f1d602a9945f Expression Expression 197 1105 324 44 366 1127 2 ba80fd98-91a1-4958-b6a7-a94e40e52bdb ba80fd98-91a1-4958-b6a7-a94e40e52bdb 1 8ec86459-bf01-4409-baee-174d0d2b13d0 Expression variable cf3ef3e5-5024-4f5c-a4c6-0baf27546401 Variable outer outer true 65023053-5666-4c0b-b753-08fe81bad98b 1 199 1107 30 20 215.5 1117 Expression variable 275aff5f-fca5-430e-bd77-af734dc26d01 Variable thick thick true b958296d-1f29-45e4-a204-834500f3a2c8 1 199 1127 30 20 215.5 1137 Result of expression c29e43b1-f147-4596-9a27-65e202efbf44 Result R false 0 503 1107 16 40 511 1127 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 61e14a85-7063-4c93-bec1-f130a2a79f94 Panel false 0 20b5374e-e220-4840-8424-dcf1b57d3ad2 1 Double click to edit panel content… 744 1065 97 58 0 0 0 744.1909 1065.401 255;255;250;90 true true true false false true d5967b9f-e8ee-436b-a8ad-29fdcecf32d5 Curve Contains a collection of generic curves 9ce607e3-994c-4bda-99e3-6e421454263b Curve Crv false 10cf8192-1856-476a-b1e6-4b0f3c70a20b 1 1303 728 50 24 1328.596 740.7537 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe Scribble true 147.2881 1085.861 504.9157 1084.888 504.9566 1099.915 147.329 1100.889 A quick note Microsoft Sans Serif a2b3599b-d7ad-4dff-aca4-d6dbc30755af false Scribble Scribble 16 area moment of inertia for a hollow rod (in m^4) 142.2881 1079.888 367.6685 26.00085 147.2881 1085.861 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe Scribble true 544.7806 1376.747 735.764 1376.736 735.7651 1391.763 544.7817 1391.774 A quick note Microsoft Sans Serif 37f949de-70f1-4437-96b3-797739d591c1 false Scribble Scribble 16 One height, two solutions 539.7806 1371.736 200.9845 25.03821 544.7806 1376.747 079bd9bd-54a0-41d4-98af-db999015f63d VB Script Private Function IsSet(ByVal param As String) As Boolean ' Check if an input parameter has data Dim i As Integer = Component.Params.IndexOfInputParam(param) If i > -1 Then Return Component.Params.Input.ElementAt(i).DataType > 1 ' input parameter DataType of 1 means it's not receiving input (internal or external) Else Msg("error", "Input parameter '" & param & "' not found") Return False End If End Function Private Sub Msg(ByVal type As String, ByVal msg As String) ' Output an error, warning, or informational message Select Case type Case "error" Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, msg) Print("Error: " & msg) Case "warning" Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, msg) Print("Warning: " & msg) Case "info" Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, msg) Print(msg) End Select End Sub ' Solve for the m parameter from length and width (reference {1} equation (34), except b = width and K(k) and E(k) should be K(m) and E(m)) Private Function SolveMFromLenWid(ByVal L As Double, ByVal w As Double) As Double If w = 0 Then Return Defined.M_ZERO_W ' for the boundry condition width = 0, bypass the function and return the known m value End If Dim n As Integer = 1 ' Iteration counter (quit if >MAXIT) Dim lower As Double = 0 ' m must be within this range Dim upper As Double = 1 Dim m As Double Dim cwl As Double Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 cwl = 2 * EllipticE(m) / EllipticK(m) - 1 ' calculate w/L with the test value of m If cwl < w / L Then ' compares the calculated w/L with the actual w/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop Return m End Function ' Solve for the m parameter from length and height (reference {1} equation (33), except K(k) should be K(m) and k = sqrt(m)) ' Note that it's actually possible to find 2 valid values for m (hence 2 width values) at certain height values Private Function SolveMFromLenHt(ByVal L As Double, ByVal h As Double) As List(Of Double) Dim n As Integer = 1 ' Iteration counter (quit if >MAXIT) Dim lower As Double = 0 ' m must be within this range Dim upper As Double = 1 Dim twoWidths As Boolean = h / L >= Defined.DOUBLE_W_HL_RATIO And h / L < Defined.MAX_HL_RATIO ' check to see if h/L is within the range where 2 solutions for the width are possible Dim m As Double Dim mult_m As New List(Of Double) Dim chl As Double If twoWidths Then ' find the first of two possible solutions for m with the following limits: lower = Defined.M_DOUBLE_W ' see constants at bottom of script upper = Defined.M_MAXHEIGHT ' see constants at bottom of script Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 chl = Math.Sqrt(m) / EllipticK(m) ' calculate h/L with the test value of m If chl > h / L Then ' compares the calculated h/L with the actual h/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop mult_m.Add(m) ' then find the second of two possible solutions for m with the following limits: lower = Defined.M_MAXHEIGHT ' see constants at bottom of script upper = 1 Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 chl = Math.Sqrt(m) / EllipticK(m) ' calculate h/L with the test value of m If chl < h / L Then ' compares the calculated h/L with the actual h/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop If m <= Defined.M_MAX Then ' return this m parameter only if it falls within the maximum useful value (above which the curve breaks down) mult_m.Add(m) End If Else ' find the one possible solution for the m parameter upper = Defined.M_DOUBLE_W ' limit the upper end of the search to the maximum value of m for which only one solution exists Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 chl = Math.Sqrt(m) / EllipticK(m) ' calculate h/L with the test value of m If chl > h / L Then ' compares the calculated h/L with the actual h/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop mult_m.Add(m) End If Return mult_m End Function ' Solve for the m parameter from width and height (derived from reference {1} equations (33) and (34) with same notes as above) Private Function SolveMFromWidHt(ByVal w As Double, ByVal h As Double) As Double Dim n As Integer = 1 ' Iteration counter (quit if >MAXIT) Dim lower As Double = 0 ' m must be within this range Dim upper As Double = 1 Dim m As Double Dim cwh As Double Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 cwh = (2 * EllipticE(m) - EllipticK(m)) / Math.Sqrt(m) ' calculate w/h with the test value of m If cwh < w / h Then ' compares the calculated w/h with the actual w/h then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop Return m End Function ' Calculate length based on height and an m parameter, derived from reference {1} equation (33), except K(k) should be K(m) and k = sqrt(m) Private Function Cal_L(ByVal h As Double, ByVal m As Double) As Double Return h * EllipticK(m) / Math.Sqrt(m) End Function ' Calculate width based on length and an m parameter, derived from reference {1} equation (34), except b = width and K(k) and E(k) should be K(m) and E(m) Private Function Cal_W(ByVal L As Double, ByVal m As Double) As Double Return L * (2 * EllipticE(m) / EllipticK(m) - 1) End Function ' Calculate height based on length and an m parameter, from reference {1} equation (33), except K(k) should be K(m) and k = sqrt(m) Private Function Cal_H(ByVal L As Double, ByVal m As Double) As Double Return L * Math.Sqrt(m) / EllipticK(m) End Function ' Calculate the unique m parameter based on a start tangent angle, from reference {2}, just above equation (9a), that states k = Sin(angle / 2 + Pi / 4), ' but as m = k^2 and due to this script's need for an angle rotated 90° versus the one in reference {1}, the following formula is the result ' New note: verified by reference {4}, pg. 78 at the bottom Private Function Cal_M(ByVal a As Double) As Double Return (1 - Math.Cos(a)) / 2 ' equal to Sin^2(a/2) too End Function ' Calculate start tangent angle based on an m parameter, derived from above formula Private Function Cal_A(ByVal m As Double) As Double Return Math.Acos(1 - 2 * m) End Function ' This is the heart of this script, taking the found (or specified) length, width, and angle values along with the found m parameter to create ' a list of points that approximate the shape or form of the elastica. It works by finding the x and y coordinates (which are reversed versus ' the original equations (12a) and (12b) from reference {2} due to the 90° difference in orientation) based on the tangent angle along the curve. ' See reference {2} for more details on how they derived it. Note that to simplify things, the algorithm only calculates the points for half of the ' curve, then mirrors those points along the y-axis. Private Function FindBendForm(ByVal L As Double, ByVal w As Double, ByVal m As Double, ByVal ang As Double, ByVal refPln As Plane) As List(Of Point3d) L = L / 2 ' because the below algorithm is based on the formulas in reference {2} for only half of the curve w = w / 2 ' same If ang = 0 Then ' if angle (and height) = 0, then simply return the start and end points of the straight line Dim out As New List(Of Point3d) out.Add(refPln.PointAt(w, 0, 0)) out.Add(refPln.PointAt(-w, 0, 0)) Return out End If Dim x As Double Dim y As Double Dim halfCurvePts As New List(Of Point3d) Dim fullCurvePts As New List(Of Point3d) Dim translatedPts As New List(Of Point3d) ang -= Math.PI / 2 ' a hack to allow this algorithm to work, since the original curve in paper {2} was rotated 90° Dim angB As Double = ang + (-Math.PI / 2 - ang) / Defined.CURVEDIVS ' angB is the 'lowercase theta' which should be in formula {2}(12b) as the interval ' start [a typo...see equation(3)]. It's necessary to start angB at ang + [interval] instead of just ang due to integration failing at angB = ang halfCurvePts.Add(New Point3d(w, 0, 0)) ' start with this known initial point, as integration will fail when angB = ang ' each point {x, y} is calculated from the tangent angle, angB, that occurs at each point (which is why this iterates from ~ang to -pi/2, the known end condition) Do While Math.Round(angB, Defined.ROUNDTO) >= Math.Round(-Math.PI / 2, Defined.ROUNDTO) y = (Math.Sqrt(2) * Math.Sqrt(Math.Sin(ang) - Math.Sin(angB)) * (w + L)) / (2 * EllipticE(m)) ' note that x and y are swapped vs. (12a) and (12b) x = (L / (Math.Sqrt(2) * EllipticK(m))) * Simpson(angB, -Math.PI / 2, 500, ang) ' calculate the Simpson approximation of the integral (function f below) ' over the interval angB ('lowercase theta') to -pi/2. side note: is 500 too few iterations for the Simson algorithm? If Math.Round(x, Defined.ROUNDTO) = 0 Then x = 0 halfCurvePts.Add(New Point3d(x, y, 0)) angB += (-Math.PI / 2 - ang) / Defined.CURVEDIVS ' onto the next tangent angle Loop ' After finding the x and y values for half of the curve, add the {-x, y} values for the rest of the curve For Each point As Point3d In halfCurvePts If Math.Round(point.X, Defined.ROUNDTO) = 0 Then If Math.Round(point.Y, Defined.ROUNDTO) = 0 Then fullCurvePts.Add(New Point3d(0, 0, 0)) ' special case when width = 0: when x = 0, only duplicate the point when y = 0 too End If Else fullCurvePts.Add(New Point3d(-point.X, point.Y, 0)) End If Next halfCurvePts.Reverse fullCurvePts.AddRange(halfCurvePts) For Each p As Point3d In fullCurvePts translatedPts.Add(refPln.PointAt(p.X, p.Y, p.Z)) ' translate the points from the reference plane to the world plane Next Return translatedPts End Function ' Interpolates the points from FindBendForm to create the Elastica curve. Uses start & end tangents for greater accuracy. Private Function MakeCurve(ByVal pts As List(Of Point3d), ByVal ang As Double, ByVal refPln As Plane) As Curve If ang <> 0 Then Dim ts, te As New Vector3d(refPln.XAxis) ts.Rotate(ang, refPln.ZAxis) te.Rotate(-ang, refPln.ZAxis) Return Curve.CreateInterpolatedCurve(pts, 3, CurveKnotStyle.Chord, ts, te) ' 3rd degree curve with 'Chord' Knot Style Else Return Curve.CreateInterpolatedCurve(pts, 3) ' if angle (and height) = 0, then simply interpolate the straight line (no start/end tangents) End If End Function ' Implements the Simpson approximation for an integral of function f below Public Function Simpson(a As Double, b As Double, n As Integer, theta As Double) As Double 'n should be an even number Dim j As Integer, s1 As Double, s2 As Double, h As Double h = (b - a) / n s1 = 0 s2 = 0 For j = 1 To n - 1 Step 2 s1 = s1 + fn(a + j * h, theta) Next j For j = 2 To n - 2 Step 2 s2 = s2 + fn(a + j * h, theta) Next j Simpson = h / 3 * (fn(a, theta) + 4 * s1 + 2 * s2 + fn(b, theta)) End Function ' Specific calculation for the above integration Public Function fn(x As Double, theta As Double) As Double fn = Math.Sin(x) / (Math.Sqrt(Math.Sin(theta) - Math.Sin(x))) ' from reference {2} formula (12b) End Function ' Return the Complete Elliptic integral of the 1st kind ' Abramowitz and Stegun p.591, formula 17.3.11 ' Code from http://www.codeproject.com/Articles/566614/Elliptic-integrals Public Function EllipticK(ByVal m As Double) As Double Dim sum, term, above, below As Double sum = 1 term = 1 above = 1 below = 2 For i As Integer = 1 To 100 term *= above / below sum += Math.Pow(m, i) * Math.Pow(term, 2) above += 2 below += 2 Next sum *= 0.5 * Math.PI Return sum End Function ' Return the Complete Elliptic integral of the 2nd kind ' Abramowitz and Stegun p.591, formula 17.3.12 ' Code from http://www.codeproject.com/Articles/566614/Elliptic-integrals Public Function EllipticE(ByVal m As Double) As Double Dim sum, term, above, below As Double sum = 1 term = 1 above = 1 below = 2 For i As Integer = 1 To 100 term *= above / below sum -= Math.Pow(m, i) * Math.Pow(term, 2) / above above += 2 below += 2 Next sum *= 0.5 * Math.PI Return sum End Function Friend Partial NotInheritable Class Defined Private Sub New() End Sub ' Note: most of these values for m and h/L ratio were found with Wolfram Alpha and either specific intercepts (x=0) or local minima/maxima. They should be constant. Public Const M_SKETCHY As Double = 0.95 ' value of the m parameter where the curvature near the ends of the curve gets wonky Public Const M_MAX As Double = 0.993 ' maximum useful value of the m parameter, above which this algorithm for the form of the curve breaks down Public Const M_ZERO_W As Double = 0.826114765984970336 ' value of the m parameter when width = 0 Public Const M_MAXHEIGHT As Double = 0.701327460663101223 ' value of the m parameter at maximum possible height of the bent rod/wire Public Const M_DOUBLE_W As Double = 0.180254422335013983 ' minimum value of the m parameter when two width values are possible for a given height and length Public Const DOUBLE_W_HL_RATIO As Double = 0.257342117984635757 ' value of the height/length ratio above which there are two possible width values Public Const MAX_HL_RATIO As Double = 0.403140189705650243 ' maximum possible value of the height/length ratio Public Const MAXERR As Double = 0.0000000001 ' error tolerance Public Const MAXIT As Integer = 100 ' maximum number of iterations Public Const ROUNDTO As Integer = 10 ' number of decimal places to round off to Public Const CURVEDIVS As Integer = 50 ' number of sample points for building the curve (or half-curve as it were) End Class A VB.NET scriptable component 98 86 true 34e9c6ff-5f0a-453a-89bb-504c40c19604 VB Script VB true 0 ' ----------------------------------------------------------------- ' Elastic Bending Script by Will McElwain ' Created February 2014 ' ' DESCRIPTION: ' This beast creates the so-called 'elastica curve', the shape a long, thin rod or wire makes when it is bent elastically (i.e. not permanently). In this case, force ' is assumed to only be applied horizontally (which would be in line with the rod at rest) and both ends are assumed to be pinned or hinged meaning they are free ' to rotate (as opposed to clamped, when the end tangent angle is fixed, usually horizontally). An interesting finding is that it doesn't matter what the material or ' cross-sectional area is, as long as they're uniform along the entire length. Everything makes the same shape when bent as long as it doesn't cross the threshold ' from elastic to plastic (permanent) deformation (I don't bother to find that limit here, but can be found if the yield stress for a material is known). ' ' Key to the formulas used in this script are elliptic integrals, specifically K(m), the complete elliptic integral of the first kind, and E(m), the complete elliptic ' integral of the second kind. There was a lot of confusion over the 'm' and 'k' parameters for these functions, as some people use them interchangeably, but they are ' not the same. m = k^2 (thus k = Sqrt(m)). I try to use the 'm' parameter exclusively to avoid this confusion. Note that there is a unique 'm' parameter for every ' configuration/shape of the elastica curve. ' ' This script tries to find that unique 'm' parameter based on the inputs. The algorithm starts with a test version of m, evaluates an expression, say 2*E(m)/K(m)-1, ' then compares the result to what it should be (in this case, a known width/length ratio). Iterate until the correct m is found. Once we have m, we can then calculate ' all of the other unknowns, then find points that lie on that curve, then interpolate those points for the actual curve. You can also use Wolfram|Alpha as I did to ' find the m parameter based on the equations in this script (example here: http://tiny.cc/t4tpbx for when say width=45.2 and length=67.1). ' ' Other notes: ' * This script works with negative values for width, which will creat a self-intersecting curve (as it should). The curvature of the elastica starts to break down around ' m=0.95 (~154°), but this script will continue to work until M_MAX, m=0.993 (~169°). If you wish to ignore self-intersecting curves, set ignoreSelfIntersecting to True ' * When the only known values are length and height, it is actually possible for certain ratios of height to length to have two valid m values (thus 2 possible widths ' and angles). This script will return them both. ' * Only the first two valid parameters (of the required ones) will be used, meaning if all four are connected (length, width or a PtB, height, and angle), this script will ' only use length and width (or a PtB). ' * Depending on the magnitude of your inputs (say if they're really small, like if length < 10), you might have to increase the constant ROUNDTO at the bottom ' ' REFERENCES: ' {1} "The elastic rod" by M.E. Pacheco Q. & E. Pina, http://www.scielo.org.mx/pdf/rmfe/v53n2/v53n2a8.pdf ' {2} "An experiment in nonlinear beam theory" by A. Valiente, http://www.deepdyve.com/lp/doc/I3lwnxdfGz , also here: http://tiny.cc/Valiente_AEiNBT ' {3} "Snap buckling, writhing and Loop formation In twisted rods" by V.G.A. GOSS, http://myweb.lsbu.ac.uk/~gossga/thesisFinal.pdf ' {4} "Theory of Elastic Stability" by Stephen Timoshenko, http://www.scribd.com/doc/50402462/Timoshenko-Theory-of-Elastic-Stability (start on p. 76) ' ' INPUT: ' PtA - First anchor point (required) ' PtB - Second anchor point (optional, though 2 out of the 4--length, width, height, angle--need to be specified) ' [note that PtB can be the same as PtA (meaning width would be zero)] ' [also note that if a different width is additionally specified that's not equal to the distance between PtA and PtB, then the end point will not equal PtB anymore] ' Pln - Plane of the bent rod/wire, which bends up in the +y direction. The line between PtA and PtB (if specified) must be parallel to the x-axis of this plane ' ' ** 2 of the following 4 need to be specified ** ' Len - Length of the rod/wire, which needs to be > 0 ' Wid - Width between the endpoints of the curve [note: if PtB is specified in addition, and distance between PtA and PtB <> width, the end point will be relocated ' Ht - Height of the bent rod/wire (when negative, curve will bend downward, relative to the input plane, instead) ' Ang - Inner departure angle or tangent angle (in radians) at the ends of the bent rod/wire. Set up so as width approaches length (thus height approaches zero), angle approaches zero ' ' * Following variables only needed for optional calculating of bending force, not for shape of curve. ' E - Young's modulus (modulus of elasticity) in GPa (=N/m^2) (material-specific. for example, 7075 aluminum is roughly 71.7 GPa) ' I - Second moment of area (or area moment of inertia) in m^4 (cross-section-specific. for example, a hollow rod ' would have I = pi * (outer_diameter^4 - inner_diameter^4) / 32 ' Note: E*I is also known as flexural rigidity or bending stiffness ' ' OUTPUT: ' out - only for debugging messages ' Pts - the list of points that approximate the shape of the elastica ' Crv - the 3rd-degree curve interpolated from those points (with accurate start & end tangents) ' L - the length of the rod/wire ' W - the distance (width) between the endpoints of the rod/wire ' H - the height of the bent rod/wire ' A - the tangent angle at the (start) end of the rod/wire ' F - the force needed to hold the rod/wire in a specific shape (based on the material properties & cross-section) **be sure your units for 'I' match your units for the ' rest of your inputs (length, width, etc.). Also note that the critical buckling load (force) that makes the rod/wire start to bend can be found at height=0 ' ' THANKS TO: ' Mårten Nettelbladt (thegeometryofbending.blogspot.com) ' Daniel Piker (Kangaroo plugin) ' David Rutten (Grasshopper guru) ' Euler & Bernoulli (the O.G.'s) ' ' ----------------------------------------------------------------- Dim ignoreSelfIntersecting As Boolean = False ' set to True if you don't want to output curves where width < 0, which creates a self-intersecting curve Dim inCt As Integer = 0 ' count the number of required parameters that are receiving data Dim length As Double Dim width As System.Object = Nothing ' need to set as Nothing so we can check if it has been assigned a value later Dim height As Double Dim angle As Double Dim m As Double Dim multiple_m As New List(Of Double) Dim AtoB As Line Dim flip_H As Boolean = False ' if height is negative, this flag will be set Dim flip_A As Boolean = False ' if angle is negative, this flag will be set If Not IsSet("Pln") Then Msg("error", "Base plane is not set") Return End If If Not IsSet("PtA") Then Msg("error", "Point A is not set") Return End If If Math.Round(Pln.DistanceTo(PtA), Defined.ROUNDTO) <> 0 Then Msg("error", "Point A is not on the base plane") Return End If Dim refPlane As Plane = Pln ' create a reference plane = input plane and set the origin of it to PtA in case PtA isn't the origin already refPlane.Origin = PtA If IsSet("PtB") Then If Math.Round(Pln.DistanceTo(PtB), Defined.ROUNDTO) <> 0 Then Msg("error", "Point B is not on the base plane") Return End If AtoB = New Line(PtA, PtB) If AtoB.Length <> 0 And Not AtoB.Direction.IsPerpendicularTo(Pln.YAxis) Then Msg("error", "The line between PtA and PtB is not perpendicular to the Y-axis of the specified plane") Return End If inCt += 1 If IsSet("Wid") Then Msg("info", "Wid will override the distance between PtA and PtB. If you do not want this to happen, disconnect PtB or Wid.") width = PtA.DistanceTo(PtB) ' get the width (distance) between PtA and PtB Dim refPtB As Point3d refPlane.RemapToPlaneSpace(PtB, refPtB) If refPtB.X < 0 Then width = -width ' check if PtB is to the left of PtA...if so, width is negative End If If IsSet("Len") Then inCt += 1 If IsSet("Wid") Then inCt += 1 If IsSet("Ht") Then inCt += 1 If IsSet("Ang") Then inCt += 1 If inCt > 2 Then Msg("info", "More parameters set than are required (out of length, width, height, angle). Only using the first two valid ones.") ' check for connected/specified inputs. note: only the first two that it comes across will be used If IsSet("Len") Then ' if length is specified then... If Len <= 0 Then Msg("error", "Length cannot be negative or zero") Return End If If IsSet("Wid") Then ' find height & angle based on length and specified width If Wid > Len Then Msg("error", "Width is greater than length") Return End If If Wid = Len Then ' skip the solver and set the known values height = 0 m = 0 angle = 0 width = Wid Else m = SolveMFromLenWid(Len, Wid) height = Cal_H(Len, m) ' L * Sqrt(m) / K(m) angle = Cal_A(m) ' Acos(1 - 2 * m) width = Wid End If Else If width IsNot Nothing Then ' find height & angle based on length and calculated width (distance between PtA and PtB) If width > Len Then Msg("error", "Width is greater than length") Return End If If width = Len Then ' skip the solver and set the known values height = 0 m = 0 angle = 0 Else m = SolveMFromLenWid(Len, width) height = Cal_H(Len, m) ' L * Sqrt(m) / K(m) angle = Cal_A(m) ' Acos(1 - 2 * m) End If Else If IsSet("Ht") Then ' find width & angle based on length and height ** possible to return 2 results ** If Math.Abs(Ht / Len) > Defined.MAX_HL_RATIO Then Msg("error", "Height not possible with given length") Return End If If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If If Ht = 0 Then ' skip the solver and set the known values width = Len angle = 0 Else multiple_m = SolveMFromLenHt(Len, Ht) ' note that it's possible for two values of m to be found if height is close to max height If multiple_m.Count = 1 Then ' if there's only one m value returned, calculate the width & angle here. we'll deal with multiple m values later m = multiple_m.Item(0) width = Cal_W(Len, m) ' L * (2 * E(m) / K(m) - 1) angle = Cal_A(m) ' Acos(1 - 2 * m) End If End If height = Ht Else If IsSet("Ang") Then ' find width & height based on length and angle If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then ' skip the solver and set the known values width = Len height = 0 Else width = Cal_W(Len, m) ' L * (2 * E(m) / K(m) - 1) height = Cal_H(Len, m) ' L * Sqrt(m) / K(m) End If angle = Ang Else Msg("error", "Need to specify one more parameter in addition to length") Return End If length = Len Else If IsSet("Wid") Then ' if width is specified then... If IsSet("Ht") Then ' find length & angle based on specified width and height If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If If Ht = 0 Then ' skip the solver and set the known values length = Wid angle = 0 Else m = SolveMFromWidHt(Wid, Ht) length = Cal_L(Ht, m) ' h * K(m) / Sqrt(m) angle = Cal_A(m) ' Acos(1 - 2 * m) End If height = Ht Else If IsSet("Ang") Then ' find length & height based on specified width and angle If Wid = 0 Then Msg("error", "Curve not possible with width = 0 and an angle as inputs") Return End If If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then ' skip the solver and set the known values length = Wid height = 0 Else length = Wid / (2 * EllipticE(m) / EllipticK(m) - 1) If length < 0 Then Msg("error", "Curve not possible at specified width and angle (calculated length is negative)") Return End If height = Cal_H(length, m) ' L * Sqrt(m) / K(m) End If angle = Ang Else Msg("error", "Need to specify one more parameter in addition to width (Wid)") Return End If width = Wid Else If width IsNot Nothing Then ' if width is determined by PtA and PtB then... If IsSet("Ht") Then ' find length & angle based on calculated width and height If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If If Ht = 0 Then ' skip the solver and set the known values length = width angle = 0 Else m = SolveMFromWidHt(width, Ht) length = Cal_L(Ht, m) ' h * K(m) / Sqrt(m) angle = Cal_A(m) ' Acos(1 - 2 * m) End If height = Ht Else If IsSet("Ang") Then ' find length & height based on calculated width and angle If width = 0 Then Msg("error", "Curve not possible with width = 0 and an angle as inputs") Return End If If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then ' skip the solver and set the known values length = width height = 0 Else length = width / (2 * EllipticE(m) / EllipticK(m) - 1) If length < 0 Then Msg("error", "Curve not possible at specified width and angle (calculated length is negative)") Return End If height = Cal_H(length, m) ' L * Sqrt(m) / K(m) End If angle = Ang Else Msg("error", "Need to specify one more parameter in addition to PtA and PtB") Return End If Else If IsSet("Ht") Then ' if height is specified then... If IsSet("Ang") Then ' find length & width based on height and angle If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_H = True flip_A = True End If If Ht = 0 Then Msg("error", "Height can't = 0 if only height and angle are specified") Return Else If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = Not flip_A flip_H = Not flip_H End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then Msg("error", "Angle can't = 0 if only height and angle are specified") Return Else length = Cal_L(Ht, m) ' h * K(m) / Sqrt(m) width = Cal_W(length, m) ' L * (2 * E(m) / K(m) - 1) End If angle = Ang End If height = Ht Else Msg("error", "Need to specify one more parameter in addition to height") Return End If Else If IsSet("Ang") Then Msg("error", "Need to specify one more parameter in addition to angle") Return Else Msg("error", "Need to specify two of the four parameters: length, width (or PtB), height, and angle") Return End If If m > Defined.M_MAX Then Msg("error", "Form of curve not solvable with current algorithm and given inputs") Return End If refPlane.Origin = refPlane.PointAt(width / 2, 0, 0) ' adjust the origin of the reference plane so that the curve is centered about the y-axis (start of the curve is at x = -width/2) If multiple_m.Count > 1 Then ' if there is more than one m value returned, calculate the width, angle, and curve for each Dim multi_pts As New DataTree(Of Point3d) Dim multi_crv As New List(Of Curve) Dim tmp_pts As New List(Of Point3d) Dim multi_W, multi_A, multi_F As New List(Of Double) Dim j As Integer = 0 ' used for creating a new branch (GH_Path) for storing pts which is itself a list of points For Each m_val As Double In multiple_m width = Cal_W(length, m_val) 'length * (2 * EllipticE(m_val) / EllipticK(m_val) - 1) If width < 0 And ignoreSelfIntersecting Then Msg("warning", "One curve is self-intersecting. To enable these, set ignoreSelfIntersecting to False") Continue For End If If m_val >= Defined.M_SKETCHY Then Msg("info", "Accuracy of the curve whose width = " & Math.Round(width, 4) & " is not guaranteed") angle = Cal_A(m_val) 'Math.Asin(2 * m_val - 1) refPlane.Origin = refPlane.PointAt(width / 2, 0, 0) ' adjust the origin of the reference plane so that the curve is centered about the y-axis (start of the curve is at x = -width/2) tmp_pts = FindBendForm(length, width, m_val, angle, refPlane) multi_pts.AddRange(tmp_pts, New GH_Path(j)) multi_crv.Add(MakeCurve(tmp_pts, angle, refPlane)) multi_W.Add(width) If flip_A Then angle = -angle multi_A.Add(angle) E = E * 10 ^ 9 ' Young's modulus input E is in GPa, so we convert to Pa here (= N/m^2) multi_F.Add(EllipticK(m_val) ^ 2 * E * I / length ^ 2) ' from reference {4} pg. 79 j += 1 refPlane.Origin = PtA ' reset the reference plane origin to PtA for the next m_val 'Print("length=" & length & ", width=" & width & ", height=" & height & ", angle=" & angle & ", m=" & m_val & ", k=" & Math.Sqrt(m_val) & ", w/L=" & width / length & ", h/L=" & height / length & ", w/h=" & width / height) Next ' assign the outputs Pts = multi_pts Crv = multi_crv L = length W = multi_W If flip_H Then height = -height H = height A = multi_A F = multi_F Else ' only deal with the single m value If m >= Defined.M_SKETCHY Then Msg("info", "Accuracy of the curve at these parameters is not guaranteed") If width < 0 And ignoreSelfIntersecting Then Msg("error", "Curve is self-intersecting. To enable these, set ignoreSelfIntersecting to False") Return End If Pts = FindBendForm(length, width, m, angle, refPlane) Crv = MakeCurve(pts, angle, refPlane) L = length W = width If flip_H Then height = -height H = height If flip_A Then angle = -angle A = angle E = E * 10 ^ 9 ' Young's modulus input E is in GPa, so we convert to Pa here (= N/m^2) F = EllipticK(m) ^ 2 * E * I / length ^ 2 ' from reference {4} pg. 79. Note: the critical buckling (that makes the rod/wire start to bend) can be found at height=0 (width=length) 'height = Math.Sqrt(((2 * Len / 5) ^ 2 - ((Wid - Len / 5) / 2) ^ 2) ' quick approximation discovered by Mårten of 'Geometry of Bending' fame ( http://tiny.cc/it2pbx ) 'width = (Len +/- 2 * Math.Sqrt(4 * Len ^ 2 - 25 * Ht ^ 2)) / 5 ' derived from above 'length = (2 * Math.Sqrt(15 * Ht ^ 2 + 4 * Wid ^ 2) - Wid) / 3 ' derived from above 'Print("length=" & length & ", width=" & width & ", height=" & height & ", angle=" & angle & ", m=" & m & ", k=" & Math.Sqrt(m) & ", w/L=" & width / length & ", h/L=" & height / length & ", w/h=" & width / height) End If 615 822 84 184 657 914 9 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 8 3ede854e-c753-40eb-84cb-b48008f14fd4 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 true Script Variable PtA e44ef3d7-d80d-4847-b3c9-7145c5d256c7 PtA PtA true 0 true 544607c8-b250-4465-bd1a-e6ed510b2090 1 e1937b56-b1da-4c12-8bd8-e34ee81746ef 617 824 25 20 631 834 true Script Variable PtB f3a5f81b-cbeb-43a5-b9d7-95ddf8329091 PtB PtB true 0 true 0 e1937b56-b1da-4c12-8bd8-e34ee81746ef 617 844 25 20 631 854 true Script Variable Pln 25fe63c8-a9a1-43dd-ba6c-ff444bb1bd38 Pln Pln true 0 true 2e367eec-73c9-49fe-931b-de57feb15198 1 3897522d-58e9-4d60-b38c-978ddacfedd8 617 864 25 20 631 874 true Script Variable Len c645a879-0d63-4de2-8207-39e77ea0b36a Len Len true 0 true 7e001f0c-1ecd-4198-95f7-0d0278815c2b 1 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 617 884 25 20 631 894 true Script Variable Wid 563b27b8-1de0-4076-9540-f1f6287d4585 Wid Wid true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 617 904 25 20 631 914 true Script Variable Ht f945874f-3911-45dd-a65b-5931b59aaeda Ht Ht true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 617 924 25 20 631 934 true Script Variable Ang 7cbb4f70-f9aa-4f07-bc67-aac4eaace9e1 Ang Ang true 0 true b2a8f43d-df9e-4385-bfbe-4e7a54acdc52 1 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 617 944 25 20 631 954 true Script Variable E 757c9fbf-c87a-438c-9240-7ed119111c17 E E true 0 true 0068d51d-c81e-4187-8df3-5835ab363a73 1 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 617 964 25 20 631 974 true Script Variable I dfba63c4-6cba-4504-bd15-6d7b03b308e0 I I true 0 true c29e43b1-f147-4596-9a27-65e202efbf44 1 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 617 984 25 20 631 994 1 Print, Reflect and Error streams e74891fd-659b-4fdb-833d-cbae9b2064f7 out out false 0 672 824 25 22 684.5 835.25 Output parameter Pts 74a1959d-f66e-4c81-8b36-b26ed2379bb0 Pts Pts false 0 672 846 25 23 684.5 857.75 Output parameter Crv 9cf1549b-e355-4e34-8c66-56776a6693a3 Crv Crv false 0 672 869 25 22 684.5 880.25 Output parameter L 03e53700-d1d7-480a-9312-c1af517baedd L L false 0 672 891 25 23 684.5 902.75 Output parameter W 09f794fe-078e-4e7b-840b-9d52b913b097 W W false 0 672 914 25 22 684.5 925.25 Output parameter H 2373426a-a151-4361-9f8e-d19fef664dc8 H H false 0 672 936 25 23 684.5 947.75 Output parameter A 619887fe-9427-4cea-b7fd-0b8e37ad0966 A A false 0 672 959 25 22 684.5 970.25 Output parameter F 20b5374e-e220-4840-8424-dcf1b57d3ad2 F F false 0 672 981 25 23 684.5 992.75 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values 7e001f0c-1ecd-4198-95f7-0d0278815c2b Number Slider length false 0 160 874 382 20 160.4443 874.5179 2 1 0 400 0 0 145.76 fbac3e32-f100-4292-8692-77240a42fd1a Point Contains a collection of three-dimensional points true 37000574-d15c-4f7d-92d9-b148cb8b434c Point Pt false 74a1959d-f66e-4c81-8b36-b26ed2379bb0 1 784 791 50 24 809.0988 803.2372 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 982de226-ec32-4b13-b043-f53881d2e028 Panel false 0 1487d807-5f9b-4abc-9b8d-effa22608b5d 1 Double click to edit panel content… 849 986 105 55 0 0 0 849.9546 986.3983 255;255;250;90 true true true false false true 0d77c51e-584f-44e8-aed2-c2ddf4803888 Degrees Convert an angle specified in radians to degrees f53628df-d187-47f6-8939-9a7cff89ea30 Degrees Deg 764 997 64 28 794 1011 Angle in radians 6e9d9246-ea82-40e7-a5c7-6200a0750140 Radians R false 619887fe-9427-4cea-b7fd-0b8e37ad0966 1 766 999 13 24 774 1011 Angle in degrees 1487d807-5f9b-4abc-9b8d-effa22608b5d Degrees D false 0 809 999 17 24 817.5 1011 3581f42a-9592-4549-bd6b-1c0fc39d067b Construct Point Construct a point from {xyz} coordinates. f1c497f2-a656-46bd-97ff-e6f55517ad83 Construct Point Pt 390 743 67 64 421 775 {x} coordinate f274b47b-1218-4782-8247-527101e3221f X coordinate X false 0 392 745 14 20 400.5 755 1 1 {0} 0 {y} coordinate 8bb61e7c-9fbb-42a5-be49-c22980dcde8c Y coordinate Y false fd7d6e04-d8e1-46ec-9660-5d3b6392bb5c 1 392 765 14 20 400.5 775 1 1 {0} -100 {z} coordinate fd7813a9-1054-41ca-9f6b-83a5fd0efcce Z coordinate Z false 0 392 785 14 20 400.5 795 1 1 {0} 0 Point coordinate 544607c8-b250-4465-bd1a-e6ed510b2090 Point Pt false 0 436 745 19 60 445.5 775 d5967b9f-e8ee-436b-a8ad-29fdcecf32d5 Curve Contains a collection of generic curves 12b4fe3c-7f43-444b-be77-19b89104763b Curve Crv false 9cf1549b-e355-4e34-8c66-56776a6693a3 1 784 825 50 24 809.8876 837.7669 17b7152b-d30d-4d50-b9ef-c9fe25576fc2 XY Plane World XY plane. true 8cd68a99-5c0c-40f6-8f6c-c57b65a7c0fb XY Plane XY 492 837 64 28 523 851 Origin of plane d599495e-da0d-4b5f-96ca-d429779f4f7e Origin O false 544607c8-b250-4465-bd1a-e6ed510b2090 1 494 839 14 24 502.5 851 1 1 {0} 0 0 0 World XY plane 2e367eec-73c9-49fe-931b-de57feb15198 Plane P false 0 538 839 16 24 546 851 a4cd2751-414d-42ec-8916-476ebf62d7fe Radians Convert an angle specified in degrees to radians 0de50cbe-99d5-4598-bc61-8cc3eacb616f Radians Rad 481 954 64 28 512 968 Angle in degrees 8514650b-5a4b-4bd4-b272-4db0089c786a Degrees D false 5eda29cf-b445-43dc-9fe0-d002172bab75 1 483 956 14 24 491.5 968 Angle in radians b2a8f43d-df9e-4385-bfbe-4e7a54acdc52 Radians R false 0 527 956 16 24 535 968 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values 5eda29cf-b445-43dc-9fe0-d002172bab75 Number Slider angle ° false 0 167 960 295 20 167.2024 960.4379 2 1 0 170 0 0 60 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values e59fa8de-3b01-40cb-9090-35007fc19bba Panel false 0 03e53700-d1d7-480a-9312-c1af517baedd 1 Double click to edit panel content… 739 877 97 38 0 0 0 739.8782 877.1644 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 552a2357-924c-4919-bc11-da8244c7b3c6 Panel false 0 09f794fe-078e-4e7b-840b-9d52b913b097 1 Double click to edit panel content… 850 902 105 55 0 0 0 850.7964 902.0322 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 7d007978-016f-4867-9bef-68c359c70508 Panel false 0 2373426a-a151-4361-9f8e-d19fef664dc8 1 Double click to edit panel content… 738 937 97 38 0 0 0 738.8221 937.5484 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 6f2330d2-0e46-4e09-9ac5-7b7d2a1d3954 Panel false 0 0 0.5 988 843 50 20 0 0 0 988.351 843.602 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values fd7d6e04-d8e1-46ec-9660-5d3b6392bb5c Panel false 0 0 -100 312 766 50 20 0 0 0 312.841 766.482 255;255;250;90 true true true false false true 8cc3a196-f6a0-49ea-9ed9-0cb343a3ae64 XZ Plane World XZ plane. true 8b551e34-6c38-4a2b-9e47-c0b841fb2d47 XZ Plane XZ 1145 713 64 28 1176 727 Origin of plane 6e5d1c51-960b-46f9-898e-f2b11bdb8846 Origin O false 544607c8-b250-4465-bd1a-e6ed510b2090 1 1147 715 14 24 1155.5 727 1 1 {0} 0 0 0 World XZ plane 0654eb92-001e-43e0-b38a-58a5d0d80f8e Plane P false 0 1191 715 16 24 1199 727 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 95f9fd7f-37dc-4bd8-8105-7301ef052bdd Panel false 0 0 160 306 175 50 20 0 0 0 306.7406 175.2052 255;255;250;90 true true true false false true 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe Scribble true -552.3402 73.81226 -194.0922 73.81226 -194.0922 88.8396 -552.3402 88.8396 A quick note Microsoft Sans Serif 501dc92a-f2db-4bb4-8054-89bd8827333b false Scribble Scribble 16 for testing different points on an alternate plane -557.3402 68.81226 368.248 25.02734 -552.3402 73.81226 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe Scribble true 317.6904 47.65552 768.3664 47.65552 768.3664 71.13574 317.6904 71.13574 A quick note Microsoft Sans Serif 32f0e79e-6dca-4f24-9b30-17ca99e1ac07 false Scribble Scribble 25 Elastic Bending Script - Main Example 312.6904 42.65552 460.676 33.48022 317.6904 47.65552 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe Scribble true 559.8143 683.617 913.3091 683.2137 913.3262 698.241 559.8313 698.6443 A quick note Microsoft Sans Serif 834f8190-7e8f-4039-8f4f-5e398c86ddfa false Scribble Scribble 16 At 60°, minimum curve radius = height. Try 90° 554.8143 678.2137 363.5119 25.43066 559.8143 683.617 079bd9bd-54a0-41d4-98af-db999015f63d VB Script Private Function IsSet(ByVal param As String) As Boolean ' Check if an input parameter has data Dim i As Integer = Component.Params.IndexOfInputParam(param) If i > -1 Then Return Component.Params.Input.ElementAt(i).DataType > 1 ' input parameter DataType of 1 means it's not receiving input (internal or external) Else Msg("error", "Input parameter '" & param & "' not found") Return False End If End Function Private Sub Msg(ByVal type As String, ByVal msg As String) ' Output an error, warning, or informational message Select Case type Case "error" Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, msg) Print("Error: " & msg) Case "warning" Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, msg) Print("Warning: " & msg) Case "info" Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, msg) Print(msg) End Select End Sub ' Solve for the m parameter from length and width (reference {1} equation (34), except b = width and K(k) and E(k) should be K(m) and E(m)) Private Function SolveMFromLenWid(ByVal L As Double, ByVal w As Double) As Double If w = 0 Then Return Defined.M_ZERO_W ' for the boundry condition width = 0, bypass the function and return the known m value End If Dim n As Integer = 1 ' Iteration counter (quit if >MAXIT) Dim lower As Double = 0 ' m must be within this range Dim upper As Double = 1 Dim m As Double Dim cwl As Double Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 cwl = 2 * EllipticE(m) / EllipticK(m) - 1 ' calculate w/L with the test value of m If cwl < w / L Then ' compares the calculated w/L with the actual w/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop Return m End Function ' Solve for the m parameter from length and height (reference {1} equation (33), except K(k) should be K(m) and k = sqrt(m)) ' Note that it's actually possible to find 2 valid values for m (hence 2 width values) at certain height values Private Function SolveMFromLenHt(ByVal L As Double, ByVal h As Double) As List(Of Double) Dim n As Integer = 1 ' Iteration counter (quit if >MAXIT) Dim lower As Double = 0 ' m must be within this range Dim upper As Double = 1 Dim twoWidths As Boolean = h / L >= Defined.DOUBLE_W_HL_RATIO And h / L < Defined.MAX_HL_RATIO ' check to see if h/L is within the range where 2 solutions for the width are possible Dim m As Double Dim mult_m As New List(Of Double) Dim chl As Double If twoWidths Then ' find the first of two possible solutions for m with the following limits: lower = Defined.M_DOUBLE_W ' see constants at bottom of script upper = Defined.M_MAXHEIGHT ' see constants at bottom of script Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 chl = Math.Sqrt(m) / EllipticK(m) ' calculate h/L with the test value of m If chl > h / L Then ' compares the calculated h/L with the actual h/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop mult_m.Add(m) ' then find the second of two possible solutions for m with the following limits: lower = Defined.M_MAXHEIGHT ' see constants at bottom of script upper = 1 Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 chl = Math.Sqrt(m) / EllipticK(m) ' calculate h/L with the test value of m If chl < h / L Then ' compares the calculated h/L with the actual h/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop If m <= Defined.M_MAX Then ' return this m parameter only if it falls within the maximum useful value (above which the curve breaks down) mult_m.Add(m) End If Else ' find the one possible solution for the m parameter upper = Defined.M_DOUBLE_W ' limit the upper end of the search to the maximum value of m for which only one solution exists Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 chl = Math.Sqrt(m) / EllipticK(m) ' calculate h/L with the test value of m If chl > h / L Then ' compares the calculated h/L with the actual h/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop mult_m.Add(m) End If Return mult_m End Function ' Solve for the m parameter from width and height (derived from reference {1} equations (33) and (34) with same notes as above) Private Function SolveMFromWidHt(ByVal w As Double, ByVal h As Double) As Double Dim n As Integer = 1 ' Iteration counter (quit if >MAXIT) Dim lower As Double = 0 ' m must be within this range Dim upper As Double = 1 Dim m As Double Dim cwh As Double Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 cwh = (2 * EllipticE(m) - EllipticK(m)) / Math.Sqrt(m) ' calculate w/h with the test value of m If cwh < w / h Then ' compares the calculated w/h with the actual w/h then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop Return m End Function ' Calculate length based on height and an m parameter, derived from reference {1} equation (33), except K(k) should be K(m) and k = sqrt(m) Private Function Cal_L(ByVal h As Double, ByVal m As Double) As Double Return h * EllipticK(m) / Math.Sqrt(m) End Function ' Calculate width based on length and an m parameter, derived from reference {1} equation (34), except b = width and K(k) and E(k) should be K(m) and E(m) Private Function Cal_W(ByVal L As Double, ByVal m As Double) As Double Return L * (2 * EllipticE(m) / EllipticK(m) - 1) End Function ' Calculate height based on length and an m parameter, from reference {1} equation (33), except K(k) should be K(m) and k = sqrt(m) Private Function Cal_H(ByVal L As Double, ByVal m As Double) As Double Return L * Math.Sqrt(m) / EllipticK(m) End Function ' Calculate the unique m parameter based on a start tangent angle, from reference {2}, just above equation (9a), that states k = Sin(angle / 2 + Pi / 4), ' but as m = k^2 and due to this script's need for an angle rotated 90° versus the one in reference {1}, the following formula is the result ' New note: verified by reference {4}, pg. 78 at the bottom Private Function Cal_M(ByVal a As Double) As Double Return (1 - Math.Cos(a)) / 2 ' equal to Sin^2(a/2) too End Function ' Calculate start tangent angle based on an m parameter, derived from above formula Private Function Cal_A(ByVal m As Double) As Double Return Math.Acos(1 - 2 * m) End Function ' This is the heart of this script, taking the found (or specified) length, width, and angle values along with the found m parameter to create ' a list of points that approximate the shape or form of the elastica. It works by finding the x and y coordinates (which are reversed versus ' the original equations (12a) and (12b) from reference {2} due to the 90° difference in orientation) based on the tangent angle along the curve. ' See reference {2} for more details on how they derived it. Note that to simplify things, the algorithm only calculates the points for half of the ' curve, then mirrors those points along the y-axis. Private Function FindBendForm(ByVal L As Double, ByVal w As Double, ByVal m As Double, ByVal ang As Double, ByVal refPln As Plane) As List(Of Point3d) L = L / 2 ' because the below algorithm is based on the formulas in reference {2} for only half of the curve w = w / 2 ' same If ang = 0 Then ' if angle (and height) = 0, then simply return the start and end points of the straight line Dim out As New List(Of Point3d) out.Add(refPln.PointAt(w, 0, 0)) out.Add(refPln.PointAt(-w, 0, 0)) Return out End If Dim x As Double Dim y As Double Dim halfCurvePts As New List(Of Point3d) Dim fullCurvePts As New List(Of Point3d) Dim translatedPts As New List(Of Point3d) ang -= Math.PI / 2 ' a hack to allow this algorithm to work, since the original curve in paper {2} was rotated 90° Dim angB As Double = ang + (-Math.PI / 2 - ang) / Defined.CURVEDIVS ' angB is the 'lowercase theta' which should be in formula {2}(12b) as the interval ' start [a typo...see equation(3)]. It's necessary to start angB at ang + [interval] instead of just ang due to integration failing at angB = ang halfCurvePts.Add(New Point3d(w, 0, 0)) ' start with this known initial point, as integration will fail when angB = ang ' each point {x, y} is calculated from the tangent angle, angB, that occurs at each point (which is why this iterates from ~ang to -pi/2, the known end condition) Do While Math.Round(angB, Defined.ROUNDTO) >= Math.Round(-Math.PI / 2, Defined.ROUNDTO) y = (Math.Sqrt(2) * Math.Sqrt(Math.Sin(ang) - Math.Sin(angB)) * (w + L)) / (2 * EllipticE(m)) ' note that x and y are swapped vs. (12a) and (12b) x = (L / (Math.Sqrt(2) * EllipticK(m))) * Simpson(angB, -Math.PI / 2, 500, ang) ' calculate the Simpson approximation of the integral (function f below) ' over the interval angB ('lowercase theta') to -pi/2. side note: is 500 too few iterations for the Simson algorithm? If Math.Round(x, Defined.ROUNDTO) = 0 Then x = 0 halfCurvePts.Add(New Point3d(x, y, 0)) angB += (-Math.PI / 2 - ang) / Defined.CURVEDIVS ' onto the next tangent angle Loop ' After finding the x and y values for half of the curve, add the {-x, y} values for the rest of the curve For Each point As Point3d In halfCurvePts If Math.Round(point.X, Defined.ROUNDTO) = 0 Then If Math.Round(point.Y, Defined.ROUNDTO) = 0 Then fullCurvePts.Add(New Point3d(0, 0, 0)) ' special case when width = 0: when x = 0, only duplicate the point when y = 0 too End If Else fullCurvePts.Add(New Point3d(-point.X, point.Y, 0)) End If Next halfCurvePts.Reverse fullCurvePts.AddRange(halfCurvePts) For Each p As Point3d In fullCurvePts translatedPts.Add(refPln.PointAt(p.X, p.Y, p.Z)) ' translate the points from the reference plane to the world plane Next Return translatedPts End Function ' Interpolates the points from FindBendForm to create the Elastica curve. Uses start & end tangents for greater accuracy. Private Function MakeCurve(ByVal pts As List(Of Point3d), ByVal ang As Double, ByVal refPln As Plane) As Curve If ang <> 0 Then Dim ts, te As New Vector3d(refPln.XAxis) ts.Rotate(ang, refPln.ZAxis) te.Rotate(-ang, refPln.ZAxis) Return Curve.CreateInterpolatedCurve(pts, 3, CurveKnotStyle.Chord, ts, te) ' 3rd degree curve with 'Chord' Knot Style Else Return Curve.CreateInterpolatedCurve(pts, 3) ' if angle (and height) = 0, then simply interpolate the straight line (no start/end tangents) End If End Function ' Implements the Simpson approximation for an integral of function f below Public Function Simpson(a As Double, b As Double, n As Integer, theta As Double) As Double 'n should be an even number Dim j As Integer, s1 As Double, s2 As Double, h As Double h = (b - a) / n s1 = 0 s2 = 0 For j = 1 To n - 1 Step 2 s1 = s1 + fn(a + j * h, theta) Next j For j = 2 To n - 2 Step 2 s2 = s2 + fn(a + j * h, theta) Next j Simpson = h / 3 * (fn(a, theta) + 4 * s1 + 2 * s2 + fn(b, theta)) End Function ' Specific calculation for the above integration Public Function fn(x As Double, theta As Double) As Double fn = Math.Sin(x) / (Math.Sqrt(Math.Sin(theta) - Math.Sin(x))) ' from reference {2} formula (12b) End Function ' Return the Complete Elliptic integral of the 1st kind ' Abramowitz and Stegun p.591, formula 17.3.11 ' Code from http://www.codeproject.com/Articles/566614/Elliptic-integrals Public Function EllipticK(ByVal m As Double) As Double Dim sum, term, above, below As Double sum = 1 term = 1 above = 1 below = 2 For i As Integer = 1 To 100 term *= above / below sum += Math.Pow(m, i) * Math.Pow(term, 2) above += 2 below += 2 Next sum *= 0.5 * Math.PI Return sum End Function ' Return the Complete Elliptic integral of the 2nd kind ' Abramowitz and Stegun p.591, formula 17.3.12 ' Code from http://www.codeproject.com/Articles/566614/Elliptic-integrals Public Function EllipticE(ByVal m As Double) As Double Dim sum, term, above, below As Double sum = 1 term = 1 above = 1 below = 2 For i As Integer = 1 To 100 term *= above / below sum -= Math.Pow(m, i) * Math.Pow(term, 2) / above above += 2 below += 2 Next sum *= 0.5 * Math.PI Return sum End Function Friend Partial NotInheritable Class Defined Private Sub New() End Sub ' Note: most of these values for m and h/L ratio were found with Wolfram Alpha and either specific intercepts (x=0) or local minima/maxima. They should be constant. Public Const M_SKETCHY As Double = 0.95 ' value of the m parameter where the curvature near the ends of the curve gets wonky Public Const M_MAX As Double = 0.993 ' maximum useful value of the m parameter, above which this algorithm for the form of the curve breaks down Public Const M_ZERO_W As Double = 0.826114765984970336 ' value of the m parameter when width = 0 Public Const M_MAXHEIGHT As Double = 0.701327460663101223 ' value of the m parameter at maximum possible height of the bent rod/wire Public Const M_DOUBLE_W As Double = 0.180254422335013983 ' minimum value of the m parameter when two width values are possible for a given height and length Public Const DOUBLE_W_HL_RATIO As Double = 0.257342117984635757 ' value of the height/length ratio above which there are two possible width values Public Const MAX_HL_RATIO As Double = 0.403140189705650243 ' maximum possible value of the height/length ratio Public Const MAXERR As Double = 0.0000000001 ' error tolerance Public Const MAXIT As Integer = 100 ' maximum number of iterations Public Const ROUNDTO As Integer = 10 ' number of decimal places to round off to Public Const CURVEDIVS As Integer = 50 ' number of sample points for building the curve (or half-curve as it were) End Class A VB.NET scriptable component 98 86 true bf1f4616-5fd9-426e-9474-52a076d17bf4 VB Script VB true 0 ' ----------------------------------------------------------------- ' Elastic Bending Script by Will McElwain ' Created February 2014 ' ' DESCRIPTION: ' This beast creates the so-called 'elastica curve', the shape a long, thin rod or wire makes when it is bent elastically (i.e. not permanently). In this case, force ' is assumed to only be applied horizontally (which would be in line with the rod at rest) and both ends are assumed to be pinned or hinged meaning they are free ' to rotate (as opposed to clamped, when the end tangent angle is fixed, usually horizontally). An interesting finding is that it doesn't matter what the material or ' cross-sectional area is, as long as they're uniform along the entire length. Everything makes the same shape when bent as long as it doesn't cross the threshold ' from elastic to plastic (permanent) deformation (I don't bother to find that limit here, but can be found if the yield stress for a material is known). ' ' Key to the formulas used in this script are elliptic integrals, specifically K(m), the complete elliptic integral of the first kind, and E(m), the complete elliptic ' integral of the second kind. There was a lot of confusion over the 'm' and 'k' parameters for these functions, as some people use them interchangeably, but they are ' not the same. m = k^2 (thus k = Sqrt(m)). I try to use the 'm' parameter exclusively to avoid this confusion. Note that there is a unique 'm' parameter for every ' configuration/shape of the elastica curve. ' ' This script tries to find that unique 'm' parameter based on the inputs. The algorithm starts with a test version of m, evaluates an expression, say 2*E(m)/K(m)-1, ' then compares the result to what it should be (in this case, a known width/length ratio). Iterate until the correct m is found. Once we have m, we can then calculate ' all of the other unknowns, then find points that lie on that curve, then interpolate those points for the actual curve. You can also use Wolfram|Alpha as I did to ' find the m parameter based on the equations in this script (example here: http://tiny.cc/t4tpbx for when say width=45.2 and length=67.1). ' ' Other notes: ' * This script works with negative values for width, which will creat a self-intersecting curve (as it should). The curvature of the elastica starts to break down around ' m=0.95 (~154°), but this script will continue to work until M_MAX, m=0.993 (~169°). If you wish to ignore self-intersecting curves, set ignoreSelfIntersecting to True ' * When the only known values are length and height, it is actually possible for certain ratios of height to length to have two valid m values (thus 2 possible widths ' and angles). This script will return them both. ' * Only the first two valid parameters (of the required ones) will be used, meaning if all four are connected (length, width or a PtB, height, and angle), this script will ' only use length and width (or a PtB). ' * Depending on the magnitude of your inputs (say if they're really small, like if length < 10), you might have to increase the constant ROUNDTO at the bottom ' ' REFERENCES: ' {1} "The elastic rod" by M.E. Pacheco Q. & E. Pina, http://www.scielo.org.mx/pdf/rmfe/v53n2/v53n2a8.pdf ' {2} "An experiment in nonlinear beam theory" by A. Valiente, http://www.deepdyve.com/lp/doc/I3lwnxdfGz , also here: http://tiny.cc/Valiente_AEiNBT ' {3} "Snap buckling, writhing and Loop formation In twisted rods" by V.G.A. GOSS, http://myweb.lsbu.ac.uk/~gossga/thesisFinal.pdf ' {4} "Theory of Elastic Stability" by Stephen Timoshenko, http://www.scribd.com/doc/50402462/Timoshenko-Theory-of-Elastic-Stability (start on p. 76) ' ' INPUT: ' PtA - First anchor point (required) ' PtB - Second anchor point (optional, though 2 out of the 4--length, width, height, angle--need to be specified) ' [note that PtB can be the same as PtA (meaning width would be zero)] ' [also note that if a different width is additionally specified that's not equal to the distance between PtA and PtB, then the end point will not equal PtB anymore] ' Pln - Plane of the bent rod/wire, which bends up in the +y direction. The line between PtA and PtB (if specified) must be parallel to the x-axis of this plane ' ' ** 2 of the following 4 need to be specified ** ' Len - Length of the rod/wire, which needs to be > 0 ' Wid - Width between the endpoints of the curve [note: if PtB is specified in addition, and distance between PtA and PtB <> width, the end point will be relocated ' Ht - Height of the bent rod/wire (when negative, curve will bend downward, relative to the input plane, instead) ' Ang - Inner departure angle or tangent angle (in radians) at the ends of the bent rod/wire. Set up so as width approaches length (thus height approaches zero), angle approaches zero ' ' * Following variables only needed for optional calculating of bending force, not for shape of curve. ' E - Young's modulus (modulus of elasticity) in GPa (=N/m^2) (material-specific. for example, 7075 aluminum is roughly 71.7 GPa) ' I - Second moment of area (or area moment of inertia) in m^4 (cross-section-specific. for example, a hollow rod ' would have I = pi * (outer_diameter^4 - inner_diameter^4) / 32 ' Note: E*I is also known as flexural rigidity or bending stiffness ' ' OUTPUT: ' out - only for debugging messages ' Pts - the list of points that approximate the shape of the elastica ' Crv - the 3rd-degree curve interpolated from those points (with accurate start & end tangents) ' L - the length of the rod/wire ' W - the distance (width) between the endpoints of the rod/wire ' H - the height of the bent rod/wire ' A - the tangent angle at the (start) end of the rod/wire ' F - the force needed to hold the rod/wire in a specific shape (based on the material properties & cross-section) **be sure your units for 'I' match your units for the ' rest of your inputs (length, width, etc.). Also note that the critical buckling load (force) that makes the rod/wire start to bend can be found at height=0 ' ' THANKS TO: ' Mårten Nettelbladt (thegeometryofbending.blogspot.com) ' Daniel Piker (Kangaroo plugin) ' David Rutten (Grasshopper guru) ' Euler & Bernoulli (the O.G.'s) ' ' ----------------------------------------------------------------- Dim ignoreSelfIntersecting As Boolean = False ' set to True if you don't want to output curves where width < 0, which creates a self-intersecting curve Dim inCt As Integer = 0 ' count the number of required parameters that are receiving data Dim length As Double Dim width As System.Object = Nothing ' need to set as Nothing so we can check if it has been assigned a value later Dim height As Double Dim angle As Double Dim m As Double Dim multiple_m As New List(Of Double) Dim AtoB As Line Dim flip_H As Boolean = False ' if height is negative, this flag will be set Dim flip_A As Boolean = False ' if angle is negative, this flag will be set If Not IsSet("Pln") Then Msg("error", "Base plane is not set") Return End If If Not IsSet("PtA") Then Msg("error", "Point A is not set") Return End If If Math.Round(Pln.DistanceTo(PtA), Defined.ROUNDTO) <> 0 Then Msg("error", "Point A is not on the base plane") Return End If Dim refPlane As Plane = Pln ' create a reference plane = input plane and set the origin of it to PtA in case PtA isn't the origin already refPlane.Origin = PtA If IsSet("PtB") Then If Math.Round(Pln.DistanceTo(PtB), Defined.ROUNDTO) <> 0 Then Msg("error", "Point B is not on the base plane") Return End If AtoB = New Line(PtA, PtB) If AtoB.Length <> 0 And Not AtoB.Direction.IsPerpendicularTo(Pln.YAxis) Then Msg("error", "The line between PtA and PtB is not perpendicular to the Y-axis of the specified plane") Return End If inCt += 1 If IsSet("Wid") Then Msg("info", "Wid will override the distance between PtA and PtB. If you do not want this to happen, disconnect PtB or Wid.") width = PtA.DistanceTo(PtB) ' get the width (distance) between PtA and PtB Dim refPtB As Point3d refPlane.RemapToPlaneSpace(PtB, refPtB) If refPtB.X < 0 Then width = -width ' check if PtB is to the left of PtA...if so, width is negative End If If IsSet("Len") Then inCt += 1 If IsSet("Wid") Then inCt += 1 If IsSet("Ht") Then inCt += 1 If IsSet("Ang") Then inCt += 1 If inCt > 2 Then Msg("info", "More parameters set than are required (out of length, width, height, angle). Only using the first two valid ones.") ' check for connected/specified inputs. note: only the first two that it comes across will be used If IsSet("Len") Then ' if length is specified then... If Len <= 0 Then Msg("error", "Length cannot be negative or zero") Return End If If IsSet("Wid") Then ' find height & angle based on length and specified width If Wid > Len Then Msg("error", "Width is greater than length") Return End If If Wid = Len Then ' skip the solver and set the known values height = 0 m = 0 angle = 0 width = Wid Else m = SolveMFromLenWid(Len, Wid) height = Cal_H(Len, m) ' L * Sqrt(m) / K(m) angle = Cal_A(m) ' Acos(1 - 2 * m) width = Wid End If Else If width IsNot Nothing Then ' find height & angle based on length and calculated width (distance between PtA and PtB) If width > Len Then Msg("error", "Width is greater than length") Return End If If width = Len Then ' skip the solver and set the known values height = 0 m = 0 angle = 0 Else m = SolveMFromLenWid(Len, width) height = Cal_H(Len, m) ' L * Sqrt(m) / K(m) angle = Cal_A(m) ' Acos(1 - 2 * m) End If Else If IsSet("Ht") Then ' find width & angle based on length and height ** possible to return 2 results ** If Math.Abs(Ht / Len) > Defined.MAX_HL_RATIO Then Msg("error", "Height not possible with given length") Return End If If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If If Ht = 0 Then ' skip the solver and set the known values width = Len angle = 0 Else multiple_m = SolveMFromLenHt(Len, Ht) ' note that it's possible for two values of m to be found if height is close to max height If multiple_m.Count = 1 Then ' if there's only one m value returned, calculate the width & angle here. we'll deal with multiple m values later m = multiple_m.Item(0) width = Cal_W(Len, m) ' L * (2 * E(m) / K(m) - 1) angle = Cal_A(m) ' Acos(1 - 2 * m) End If End If height = Ht Else If IsSet("Ang") Then ' find width & height based on length and angle If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then ' skip the solver and set the known values width = Len height = 0 Else width = Cal_W(Len, m) ' L * (2 * E(m) / K(m) - 1) height = Cal_H(Len, m) ' L * Sqrt(m) / K(m) End If angle = Ang Else Msg("error", "Need to specify one more parameter in addition to length") Return End If length = Len Else If IsSet("Wid") Then ' if width is specified then... If IsSet("Ht") Then ' find length & angle based on specified width and height If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If If Ht = 0 Then ' skip the solver and set the known values length = Wid angle = 0 Else m = SolveMFromWidHt(Wid, Ht) length = Cal_L(Ht, m) ' h * K(m) / Sqrt(m) angle = Cal_A(m) ' Acos(1 - 2 * m) End If height = Ht Else If IsSet("Ang") Then ' find length & height based on specified width and angle If Wid = 0 Then Msg("error", "Curve not possible with width = 0 and an angle as inputs") Return End If If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then ' skip the solver and set the known values length = Wid height = 0 Else length = Wid / (2 * EllipticE(m) / EllipticK(m) - 1) If length < 0 Then Msg("error", "Curve not possible at specified width and angle (calculated length is negative)") Return End If height = Cal_H(length, m) ' L * Sqrt(m) / K(m) End If angle = Ang Else Msg("error", "Need to specify one more parameter in addition to width (Wid)") Return End If width = Wid Else If width IsNot Nothing Then ' if width is determined by PtA and PtB then... If IsSet("Ht") Then ' find length & angle based on calculated width and height If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If If Ht = 0 Then ' skip the solver and set the known values length = width angle = 0 Else m = SolveMFromWidHt(width, Ht) length = Cal_L(Ht, m) ' h * K(m) / Sqrt(m) angle = Cal_A(m) ' Acos(1 - 2 * m) End If height = Ht Else If IsSet("Ang") Then ' find length & height based on calculated width and angle If width = 0 Then Msg("error", "Curve not possible with width = 0 and an angle as inputs") Return End If If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then ' skip the solver and set the known values length = width height = 0 Else length = width / (2 * EllipticE(m) / EllipticK(m) - 1) If length < 0 Then Msg("error", "Curve not possible at specified width and angle (calculated length is negative)") Return End If height = Cal_H(length, m) ' L * Sqrt(m) / K(m) End If angle = Ang Else Msg("error", "Need to specify one more parameter in addition to PtA and PtB") Return End If Else If IsSet("Ht") Then ' if height is specified then... If IsSet("Ang") Then ' find length & width based on height and angle If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_H = True flip_A = True End If If Ht = 0 Then Msg("error", "Height can't = 0 if only height and angle are specified") Return Else If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = Not flip_A flip_H = Not flip_H End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then Msg("error", "Angle can't = 0 if only height and angle are specified") Return Else length = Cal_L(Ht, m) ' h * K(m) / Sqrt(m) width = Cal_W(length, m) ' L * (2 * E(m) / K(m) - 1) End If angle = Ang End If height = Ht Else Msg("error", "Need to specify one more parameter in addition to height") Return End If Else If IsSet("Ang") Then Msg("error", "Need to specify one more parameter in addition to angle") Return Else Msg("error", "Need to specify two of the four parameters: length, width (or PtB), height, and angle") Return End If If m > Defined.M_MAX Then Msg("error", "Form of curve not solvable with current algorithm and given inputs") Return End If refPlane.Origin = refPlane.PointAt(width / 2, 0, 0) ' adjust the origin of the reference plane so that the curve is centered about the y-axis (start of the curve is at x = -width/2) If multiple_m.Count > 1 Then ' if there is more than one m value returned, calculate the width, angle, and curve for each Dim multi_pts As New DataTree(Of Point3d) Dim multi_crv As New List(Of Curve) Dim tmp_pts As New List(Of Point3d) Dim multi_W, multi_A, multi_F As New List(Of Double) Dim j As Integer = 0 ' used for creating a new branch (GH_Path) for storing pts which is itself a list of points For Each m_val As Double In multiple_m width = Cal_W(length, m_val) 'length * (2 * EllipticE(m_val) / EllipticK(m_val) - 1) If width < 0 And ignoreSelfIntersecting Then Msg("warning", "One curve is self-intersecting. To enable these, set ignoreSelfIntersecting to False") Continue For End If If m_val >= Defined.M_SKETCHY Then Msg("info", "Accuracy of the curve whose width = " & Math.Round(width, 4) & " is not guaranteed") angle = Cal_A(m_val) 'Math.Asin(2 * m_val - 1) refPlane.Origin = refPlane.PointAt(width / 2, 0, 0) ' adjust the origin of the reference plane so that the curve is centered about the y-axis (start of the curve is at x = -width/2) tmp_pts = FindBendForm(length, width, m_val, angle, refPlane) multi_pts.AddRange(tmp_pts, New GH_Path(j)) multi_crv.Add(MakeCurve(tmp_pts, angle, refPlane)) multi_W.Add(width) If flip_A Then angle = -angle multi_A.Add(angle) E = E * 10 ^ 9 ' Young's modulus input E is in GPa, so we convert to Pa here (= N/m^2) multi_F.Add(EllipticK(m_val) ^ 2 * E * I / length ^ 2) ' from reference {4} pg. 79 j += 1 refPlane.Origin = PtA ' reset the reference plane origin to PtA for the next m_val 'Print("length=" & length & ", width=" & width & ", height=" & height & ", angle=" & angle & ", m=" & m_val & ", k=" & Math.Sqrt(m_val) & ", w/L=" & width / length & ", h/L=" & height / length & ", w/h=" & width / height) Next ' assign the outputs Pts = multi_pts Crv = multi_crv L = length W = multi_W If flip_H Then height = -height H = height A = multi_A F = multi_F Else ' only deal with the single m value If m >= Defined.M_SKETCHY Then Msg("info", "Accuracy of the curve at these parameters is not guaranteed") If width < 0 And ignoreSelfIntersecting Then Msg("error", "Curve is self-intersecting. To enable these, set ignoreSelfIntersecting to False") Return End If Pts = FindBendForm(length, width, m, angle, refPlane) Crv = MakeCurve(pts, angle, refPlane) L = length W = width If flip_H Then height = -height H = height If flip_A Then angle = -angle A = angle E = E * 10 ^ 9 ' Young's modulus input E is in GPa, so we convert to Pa here (= N/m^2) F = EllipticK(m) ^ 2 * E * I / length ^ 2 ' from reference {4} pg. 79. Note: the critical buckling (that makes the rod/wire start to bend) can be found at height=0 (width=length) 'height = Math.Sqrt(((2 * Len / 5) ^ 2 - ((Wid - Len / 5) / 2) ^ 2) ' quick approximation discovered by Mårten of 'Geometry of Bending' fame ( http://tiny.cc/it2pbx ) 'width = (Len +/- 2 * Math.Sqrt(4 * Len ^ 2 - 25 * Ht ^ 2)) / 5 ' derived from above 'length = (2 * Math.Sqrt(15 * Ht ^ 2 + 4 * Wid ^ 2) - Wid) / 3 ' derived from above 'Print("length=" & length & ", width=" & width & ", height=" & height & ", angle=" & angle & ", m=" & m & ", k=" & Math.Sqrt(m) & ", w/L=" & width / length & ", h/L=" & height / length & ", w/h=" & width / height) End If 618 1502 84 184 660 1594 9 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 8 3ede854e-c753-40eb-84cb-b48008f14fd4 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 true Script Variable PtA fc00bec5-e331-4012-b0a8-a6f9d0f686f7 PtA PtA true 0 true 7470aaae-fe5c-4a6e-a5d7-3c8c950bb9fb 1 e1937b56-b1da-4c12-8bd8-e34ee81746ef 620 1504 25 20 634 1514 true Script Variable PtB c5bc96c5-9e28-4cb8-9259-356c9db2b9fb PtB PtB true 0 true 0 e1937b56-b1da-4c12-8bd8-e34ee81746ef 620 1524 25 20 634 1534 true Script Variable Pln 71f32d80-b186-4e03-b761-b9bb960ea743 Pln Pln true 0 true c7f844d2-ba5e-476f-b4d4-193dcea7a216 1 3897522d-58e9-4d60-b38c-978ddacfedd8 620 1544 25 20 634 1554 true Script Variable Len 0aa34a2c-a64a-42a7-9ee2-ff1e67f56177 Len Len true 0 true 7d11a12d-1f6f-4777-9bc7-965dd3035809 1 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 620 1564 25 20 634 1574 true Script Variable Wid 1af65384-240e-46e3-8309-6fc1ae7504d7 Wid Wid true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 620 1584 25 20 634 1594 true Script Variable Ht 8d0b03a7-827e-4902-8b4f-5bb0e815b41b Ht Ht true 0 true d1327bed-c875-4acd-82dd-5fed4b45b311 1 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 620 1604 25 20 634 1614 true Script Variable Ang 2deaf321-2e08-44e8-a80f-9628891997a5 Ang Ang true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 620 1624 25 20 634 1634 true Script Variable E 8dff382d-9d65-40bc-ac45-64afb28a006d E E true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 620 1644 25 20 634 1654 true Script Variable I 33d1c654-7377-4887-9445-52cefe06021d I I true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 620 1664 25 20 634 1674 1 Print, Reflect and Error streams 964ae01d-510e-42cb-b06f-38805376764d out out false 0 675 1504 25 22 687.5 1515.25 Output parameter Pts 17f99969-6a6b-431e-8210-f6e15df9a9af Pts Pts false 0 675 1526 25 23 687.5 1537.75 Output parameter Crv 6548d62a-dfa5-4478-9f16-8d4fb96732c9 Crv Crv false 0 675 1549 25 22 687.5 1560.25 Output parameter L a2a9f9ae-744d-4886-a3ee-4c33b8365028 L L false 0 675 1571 25 23 687.5 1582.75 Output parameter W 0c900729-ed6e-40e8-809f-e2432521ac54 W W false 0 675 1594 25 22 687.5 1605.25 Output parameter H 7772d1f7-9786-4d55-8690-af9d6877a777 H H false 0 675 1616 25 23 687.5 1627.75 Output parameter A bc883717-6d8a-41a6-bb0b-3d7c5d7a61ea A A false 0 675 1639 25 22 687.5 1650.25 Output parameter F fc8245e0-b2d0-471d-9254-01a0eff7a0b4 F F false 0 675 1661 25 23 687.5 1672.75 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values 7d11a12d-1f6f-4777-9bc7-965dd3035809 Number Slider length false 0 163 1555 382 20 163.9633 1555.108 2 1 0 400 0 0 225 fbac3e32-f100-4292-8692-77240a42fd1a Point Contains a collection of three-dimensional points true 108e145a-a9f8-4c1a-856f-eef3be9eef4b Point Pt false 17f99969-6a6b-431e-8210-f6e15df9a9af 1 788 1463 50 24 813.4178 1475.827 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values de6e3b3a-9675-45ae-ad2f-bfc0988f9582 Panel false 0 a0d9174c-2b33-4845-86da-70d722e564b7 1 Double click to edit panel content… 846 1678 105 55 0 0 0 846.2736 1678.188 255;255;250;90 true true true false false true 0d77c51e-584f-44e8-aed2-c2ddf4803888 Degrees Convert an angle specified in radians to degrees 9bc218b0-faa5-4566-a292-b5565b343ee8 Degrees Deg 759 1690 64 28 789 1704 Angle in radians 8dd5ffda-e6b3-4328-a1c1-726386b31dd7 Radians R false bc883717-6d8a-41a6-bb0b-3d7c5d7a61ea 1 761 1692 13 24 769 1704 Angle in degrees a0d9174c-2b33-4845-86da-70d722e564b7 Degrees D false 0 804 1692 17 24 812.5 1704 3581f42a-9592-4549-bd6b-1c0fc39d067b Construct Point Construct a point from {xyz} coordinates. 5bb8544d-21ed-430d-b9a1-3d097d9c0f85 Construct Point Pt 474 1440 67 64 505 1472 {x} coordinate fd05bf19-e5b9-428a-a0b7-1183ddb4d4cb X coordinate X false 18da725f-8fce-4b5c-bfc0-b35f534747f4 1 476 1442 14 20 484.5 1452 1 1 {0} 0 {y} coordinate 5d500e04-4c0a-4e2e-90e3-a69f12e6e56b Y coordinate Y false bdda70a3-abe5-47d3-bab1-d54eaac87273 1 476 1462 14 20 484.5 1472 1 1 {0} 0 {z} coordinate b73aa23e-da5e-429b-a2ad-c29573433d8d Z coordinate Z false 0 476 1482 14 20 484.5 1492 1 1 {0} 0 Point coordinate 7470aaae-fe5c-4a6e-a5d7-3c8c950bb9fb Point Pt false 0 520 1442 19 60 529.5 1472 d5967b9f-e8ee-436b-a8ad-29fdcecf32d5 Curve Contains a collection of generic curves d707c7a6-ce24-45bb-a3b3-c616b13d07dc Curve Crv false 6548d62a-dfa5-4478-9f16-8d4fb96732c9 1 788 1506 50 24 813.4067 1518.357 17b7152b-d30d-4d50-b9ef-c9fe25576fc2 XY Plane World XY plane. true 1653bea2-6780-45be-ad4a-2c29f59e4c19 XY Plane XY 479 1515 64 28 510 1529 Origin of plane 6ccdcfe2-c623-42d1-8da5-1e99c86f6f52 Origin O false 0 481 1517 14 24 489.5 1529 1 1 {0} 0 0 0 World XY plane c7f844d2-ba5e-476f-b4d4-193dcea7a216 Plane P false 0 525 1517 16 24 533 1529 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values d1327bed-c875-4acd-82dd-5fed4b45b311 Number Slider height false 0 168 1608 381 20 168.731 1608.997 2 1 0 200 0 0 89 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values e3db26aa-60fb-43b8-8092-d7ecfc283153 Panel false 0 a2a9f9ae-744d-4886-a3ee-4c33b8365028 1 Double click to edit panel content… 743 1557 97 38 0 0 0 743.3972 1557.754 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values dfa914e0-e01f-4bbe-b66d-a9d4a08bb5c7 Panel false 0 0c900729-ed6e-40e8-809f-e2432521ac54 1 Double click to edit panel content… 854 1582 105 55 0 0 0 854.3154 1582.622 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values b487f9ab-5cec-404a-9235-d2a5f0e79007 Panel false 0 7772d1f7-9786-4d55-8690-af9d6877a777 1 Double click to edit panel content… 742 1618 97 38 0 0 0 742.3411 1618.138 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values bdda70a3-abe5-47d3-bab1-d54eaac87273 Panel false 0 0 -150 378 1465 50 20 0 0 0 378.501 1465.184 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 18da725f-8fce-4b5c-bfc0-b35f534747f4 Panel false 0 0 -150 377 1434 50 20 0 0 0 377.701 1434.784 255;255;250;90 true true true false false true 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe Scribble true 1685.887 79.1758 1984.071 81.3421 1983.831 114.478 1685.647 112.3117 A quick note Microsoft Sans Serif 18f8db3f-bb9b-4a79-9d98-1a46a8a0c59b false Scribble Scribble 16 Negative width = self-intersecting result Negative height and angle work too 1680.647 74.1758 308.4244 45.30215 1685.887 79.1758 079bd9bd-54a0-41d4-98af-db999015f63d VB Script Private Function IsSet(ByVal param As String) As Boolean ' Check if an input parameter has data Dim i As Integer = Component.Params.IndexOfInputParam(param) If i > -1 Then Return Component.Params.Input.ElementAt(i).DataType > 1 ' input parameter DataType of 1 means it's not receiving input (internal or external) Else Msg("error", "Input parameter '" & param & "' not found") Return False End If End Function Private Sub Msg(ByVal type As String, ByVal msg As String) ' Output an error, warning, or informational message Select Case type Case "error" Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, msg) Print("Error: " & msg) Case "warning" Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, msg) Print("Warning: " & msg) Case "info" Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, msg) Print(msg) End Select End Sub ' Solve for the m parameter from length and width (reference {1} equation (34), except b = width and K(k) and E(k) should be K(m) and E(m)) Private Function SolveMFromLenWid(ByVal L As Double, ByVal w As Double) As Double If w = 0 Then Return Defined.M_ZERO_W ' for the boundry condition width = 0, bypass the function and return the known m value End If Dim n As Integer = 1 ' Iteration counter (quit if >MAXIT) Dim lower As Double = 0 ' m must be within this range Dim upper As Double = 1 Dim m As Double Dim cwl As Double Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 cwl = 2 * EllipticE(m) / EllipticK(m) - 1 ' calculate w/L with the test value of m If cwl < w / L Then ' compares the calculated w/L with the actual w/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop Return m End Function ' Solve for the m parameter from length and height (reference {1} equation (33), except K(k) should be K(m) and k = sqrt(m)) ' Note that it's actually possible to find 2 valid values for m (hence 2 width values) at certain height values Private Function SolveMFromLenHt(ByVal L As Double, ByVal h As Double) As List(Of Double) Dim n As Integer = 1 ' Iteration counter (quit if >MAXIT) Dim lower As Double = 0 ' m must be within this range Dim upper As Double = 1 Dim twoWidths As Boolean = h / L >= Defined.DOUBLE_W_HL_RATIO And h / L < Defined.MAX_HL_RATIO ' check to see if h/L is within the range where 2 solutions for the width are possible Dim m As Double Dim mult_m As New List(Of Double) Dim chl As Double If twoWidths Then ' find the first of two possible solutions for m with the following limits: lower = Defined.M_DOUBLE_W ' see constants at bottom of script upper = Defined.M_MAXHEIGHT ' see constants at bottom of script Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 chl = Math.Sqrt(m) / EllipticK(m) ' calculate h/L with the test value of m If chl > h / L Then ' compares the calculated h/L with the actual h/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop mult_m.Add(m) ' then find the second of two possible solutions for m with the following limits: lower = Defined.M_MAXHEIGHT ' see constants at bottom of script upper = 1 Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 chl = Math.Sqrt(m) / EllipticK(m) ' calculate h/L with the test value of m If chl < h / L Then ' compares the calculated h/L with the actual h/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop If m <= Defined.M_MAX Then ' return this m parameter only if it falls within the maximum useful value (above which the curve breaks down) mult_m.Add(m) End If Else ' find the one possible solution for the m parameter upper = Defined.M_DOUBLE_W ' limit the upper end of the search to the maximum value of m for which only one solution exists Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 chl = Math.Sqrt(m) / EllipticK(m) ' calculate h/L with the test value of m If chl > h / L Then ' compares the calculated h/L with the actual h/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop mult_m.Add(m) End If Return mult_m End Function ' Solve for the m parameter from width and height (derived from reference {1} equations (33) and (34) with same notes as above) Private Function SolveMFromWidHt(ByVal w As Double, ByVal h As Double) As Double Dim n As Integer = 1 ' Iteration counter (quit if >MAXIT) Dim lower As Double = 0 ' m must be within this range Dim upper As Double = 1 Dim m As Double Dim cwh As Double Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 cwh = (2 * EllipticE(m) - EllipticK(m)) / Math.Sqrt(m) ' calculate w/h with the test value of m If cwh < w / h Then ' compares the calculated w/h with the actual w/h then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop Return m End Function ' Calculate length based on height and an m parameter, derived from reference {1} equation (33), except K(k) should be K(m) and k = sqrt(m) Private Function Cal_L(ByVal h As Double, ByVal m As Double) As Double Return h * EllipticK(m) / Math.Sqrt(m) End Function ' Calculate width based on length and an m parameter, derived from reference {1} equation (34), except b = width and K(k) and E(k) should be K(m) and E(m) Private Function Cal_W(ByVal L As Double, ByVal m As Double) As Double Return L * (2 * EllipticE(m) / EllipticK(m) - 1) End Function ' Calculate height based on length and an m parameter, from reference {1} equation (33), except K(k) should be K(m) and k = sqrt(m) Private Function Cal_H(ByVal L As Double, ByVal m As Double) As Double Return L * Math.Sqrt(m) / EllipticK(m) End Function ' Calculate the unique m parameter based on a start tangent angle, from reference {2}, just above equation (9a), that states k = Sin(angle / 2 + Pi / 4), ' but as m = k^2 and due to this script's need for an angle rotated 90° versus the one in reference {1}, the following formula is the result ' New note: verified by reference {4}, pg. 78 at the bottom Private Function Cal_M(ByVal a As Double) As Double Return (1 - Math.Cos(a)) / 2 ' equal to Sin^2(a/2) too End Function ' Calculate start tangent angle based on an m parameter, derived from above formula Private Function Cal_A(ByVal m As Double) As Double Return Math.Acos(1 - 2 * m) End Function ' This is the heart of this script, taking the found (or specified) length, width, and angle values along with the found m parameter to create ' a list of points that approximate the shape or form of the elastica. It works by finding the x and y coordinates (which are reversed versus ' the original equations (12a) and (12b) from reference {2} due to the 90° difference in orientation) based on the tangent angle along the curve. ' See reference {2} for more details on how they derived it. Note that to simplify things, the algorithm only calculates the points for half of the ' curve, then mirrors those points along the y-axis. Private Function FindBendForm(ByVal L As Double, ByVal w As Double, ByVal m As Double, ByVal ang As Double, ByVal refPln As Plane) As List(Of Point3d) L = L / 2 ' because the below algorithm is based on the formulas in reference {2} for only half of the curve w = w / 2 ' same If ang = 0 Then ' if angle (and height) = 0, then simply return the start and end points of the straight line Dim out As New List(Of Point3d) out.Add(refPln.PointAt(w, 0, 0)) out.Add(refPln.PointAt(-w, 0, 0)) Return out End If Dim x As Double Dim y As Double Dim halfCurvePts As New List(Of Point3d) Dim fullCurvePts As New List(Of Point3d) Dim translatedPts As New List(Of Point3d) ang -= Math.PI / 2 ' a hack to allow this algorithm to work, since the original curve in paper {2} was rotated 90° Dim angB As Double = ang + (-Math.PI / 2 - ang) / Defined.CURVEDIVS ' angB is the 'lowercase theta' which should be in formula {2}(12b) as the interval ' start [a typo...see equation(3)]. It's necessary to start angB at ang + [interval] instead of just ang due to integration failing at angB = ang halfCurvePts.Add(New Point3d(w, 0, 0)) ' start with this known initial point, as integration will fail when angB = ang ' each point {x, y} is calculated from the tangent angle, angB, that occurs at each point (which is why this iterates from ~ang to -pi/2, the known end condition) Do While Math.Round(angB, Defined.ROUNDTO) >= Math.Round(-Math.PI / 2, Defined.ROUNDTO) y = (Math.Sqrt(2) * Math.Sqrt(Math.Sin(ang) - Math.Sin(angB)) * (w + L)) / (2 * EllipticE(m)) ' note that x and y are swapped vs. (12a) and (12b) x = (L / (Math.Sqrt(2) * EllipticK(m))) * Simpson(angB, -Math.PI / 2, 500, ang) ' calculate the Simpson approximation of the integral (function f below) ' over the interval angB ('lowercase theta') to -pi/2. side note: is 500 too few iterations for the Simson algorithm? If Math.Round(x, Defined.ROUNDTO) = 0 Then x = 0 halfCurvePts.Add(New Point3d(x, y, 0)) angB += (-Math.PI / 2 - ang) / Defined.CURVEDIVS ' onto the next tangent angle Loop ' After finding the x and y values for half of the curve, add the {-x, y} values for the rest of the curve For Each point As Point3d In halfCurvePts If Math.Round(point.X, Defined.ROUNDTO) = 0 Then If Math.Round(point.Y, Defined.ROUNDTO) = 0 Then fullCurvePts.Add(New Point3d(0, 0, 0)) ' special case when width = 0: when x = 0, only duplicate the point when y = 0 too End If Else fullCurvePts.Add(New Point3d(-point.X, point.Y, 0)) End If Next halfCurvePts.Reverse fullCurvePts.AddRange(halfCurvePts) For Each p As Point3d In fullCurvePts translatedPts.Add(refPln.PointAt(p.X, p.Y, p.Z)) ' translate the points from the reference plane to the world plane Next Return translatedPts End Function ' Interpolates the points from FindBendForm to create the Elastica curve. Uses start & end tangents for greater accuracy. Private Function MakeCurve(ByVal pts As List(Of Point3d), ByVal ang As Double, ByVal refPln As Plane) As Curve If ang <> 0 Then Dim ts, te As New Vector3d(refPln.XAxis) ts.Rotate(ang, refPln.ZAxis) te.Rotate(-ang, refPln.ZAxis) Return Curve.CreateInterpolatedCurve(pts, 3, CurveKnotStyle.Chord, ts, te) ' 3rd degree curve with 'Chord' Knot Style Else Return Curve.CreateInterpolatedCurve(pts, 3) ' if angle (and height) = 0, then simply interpolate the straight line (no start/end tangents) End If End Function ' Implements the Simpson approximation for an integral of function f below Public Function Simpson(a As Double, b As Double, n As Integer, theta As Double) As Double 'n should be an even number Dim j As Integer, s1 As Double, s2 As Double, h As Double h = (b - a) / n s1 = 0 s2 = 0 For j = 1 To n - 1 Step 2 s1 = s1 + fn(a + j * h, theta) Next j For j = 2 To n - 2 Step 2 s2 = s2 + fn(a + j * h, theta) Next j Simpson = h / 3 * (fn(a, theta) + 4 * s1 + 2 * s2 + fn(b, theta)) End Function ' Specific calculation for the above integration Public Function fn(x As Double, theta As Double) As Double fn = Math.Sin(x) / (Math.Sqrt(Math.Sin(theta) - Math.Sin(x))) ' from reference {2} formula (12b) End Function ' Return the Complete Elliptic integral of the 1st kind ' Abramowitz and Stegun p.591, formula 17.3.11 ' Code from http://www.codeproject.com/Articles/566614/Elliptic-integrals Public Function EllipticK(ByVal m As Double) As Double Dim sum, term, above, below As Double sum = 1 term = 1 above = 1 below = 2 For i As Integer = 1 To 100 term *= above / below sum += Math.Pow(m, i) * Math.Pow(term, 2) above += 2 below += 2 Next sum *= 0.5 * Math.PI Return sum End Function ' Return the Complete Elliptic integral of the 2nd kind ' Abramowitz and Stegun p.591, formula 17.3.12 ' Code from http://www.codeproject.com/Articles/566614/Elliptic-integrals Public Function EllipticE(ByVal m As Double) As Double Dim sum, term, above, below As Double sum = 1 term = 1 above = 1 below = 2 For i As Integer = 1 To 100 term *= above / below sum -= Math.Pow(m, i) * Math.Pow(term, 2) / above above += 2 below += 2 Next sum *= 0.5 * Math.PI Return sum End Function Friend Partial NotInheritable Class Defined Private Sub New() End Sub ' Note: most of these values for m and h/L ratio were found with Wolfram Alpha and either specific intercepts (x=0) or local minima/maxima. They should be constant. Public Const M_SKETCHY As Double = 0.95 ' value of the m parameter where the curvature near the ends of the curve gets wonky Public Const M_MAX As Double = 0.993 ' maximum useful value of the m parameter, above which this algorithm for the form of the curve breaks down Public Const M_ZERO_W As Double = 0.826114765984970336 ' value of the m parameter when width = 0 Public Const M_MAXHEIGHT As Double = 0.701327460663101223 ' value of the m parameter at maximum possible height of the bent rod/wire Public Const M_DOUBLE_W As Double = 0.180254422335013983 ' minimum value of the m parameter when two width values are possible for a given height and length Public Const DOUBLE_W_HL_RATIO As Double = 0.257342117984635757 ' value of the height/length ratio above which there are two possible width values Public Const MAX_HL_RATIO As Double = 0.403140189705650243 ' maximum possible value of the height/length ratio Public Const MAXERR As Double = 0.0000000001 ' error tolerance Public Const MAXIT As Integer = 100 ' maximum number of iterations Public Const ROUNDTO As Integer = 10 ' number of decimal places to round off to Public Const CURVEDIVS As Integer = 50 ' number of sample points for building the curve (or half-curve as it were) End Class A VB.NET scriptable component 98 86 true 15a333e8-a6e9-40f9-ae49-542ab7d2e084 VB Script VB true 0 ' ----------------------------------------------------------------- ' Elastic Bending Script by Will McElwain ' Created February 2014 ' ' DESCRIPTION: ' This beast creates the so-called 'elastica curve', the shape a long, thin rod or wire makes when it is bent elastically (i.e. not permanently). In this case, force ' is assumed to only be applied horizontally (which would be in line with the rod at rest) and both ends are assumed to be pinned or hinged meaning they are free ' to rotate (as opposed to clamped, when the end tangent angle is fixed, usually horizontally). An interesting finding is that it doesn't matter what the material or ' cross-sectional area is, as long as they're uniform along the entire length. Everything makes the same shape when bent as long as it doesn't cross the threshold ' from elastic to plastic (permanent) deformation (I don't bother to find that limit here, but can be found if the yield stress for a material is known). ' ' Key to the formulas used in this script are elliptic integrals, specifically K(m), the complete elliptic integral of the first kind, and E(m), the complete elliptic ' integral of the second kind. There was a lot of confusion over the 'm' and 'k' parameters for these functions, as some people use them interchangeably, but they are ' not the same. m = k^2 (thus k = Sqrt(m)). I try to use the 'm' parameter exclusively to avoid this confusion. Note that there is a unique 'm' parameter for every ' configuration/shape of the elastica curve. ' ' This script tries to find that unique 'm' parameter based on the inputs. The algorithm starts with a test version of m, evaluates an expression, say 2*E(m)/K(m)-1, ' then compares the result to what it should be (in this case, a known width/length ratio). Iterate until the correct m is found. Once we have m, we can then calculate ' all of the other unknowns, then find points that lie on that curve, then interpolate those points for the actual curve. You can also use Wolfram|Alpha as I did to ' find the m parameter based on the equations in this script (example here: http://tiny.cc/t4tpbx for when say width=45.2 and length=67.1). ' ' Other notes: ' * This script works with negative values for width, which will creat a self-intersecting curve (as it should). The curvature of the elastica starts to break down around ' m=0.95 (~154°), but this script will continue to work until M_MAX, m=0.993 (~169°). If you wish to ignore self-intersecting curves, set ignoreSelfIntersecting to True ' * When the only known values are length and height, it is actually possible for certain ratios of height to length to have two valid m values (thus 2 possible widths ' and angles). This script will return them both. ' * Only the first two valid parameters (of the required ones) will be used, meaning if all four are connected (length, width or a PtB, height, and angle), this script will ' only use length and width (or a PtB). ' * Depending on the magnitude of your inputs (say if they're really small, like if length < 10), you might have to increase the constant ROUNDTO at the bottom ' ' REFERENCES: ' {1} "The elastic rod" by M.E. Pacheco Q. & E. Pina, http://www.scielo.org.mx/pdf/rmfe/v53n2/v53n2a8.pdf ' {2} "An experiment in nonlinear beam theory" by A. Valiente, http://www.deepdyve.com/lp/doc/I3lwnxdfGz , also here: http://tiny.cc/Valiente_AEiNBT ' {3} "Snap buckling, writhing and Loop formation In twisted rods" by V.G.A. GOSS, http://myweb.lsbu.ac.uk/~gossga/thesisFinal.pdf ' {4} "Theory of Elastic Stability" by Stephen Timoshenko, http://www.scribd.com/doc/50402462/Timoshenko-Theory-of-Elastic-Stability (start on p. 76) ' ' INPUT: ' PtA - First anchor point (required) ' PtB - Second anchor point (optional, though 2 out of the 4--length, width, height, angle--need to be specified) ' [note that PtB can be the same as PtA (meaning width would be zero)] ' [also note that if a different width is additionally specified that's not equal to the distance between PtA and PtB, then the end point will not equal PtB anymore] ' Pln - Plane of the bent rod/wire, which bends up in the +y direction. The line between PtA and PtB (if specified) must be parallel to the x-axis of this plane ' ' ** 2 of the following 4 need to be specified ** ' Len - Length of the rod/wire, which needs to be > 0 ' Wid - Width between the endpoints of the curve [note: if PtB is specified in addition, and distance between PtA and PtB <> width, the end point will be relocated ' Ht - Height of the bent rod/wire (when negative, curve will bend downward, relative to the input plane, instead) ' Ang - Inner departure angle or tangent angle (in radians) at the ends of the bent rod/wire. Set up so as width approaches length (thus height approaches zero), angle approaches zero ' ' * Following variables only needed for optional calculating of bending force, not for shape of curve. ' E - Young's modulus (modulus of elasticity) in GPa (=N/m^2) (material-specific. for example, 7075 aluminum is roughly 71.7 GPa) ' I - Second moment of area (or area moment of inertia) in m^4 (cross-section-specific. for example, a hollow rod ' would have I = pi * (outer_diameter^4 - inner_diameter^4) / 32 ' Note: E*I is also known as flexural rigidity or bending stiffness ' ' OUTPUT: ' out - only for debugging messages ' Pts - the list of points that approximate the shape of the elastica ' Crv - the 3rd-degree curve interpolated from those points (with accurate start & end tangents) ' L - the length of the rod/wire ' W - the distance (width) between the endpoints of the rod/wire ' H - the height of the bent rod/wire ' A - the tangent angle at the (start) end of the rod/wire ' F - the force needed to hold the rod/wire in a specific shape (based on the material properties & cross-section) **be sure your units for 'I' match your units for the ' rest of your inputs (length, width, etc.). Also note that the critical buckling load (force) that makes the rod/wire start to bend can be found at height=0 ' ' THANKS TO: ' Mårten Nettelbladt (thegeometryofbending.blogspot.com) ' Daniel Piker (Kangaroo plugin) ' David Rutten (Grasshopper guru) ' Euler & Bernoulli (the O.G.'s) ' ' ----------------------------------------------------------------- Dim ignoreSelfIntersecting As Boolean = False ' set to True if you don't want to output curves where width < 0, which creates a self-intersecting curve Dim inCt As Integer = 0 ' count the number of required parameters that are receiving data Dim length As Double Dim width As System.Object = Nothing ' need to set as Nothing so we can check if it has been assigned a value later Dim height As Double Dim angle As Double Dim m As Double Dim multiple_m As New List(Of Double) Dim AtoB As Line Dim flip_H As Boolean = False ' if height is negative, this flag will be set Dim flip_A As Boolean = False ' if angle is negative, this flag will be set If Not IsSet("Pln") Then Msg("error", "Base plane is not set") Return End If If Not IsSet("PtA") Then Msg("error", "Point A is not set") Return End If If Math.Round(Pln.DistanceTo(PtA), Defined.ROUNDTO) <> 0 Then Msg("error", "Point A is not on the base plane") Return End If Dim refPlane As Plane = Pln ' create a reference plane = input plane and set the origin of it to PtA in case PtA isn't the origin already refPlane.Origin = PtA If IsSet("PtB") Then If Math.Round(Pln.DistanceTo(PtB), Defined.ROUNDTO) <> 0 Then Msg("error", "Point B is not on the base plane") Return End If AtoB = New Line(PtA, PtB) If AtoB.Length <> 0 And Not AtoB.Direction.IsPerpendicularTo(Pln.YAxis) Then Msg("error", "The line between PtA and PtB is not perpendicular to the Y-axis of the specified plane") Return End If inCt += 1 If IsSet("Wid") Then Msg("info", "Wid will override the distance between PtA and PtB. If you do not want this to happen, disconnect PtB or Wid.") width = PtA.DistanceTo(PtB) ' get the width (distance) between PtA and PtB Dim refPtB As Point3d refPlane.RemapToPlaneSpace(PtB, refPtB) If refPtB.X < 0 Then width = -width ' check if PtB is to the left of PtA...if so, width is negative End If If IsSet("Len") Then inCt += 1 If IsSet("Wid") Then inCt += 1 If IsSet("Ht") Then inCt += 1 If IsSet("Ang") Then inCt += 1 If inCt > 2 Then Msg("info", "More parameters set than are required (out of length, width, height, angle). Only using the first two valid ones.") ' check for connected/specified inputs. note: only the first two that it comes across will be used If IsSet("Len") Then ' if length is specified then... If Len <= 0 Then Msg("error", "Length cannot be negative or zero") Return End If If IsSet("Wid") Then ' find height & angle based on length and specified width If Wid > Len Then Msg("error", "Width is greater than length") Return End If If Wid = Len Then ' skip the solver and set the known values height = 0 m = 0 angle = 0 width = Wid Else m = SolveMFromLenWid(Len, Wid) height = Cal_H(Len, m) ' L * Sqrt(m) / K(m) angle = Cal_A(m) ' Acos(1 - 2 * m) width = Wid End If Else If width IsNot Nothing Then ' find height & angle based on length and calculated width (distance between PtA and PtB) If width > Len Then Msg("error", "Width is greater than length") Return End If If width = Len Then ' skip the solver and set the known values height = 0 m = 0 angle = 0 Else m = SolveMFromLenWid(Len, width) height = Cal_H(Len, m) ' L * Sqrt(m) / K(m) angle = Cal_A(m) ' Acos(1 - 2 * m) End If Else If IsSet("Ht") Then ' find width & angle based on length and height ** possible to return 2 results ** If Math.Abs(Ht / Len) > Defined.MAX_HL_RATIO Then Msg("error", "Height not possible with given length") Return End If If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If If Ht = 0 Then ' skip the solver and set the known values width = Len angle = 0 Else multiple_m = SolveMFromLenHt(Len, Ht) ' note that it's possible for two values of m to be found if height is close to max height If multiple_m.Count = 1 Then ' if there's only one m value returned, calculate the width & angle here. we'll deal with multiple m values later m = multiple_m.Item(0) width = Cal_W(Len, m) ' L * (2 * E(m) / K(m) - 1) angle = Cal_A(m) ' Acos(1 - 2 * m) End If End If height = Ht Else If IsSet("Ang") Then ' find width & height based on length and angle If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then ' skip the solver and set the known values width = Len height = 0 Else width = Cal_W(Len, m) ' L * (2 * E(m) / K(m) - 1) height = Cal_H(Len, m) ' L * Sqrt(m) / K(m) End If angle = Ang Else Msg("error", "Need to specify one more parameter in addition to length") Return End If length = Len Else If IsSet("Wid") Then ' if width is specified then... If IsSet("Ht") Then ' find length & angle based on specified width and height If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If If Ht = 0 Then ' skip the solver and set the known values length = Wid angle = 0 Else m = SolveMFromWidHt(Wid, Ht) length = Cal_L(Ht, m) ' h * K(m) / Sqrt(m) angle = Cal_A(m) ' Acos(1 - 2 * m) End If height = Ht Else If IsSet("Ang") Then ' find length & height based on specified width and angle If Wid = 0 Then Msg("error", "Curve not possible with width = 0 and an angle as inputs") Return End If If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then ' skip the solver and set the known values length = Wid height = 0 Else length = Wid / (2 * EllipticE(m) / EllipticK(m) - 1) If length < 0 Then Msg("error", "Curve not possible at specified width and angle (calculated length is negative)") Return End If height = Cal_H(length, m) ' L * Sqrt(m) / K(m) End If angle = Ang Else Msg("error", "Need to specify one more parameter in addition to width (Wid)") Return End If width = Wid Else If width IsNot Nothing Then ' if width is determined by PtA and PtB then... If IsSet("Ht") Then ' find length & angle based on calculated width and height If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If If Ht = 0 Then ' skip the solver and set the known values length = width angle = 0 Else m = SolveMFromWidHt(width, Ht) length = Cal_L(Ht, m) ' h * K(m) / Sqrt(m) angle = Cal_A(m) ' Acos(1 - 2 * m) End If height = Ht Else If IsSet("Ang") Then ' find length & height based on calculated width and angle If width = 0 Then Msg("error", "Curve not possible with width = 0 and an angle as inputs") Return End If If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then ' skip the solver and set the known values length = width height = 0 Else length = width / (2 * EllipticE(m) / EllipticK(m) - 1) If length < 0 Then Msg("error", "Curve not possible at specified width and angle (calculated length is negative)") Return End If height = Cal_H(length, m) ' L * Sqrt(m) / K(m) End If angle = Ang Else Msg("error", "Need to specify one more parameter in addition to PtA and PtB") Return End If Else If IsSet("Ht") Then ' if height is specified then... If IsSet("Ang") Then ' find length & width based on height and angle If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_H = True flip_A = True End If If Ht = 0 Then Msg("error", "Height can't = 0 if only height and angle are specified") Return Else If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = Not flip_A flip_H = Not flip_H End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then Msg("error", "Angle can't = 0 if only height and angle are specified") Return Else length = Cal_L(Ht, m) ' h * K(m) / Sqrt(m) width = Cal_W(length, m) ' L * (2 * E(m) / K(m) - 1) End If angle = Ang End If height = Ht Else Msg("error", "Need to specify one more parameter in addition to height") Return End If Else If IsSet("Ang") Then Msg("error", "Need to specify one more parameter in addition to angle") Return Else Msg("error", "Need to specify two of the four parameters: length, width (or PtB), height, and angle") Return End If If m > Defined.M_MAX Then Msg("error", "Form of curve not solvable with current algorithm and given inputs") Return End If refPlane.Origin = refPlane.PointAt(width / 2, 0, 0) ' adjust the origin of the reference plane so that the curve is centered about the y-axis (start of the curve is at x = -width/2) If multiple_m.Count > 1 Then ' if there is more than one m value returned, calculate the width, angle, and curve for each Dim multi_pts As New DataTree(Of Point3d) Dim multi_crv As New List(Of Curve) Dim tmp_pts As New List(Of Point3d) Dim multi_W, multi_A, multi_F As New List(Of Double) Dim j As Integer = 0 ' used for creating a new branch (GH_Path) for storing pts which is itself a list of points For Each m_val As Double In multiple_m width = Cal_W(length, m_val) 'length * (2 * EllipticE(m_val) / EllipticK(m_val) - 1) If width < 0 And ignoreSelfIntersecting Then Msg("warning", "One curve is self-intersecting. To enable these, set ignoreSelfIntersecting to False") Continue For End If If m_val >= Defined.M_SKETCHY Then Msg("info", "Accuracy of the curve whose width = " & Math.Round(width, 4) & " is not guaranteed") angle = Cal_A(m_val) 'Math.Asin(2 * m_val - 1) refPlane.Origin = refPlane.PointAt(width / 2, 0, 0) ' adjust the origin of the reference plane so that the curve is centered about the y-axis (start of the curve is at x = -width/2) tmp_pts = FindBendForm(length, width, m_val, angle, refPlane) multi_pts.AddRange(tmp_pts, New GH_Path(j)) multi_crv.Add(MakeCurve(tmp_pts, angle, refPlane)) multi_W.Add(width) If flip_A Then angle = -angle multi_A.Add(angle) E = E * 10 ^ 9 ' Young's modulus input E is in GPa, so we convert to Pa here (= N/m^2) multi_F.Add(EllipticK(m_val) ^ 2 * E * I / length ^ 2) ' from reference {4} pg. 79 j += 1 refPlane.Origin = PtA ' reset the reference plane origin to PtA for the next m_val 'Print("length=" & length & ", width=" & width & ", height=" & height & ", angle=" & angle & ", m=" & m_val & ", k=" & Math.Sqrt(m_val) & ", w/L=" & width / length & ", h/L=" & height / length & ", w/h=" & width / height) Next ' assign the outputs Pts = multi_pts Crv = multi_crv L = length W = multi_W If flip_H Then height = -height H = height A = multi_A F = multi_F Else ' only deal with the single m value If m >= Defined.M_SKETCHY Then Msg("info", "Accuracy of the curve at these parameters is not guaranteed") If width < 0 And ignoreSelfIntersecting Then Msg("error", "Curve is self-intersecting. To enable these, set ignoreSelfIntersecting to False") Return End If Pts = FindBendForm(length, width, m, angle, refPlane) Crv = MakeCurve(pts, angle, refPlane) L = length W = width If flip_H Then height = -height H = height If flip_A Then angle = -angle A = angle E = E * 10 ^ 9 ' Young's modulus input E is in GPa, so we convert to Pa here (= N/m^2) F = EllipticK(m) ^ 2 * E * I / length ^ 2 ' from reference {4} pg. 79. Note: the critical buckling (that makes the rod/wire start to bend) can be found at height=0 (width=length) 'height = Math.Sqrt(((2 * Len / 5) ^ 2 - ((Wid - Len / 5) / 2) ^ 2) ' quick approximation discovered by Mårten of 'Geometry of Bending' fame ( http://tiny.cc/it2pbx ) 'width = (Len +/- 2 * Math.Sqrt(4 * Len ^ 2 - 25 * Ht ^ 2)) / 5 ' derived from above 'length = (2 * Math.Sqrt(15 * Ht ^ 2 + 4 * Wid ^ 2) - Wid) / 3 ' derived from above 'Print("length=" & length & ", width=" & width & ", height=" & height & ", angle=" & angle & ", m=" & m & ", k=" & Math.Sqrt(m) & ", w/L=" & width / length & ", h/L=" & height / length & ", w/h=" & width / height) End If 1797 201 84 184 1839 293 9 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 8 3ede854e-c753-40eb-84cb-b48008f14fd4 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 true Script Variable PtA 512bccc3-6c0e-4ef4-ba24-8685c3ee8d8c PtA PtA true 0 true 1813e6b2-8594-4cdf-882c-e312c60bd7f7 1 e1937b56-b1da-4c12-8bd8-e34ee81746ef 1799 203 25 20 1813 213 true Script Variable PtB d879c694-aa7a-49cc-885b-4d3c9e0e85df PtB PtB true 0 true 0 e1937b56-b1da-4c12-8bd8-e34ee81746ef 1799 223 25 20 1813 233 true Script Variable Pln 0b814522-7d00-47c6-9c48-1e46bee924f2 Pln Pln true 0 true f9c309f7-e784-42bd-ac1d-c6f978935e00 1 3897522d-58e9-4d60-b38c-978ddacfedd8 1799 243 25 20 1813 253 true Script Variable Len 693f977f-077d-410b-a1cc-bc37f0473ad9 Len Len true 0 true ce3bf1e3-3694-43ca-b804-94bf1ac205b6 1 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 1799 263 25 20 1813 273 true Script Variable Wid e718cabe-f163-44e2-bf0e-4866946c6c49 Wid Wid true 0 true 2dac057e-8756-4d2f-b7af-61904cb5801a 1 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 1799 283 25 20 1813 293 true Script Variable Ht f8027746-ba7f-4f8e-bb9c-fa5b544e826b Ht Ht true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 1799 303 25 20 1813 313 true Script Variable Ang 50106206-1bb3-43fe-bd4f-366e3b16274a Ang Ang true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 1799 323 25 20 1813 333 true Script Variable E 1d6edbeb-7707-43da-8d81-c55c2a788b19 E E true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 1799 343 25 20 1813 353 true Script Variable I 06ae685a-2839-4dc2-a276-de0409a26bad I I true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 1799 363 25 20 1813 373 1 Print, Reflect and Error streams 20755b5a-7694-4c4e-82c2-fb013dc3fe1b out out false 0 1854 203 25 22 1866.5 214.25 Output parameter Pts b6862774-22a9-4557-8df9-4e105338905c Pts Pts false 0 1854 225 25 23 1866.5 236.75 Output parameter Crv 8f545f02-550b-41fb-8dd0-70baaad81a72 Crv Crv false 0 1854 248 25 22 1866.5 259.25 Output parameter L d7f4a38f-b681-4226-a169-d484336986a2 L L false 0 1854 270 25 23 1866.5 281.75 Output parameter W ce2227d2-88d5-44a1-b925-e842136dca13 W W false 0 1854 293 25 22 1866.5 304.25 Output parameter H 1c6a682f-bab0-45c4-b876-7f71802d69ab H H false 0 1854 315 25 23 1866.5 326.75 Output parameter A 24ef080a-aae1-4bd9-a2eb-97cd1569a733 A A false 0 1854 338 25 22 1866.5 349.25 Output parameter F 057d392c-b422-4b34-a8df-30546d6c59e2 F F false 0 1854 360 25 23 1866.5 371.75 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values 2dac057e-8756-4d2f-b7af-61904cb5801a Number Slider width false 0 1346 281 382 20 1346.563 281.9091 2 1 0 400 -130 0 -43.19 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values ce3bf1e3-3694-43ca-b804-94bf1ac205b6 Number Slider length false 0 1344 255 382 20 1344.994 255.0591 2 1 0 400 0 0 225 fbac3e32-f100-4292-8692-77240a42fd1a Point Contains a collection of three-dimensional points true 8083dba1-6a1d-4290-9c38-1186214db9bc Point Pt false b6862774-22a9-4557-8df9-4e105338905c 1 1969 163 50 24 1994.448 175.7781 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 3e685539-f44c-4607-be6b-cd3c05e3c5eb Panel false 0 4cd71eb6-cfaa-4a47-a72d-f24e55334beb 1 Double click to edit panel content… 2027 378 105 55 0 0 0 2027.304 378.139 255;255;250;90 true true true false false true 0d77c51e-584f-44e8-aed2-c2ddf4803888 Degrees Convert an angle specified in radians to degrees 25514f49-1dae-40e1-8f32-66e3ea2dc7bd Degrees Deg 1939 389 64 28 1969 403 Angle in radians 811b950c-3df2-44de-b7c6-6ffa4c6f2250 Radians R false 24ef080a-aae1-4bd9-a2eb-97cd1569a733 1 1941 391 13 24 1949 403 Angle in degrees 4cd71eb6-cfaa-4a47-a72d-f24e55334beb Degrees D false 0 1984 391 17 24 1992.5 403 3581f42a-9592-4549-bd6b-1c0fc39d067b Construct Point Construct a point from {xyz} coordinates. f3d440d6-99af-4801-8175-5110376379c5 Construct Point Pt 1654 139 67 64 1685 171 {x} coordinate 30c954ff-f9a8-4e9e-8c9e-f59a9c5d291c X coordinate X false 7feb34e6-c435-40ef-a40e-e2792a845fb9 1 1656 141 14 20 1664.5 151 1 1 {0} 0 {y} coordinate ae4bac11-a219-4f42-a0e6-d6bf63aeb9fa Y coordinate Y false 0 1656 161 14 20 1664.5 171 1 1 {0} 0 {z} coordinate 24106b26-bc67-44c4-a1e9-15b116c3613c Z coordinate Z false 0 1656 181 14 20 1664.5 191 1 1 {0} 0 Point coordinate 1813e6b2-8594-4cdf-882c-e312c60bd7f7 Point Pt false 0 1700 141 19 60 1709.5 171 d5967b9f-e8ee-436b-a8ad-29fdcecf32d5 Curve Contains a collection of generic curves 3be0014f-c7bb-45c2-a96c-88d0e7dd1d16 Curve Crv false 8f545f02-550b-41fb-8dd0-70baaad81a72 1 1969 206 50 24 1994.437 218.3081 17b7152b-d30d-4d50-b9ef-c9fe25576fc2 XY Plane World XY plane. true d0010e01-3735-4f83-b1a6-500844575bf9 XY Plane XY 1659 214 64 28 1690 228 Origin of plane 2c415a43-0493-4511-bc80-751474a0da20 Origin O false 0 1661 216 14 24 1669.5 228 1 1 {0} 0 0 0 World XY plane f9c309f7-e784-42bd-ac1d-c6f978935e00 Plane P false 0 1705 216 16 24 1713 228 a4cd2751-414d-42ec-8916-476ebf62d7fe Radians Convert an angle specified in degrees to radians 8fe257f5-d9cb-49d1-b6c9-0275b7bb1e07 Radians Rad 1664 333 64 28 1695 347 Angle in degrees 6b4b4cf1-fd46-40c8-9f67-929d72db351b Degrees D false 364d3276-a223-4ce8-826c-4071f2924b37 1 1666 335 14 24 1674.5 347 Angle in radians c9958f3c-a438-4f78-a3b0-79c2730cd794 Radians R false 0 1710 335 16 24 1718 347 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values 364d3276-a223-4ce8-826c-4071f2924b37 Number Slider angle ° false 0 1351 340 295 20 1351.752 340.978 2 1 0 170 -170 0 -42.08 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 49784903-21d7-4279-9bef-a75b6aa5db52 Panel false 0 d7f4a38f-b681-4226-a169-d484336986a2 1 Double click to edit panel content… 1924 257 97 38 0 0 0 1924.427 257.7051 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 4aa4b973-077b-4bf3-b682-1bab56120052 Panel false 0 ce2227d2-88d5-44a1-b925-e842136dca13 1 Double click to edit panel content… 2035 282 105 55 0 0 0 2035.346 282.573 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 372b06f9-a8b8-4e7d-b9a0-47795f8b6db6 Panel false 0 1c6a682f-bab0-45c4-b876-7f71802d69ab 1 Double click to edit panel content… 1923 318 97 38 0 0 0 1923.371 318.089 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 7feb34e6-c435-40ef-a40e-e2792a845fb9 Panel false 0 0 -75 1568 140 50 20 0 0 0 1568.331 140.4951 255;255;250;90 true true true false false true 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe Scribble true 2269.006 1162.356 2646.125 1163.049 2646.09 1181.682 2268.972 1180.989 A quick note Microsoft Sans Serif 61792023-c3a4-44db-a658-c546e321f080 false Scribble Scribble 25 And the real raison d'être: tents! 2263.972 1157.356 387.1528 29.32556 2269.006 1162.356 575660b1-8c79-4b8d-9222-7ab4a6ddb359 Rectangle 2Pt Create a rectangle from a base plane and two points b8a48901-2a87-4e7d-870b-46ddd3c0a406 Rectangle 2Pt Rec 2Pt 2171 1539 64 84 2202 1581 Rectangle base plane e2d6cfd4-201c-479e-a449-d47449b5d99c Plane P false 8d8d0823-8c31-466e-89ab-c417764715c3 1 2173 1541 14 20 2181.5 1551 1 1 {0} 0 0 0 1 0 0 0 1 0 First corner point. c31a60ef-1b6b-45b3-b296-290087f567bf Point A A false 9ac73f55-39ae-429e-8dc8-8e7f958b4042 1 2173 1561 14 20 2181.5 1571 1 1 {0} 0 0 0 Second corner point. 84fdc143-bd3e-4515-a76c-b9793c5c039d Point B B false a8fa466b-7cde-4211-998f-da025b67f6b6 1 2173 1581 14 20 2181.5 1591 1 1 {0} 10 5 0 Rectangle corner fillet radius 789908b6-b304-431d-b593-c2d0b7f80aa3 Radius R false 0 2173 1601 14 20 2181.5 1611 1 1 {0} 0 Rectangle defined by P, A and B 5cf2e6df-da94-4f18-9535-133a50f579e9 Rectangle R false 0 2217 1541 16 40 2225 1561 Length of rectangle curve 7e0b11dd-d81c-4da2-b222-5309b51b0636 Length L false 0 2217 1581 16 40 2225 1601 17b7152b-d30d-4d50-b9ef-c9fe25576fc2 XY Plane World XY plane. true 03446bc9-f51d-46c6-85eb-615230f3c249 XY Plane XY 2099 1495 64 28 2130 1509 Origin of plane 8b2fbbc3-5be3-46d2-9834-1a1a5caea969 Origin O false 0 2101 1497 14 24 2109.5 1509 1 1 {0} 0 0 0 World XY plane 8d8d0823-8c31-466e-89ab-c417764715c3 Plane P false 0 2145 1497 16 24 2153 1509 3581f42a-9592-4549-bd6b-1c0fc39d067b Construct Point Construct a point from {xyz} coordinates. 7d498686-2e4d-42a6-adc7-13da767447ec Construct Point Pt 1530 1185 67 64 1561 1217 {x} coordinate bb3e48a5-a17f-4d87-8d40-67dd01b081eb X coordinate X false 0903a6c2-e102-41a9-8326-3f9533f91f9f 1 1532 1187 14 20 1540.5 1197 1 1 {0} 0 {y} coordinate c46a319c-3740-4362-8bff-3454b1d2a6cc Y coordinate Y false 17a61eca-a95a-4f8b-aa55-052523e686bc 1 1532 1207 14 20 1540.5 1217 1 1 {0} 0 {z} coordinate 6c63b308-c5d5-44f6-a259-04c99b457c22 Z coordinate Z false 0 1532 1227 14 20 1540.5 1237 1 1 {0} 0 Point coordinate f70a66ec-d9db-496d-9885-4b577f135593 Point Pt false 0 1576 1187 19 60 1585.5 1217 a0d62394-a118-422d-abb3-6af115c75b25 Addition Mathematical addition true f35af83d-b4d8-44ef-9f78-7dc1ed1ad4dd Addition A+B 1688 1286 64 44 1719 1308 2 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 1 8ec86459-bf01-4409-baee-174d0d2b13d0 First item for addition f3b877be-ea2c-4706-b8e6-cf34d2796bca A A true f70a66ec-d9db-496d-9885-4b577f135593 1 1690 1288 14 20 1698.5 1298 Second item for addition 16e574ce-9435-47c5-850d-a8fc5152e81d B B true 60a1321b-0d85-4215-ac31-f94c7015ccf2 1 1690 1308 14 20 1698.5 1318 Result of addition 9ac73f55-39ae-429e-8dc8-8e7f958b4042 Result R false 0 1734 1288 16 40 1742 1308 56b92eab-d121-43f7-94d3-6cd8f0ddead8 Vector XYZ Create a vector from {xyz} components. true 25bf0db5-4adb-4408-bfee-7d3611f1d1b6 Vector XYZ Vec 1576 1323 64 64 1607 1355 Vector {x} component ec060dc1-0d2b-4706-ac14-728c097f877f X component X false 9b5d1997-6eb6-419e-84a2-da5dc70f5477 1 1578 1325 14 20 1586.5 1335 1 1 {0} 0 Vector {y} component 1554f826-50eb-4842-ad0c-e1591646e701 Y component Y false b582b4c2-046d-4816-9bf2-b7e40b12e3a1 1 1578 1345 14 20 1586.5 1355 1 1 {0} 0 Vector {z} component c5b4970d-6668-445e-b8a3-7d736d0f83dc Z component Z false 0 1578 1365 14 20 1586.5 1375 1 1 {0} 0 Vector construct 60a1321b-0d85-4215-ac31-f94c7015ccf2 Vector V false 0 1622 1325 16 30 1630 1340 Vector length 614a3c6d-0b33-40ad-81d3-d0dfd2cda65f Length L false 0 1622 1355 16 30 1630 1370 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 9b5d1997-6eb6-419e-84a2-da5dc70f5477 Panel false 0 0 3 1491 1321 50 20 0 0 0 1491.106 1321.658 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values b582b4c2-046d-4816-9bf2-b7e40b12e3a1 Panel false 0 0 2 1489 1351 50 20 0 0 0 1489.186 1351.098 255;255;250;90 true true true false false true 9abae6b7-fa1d-448c-9209-4a8155345841 Deconstruct Deconstruct a point into its component parts. true 04d13d9d-5383-4696-b406-0fc16bb9a913 Deconstruct pDecon 1704 1432 64 64 1734 1464 Input point ac0ad7b1-bf77-4354-ae03-3ed56f7694ac Point P false 9ac73f55-39ae-429e-8dc8-8e7f958b4042 1 1706 1434 13 60 1714 1464 Point {x} component 986c6ad6-35ae-433f-bd24-072861a5425d X component X false 0 1749 1434 17 20 1757.5 1444 Point {y} component 2c914091-22c1-40d8-abc1-4ccddd24e6ea Y component Y false 0 1749 1454 17 20 1757.5 1464 Point {z} component eb73bdaa-9667-4377-9a20-f2e4e4e9cdbf Z component Z false 0 1749 1474 17 20 1757.5 1484 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values 91f17ed9-335a-446e-9d6c-b9c687cba0f0 Number Slider Length false 0 1456 1597 263 20 1456.337 1597.275 1 1 0 100 0 0 88 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values 0fd09915-ec8a-4a40-989a-123b9e68d06c Number Slider Width false 0 1457 1655 264 20 1457.087 1655.275 1 1 0 100 0 0 54 a0d62394-a118-422d-abb3-6af115c75b25 Addition Mathematical addition true 425cffae-604f-493a-a5c9-d68e3739d648 Addition A+B 1786 1573 64 44 1817 1595 2 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 1 8ec86459-bf01-4409-baee-174d0d2b13d0 First item for addition bd91b324-b139-4235-80e4-f5e0f3620809 A A true 986c6ad6-35ae-433f-bd24-072861a5425d 1 1788 1575 14 20 1796.5 1585 Second item for addition a87172e3-431e-4ddf-a937-60b5bc804451 B B true 91f17ed9-335a-446e-9d6c-b9c687cba0f0 1 1788 1595 14 20 1796.5 1605 Result of addition ef5b5c46-d28c-4d3d-8719-82ebb12862d2 Result R false 0 1832 1575 16 40 1840 1595 a0d62394-a118-422d-abb3-6af115c75b25 Addition Mathematical addition true c608fbec-5b75-4c4d-b4d8-48a376f3cd5c Addition A+B 1790 1635 64 44 1821 1657 2 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 1 8ec86459-bf01-4409-baee-174d0d2b13d0 First item for addition bd152880-8e47-4e39-af2c-724b79caa7ea A A true 2c914091-22c1-40d8-abc1-4ccddd24e6ea 1 1792 1637 14 20 1800.5 1647 Second item for addition 2ff8f792-cc71-4183-9cdf-57524cacfb82 B B true 0fd09915-ec8a-4a40-989a-123b9e68d06c 1 1792 1657 14 20 1800.5 1667 Result of addition 49b19326-ddaa-450c-a91b-d85295831ca1 Result R false 0 1836 1637 16 40 1844 1657 3581f42a-9592-4549-bd6b-1c0fc39d067b Construct Point Construct a point from {xyz} coordinates. true 0025b0b3-cd63-4b6e-a883-c991f59c2549 Construct Point Pt 1994 1573 67 64 2025 1605 {x} coordinate e0588cd2-61aa-4863-98e0-1e9a7f21a1be X coordinate X false ef5b5c46-d28c-4d3d-8719-82ebb12862d2 1 1996 1575 14 20 2004.5 1585 1 1 {0} 0 {y} coordinate 037a8251-883a-40af-a11c-21b9ae0ecb92 Y coordinate Y false 49b19326-ddaa-450c-a91b-d85295831ca1 1 1996 1595 14 20 2004.5 1605 1 1 {0} 0 {z} coordinate 54de041d-38b7-41dc-8680-c6bfa86d9ba3 Z coordinate Z false 0 1996 1615 14 20 2004.5 1625 1 1 {0} 0 Point coordinate a8fa466b-7cde-4211-998f-da025b67f6b6 Point Pt false 0 2040 1575 19 60 2049.5 1605 a0d62394-a118-422d-abb3-6af115c75b25 Addition Mathematical addition true 66ed28c2-e779-444a-9c0b-9bd8f26ea017 Addition A+B 2124 1296 64 44 2155 1318 2 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 1 8ec86459-bf01-4409-baee-174d0d2b13d0 First item for addition e86f9ce0-a5ad-4770-854f-f91bc82511aa A A true a8fa466b-7cde-4211-998f-da025b67f6b6 1 2126 1298 14 20 2134.5 1308 Second item for addition 372d26ca-bd41-418e-9a3f-36c4cb124978 B B true 60a1321b-0d85-4215-ac31-f94c7015ccf2 1 2126 1318 14 20 2134.5 1328 Result of addition 79d26b1a-d554-4adf-802a-54c1a82df2de Result R false 0 2170 1298 16 40 2178 1318 f12daa2f-4fd5-48c1-8ac3-5dea476912ca Mirror Mirror an object. true 5caa758f-68a9-4e10-8541-d9dcff111255 Mirror Mirror 2176 1350 65 44 2207 1372 Base geometry c1d5c25c-8138-49ab-bd00-a02680a1f99d Geometry G true 60a1321b-0d85-4215-ac31-f94c7015ccf2 1 2178 1352 14 20 2186.5 1362 Mirror plane e3d075b2-072b-47d9-9463-c3aa654ad224 Plane P false 48d4eb53-6ad2-481f-8610-55d9944a22c3 1 2178 1372 14 20 2186.5 1382 1 1 {0} 0 0 0 0 1 0 0 0 1 Mirrored geometry bf8b3e5d-d755-43fb-8734-c94ed4d00bba Geometry G false 0 2222 1352 17 20 2230.5 1362 Transformation data 4ee0f674-5c0f-410f-b9f6-08a3604b4334 Transform X false 0 2222 1372 17 20 2230.5 1382 fad344bc-09b1-4855-a2e6-437ef5715fe3 YZ Plane World YZ plane. true 272c02f3-8054-4da2-b750-4109a8b8c37e YZ Plane YZ 2091 1367 64 28 2122 1381 Origin of plane 37e34f49-3112-4952-89a1-4b43e1db22f2 Origin O false 0 2093 1369 14 24 2101.5 1381 1 1 {0} 0 0 0 World YZ plane 48d4eb53-6ad2-481f-8610-55d9944a22c3 Plane P false 0 2137 1369 16 24 2145 1381 3581f42a-9592-4549-bd6b-1c0fc39d067b Construct Point Construct a point from {xyz} coordinates. true 158a8811-4a85-4636-9d80-bb6a389c6dc5 Construct Point Pt 1987 1388 67 64 2018 1420 {x} coordinate 9c32aac2-60cd-43ce-8916-e951db46c16d X coordinate X false 986c6ad6-35ae-433f-bd24-072861a5425d 1 1989 1390 14 20 1997.5 1400 1 1 {0} 0 {y} coordinate a95132f4-4546-4352-9af9-19e27eedb3c9 Y coordinate Y false 49b19326-ddaa-450c-a91b-d85295831ca1 1 1989 1410 14 20 1997.5 1420 1 1 {0} 0 {z} coordinate 8802bfb6-39ba-40ec-84a0-199a2ccc0b6a Z coordinate Z false 0 1989 1430 14 20 1997.5 1440 1 1 {0} 0 Point coordinate 21b1fb4d-d8f9-4dca-a50f-bda44d8d40b3 Point Pt false 0 2033 1390 19 60 2042.5 1420 a0d62394-a118-422d-abb3-6af115c75b25 Addition Mathematical addition true e0d2b78b-5b2e-48e8-af51-67f77e6fe2e3 Addition A+B 2364 1520 64 44 2395 1542 2 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 1 8ec86459-bf01-4409-baee-174d0d2b13d0 First item for addition 06d9a75f-442d-48a1-ace0-8e88067ed67f A A true 21b1fb4d-d8f9-4dca-a50f-bda44d8d40b3 1 2366 1522 14 20 2374.5 1532 Second item for addition 6e986d97-59c1-46d7-8208-3fa2d487fe5c B B true bf8b3e5d-d755-43fb-8734-c94ed4d00bba 1 2366 1542 14 20 2374.5 1552 Result of addition 5b37f206-7db4-44b0-ba0f-6175cec13953 Result R false 0 2410 1522 16 40 2418 1542 3581f42a-9592-4549-bd6b-1c0fc39d067b Construct Point Construct a point from {xyz} coordinates. true 8ecbb3d6-8916-43ff-9ea1-e19a4fb39844 Construct Point Pt 1996 1662 67 64 2027 1694 {x} coordinate 8e8e1801-b640-4212-9871-fb70a85ac93c X coordinate X false ef5b5c46-d28c-4d3d-8719-82ebb12862d2 1 1998 1664 14 20 2006.5 1674 1 1 {0} 0 {y} coordinate 26f967a7-45e3-4567-8d5b-e583254c129a Y coordinate Y false 2c914091-22c1-40d8-abc1-4ccddd24e6ea 1 1998 1684 14 20 2006.5 1694 1 1 {0} 0 {z} coordinate dfcf11d4-ae95-4bcd-afb7-606d1755dc76 Z coordinate Z false 0 1998 1704 14 20 2006.5 1714 1 1 {0} 0 Point coordinate e825beda-2c01-4b73-b180-297fcfe8bbc3 Point Pt false 0 2042 1664 19 60 2051.5 1694 2c56ab33-c7cc-4129-886c-d5856b714010 Subtraction Mathematical subtraction true 716d8d38-b0b8-409b-9809-4626eaa49feb Subtraction A-B 2368 1584 64 44 2399 1606 Item to subtract from (minuend) 1104ec21-7fa5-4dae-af48-1016aa881210 A A false e825beda-2c01-4b73-b180-297fcfe8bbc3 1 2370 1586 14 20 2378.5 1596 Item to subtract (subtrahend) a3acb875-c63e-4a60-8e24-40c0d03aba55 B B false bf8b3e5d-d755-43fb-8734-c94ed4d00bba 1 2370 1606 14 20 2378.5 1616 The result of the Subtraction 40a43f7c-5b18-42a9-934b-a3fca8b86584 Result R false 0 2414 1586 16 40 2422 1606 fbac3e32-f100-4292-8692-77240a42fd1a Point Contains a collection of three-dimensional points true 5c0c8191-e5d5-44d8-9b74-ad7b50095c12 Point Pt false 40a43f7c-5b18-42a9-934b-a3fca8b86584 1 2466 1587 50 24 2491.524 1599.556 fbac3e32-f100-4292-8692-77240a42fd1a Point Contains a collection of three-dimensional points true 83dc0e24-1c12-445e-8647-dffc452baa6a Point Pt false 5b37f206-7db4-44b0-ba0f-6175cec13953 1 2464 1538 50 24 2489.024 1550.806 fbac3e32-f100-4292-8692-77240a42fd1a Point Contains a collection of three-dimensional points true c54e879a-bb6c-47c6-b366-aaa5a16a426a Point Pt false f70a66ec-d9db-496d-9885-4b577f135593 1 2387 1270 50 24 2412.399 1282.068 fbac3e32-f100-4292-8692-77240a42fd1a Point Contains a collection of three-dimensional points true 72b7092f-b51c-4a8b-b457-5cc6f58de91d Point Pt false 79d26b1a-d554-4adf-802a-54c1a82df2de 1 2387 1315 50 24 2412.599 1327.418 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 8526fdb2-be35-4339-84b0-0de9c353ed2c Panel false 0 0 3 2586 1726 50 20 0 0 0 2586.39 1726.995 255;255;250;90 true true true false false true a0d62394-a118-422d-abb3-6af115c75b25 Addition Mathematical addition e9472173-0e76-45e7-b4b3-2d927d4baecc Addition A+B 2663 1691 64 44 2694 1713 2 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 1 8ec86459-bf01-4409-baee-174d0d2b13d0 First item for addition da0b56ad-280a-4f2a-8298-030632f1527e A A true 14cc88f5-707d-4e86-a518-ccdcce6ded66 1 2665 1693 14 20 2673.5 1703 Second item for addition 7467100d-05d3-4ae7-a577-8da37d4b9e55 B B true 8526fdb2-be35-4339-84b0-0de9c353ed2c 1 2665 1713 14 20 2673.5 1723 Result of addition cd2f5af0-b741-48d5-8bbd-e9695d5278d8 Result R false 0 2709 1693 16 40 2717 1713 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values 14cc88f5-707d-4e86-a518-ccdcce6ded66 Number Slider Peak Height false 0 2372 1693 263 20 2372.275 1693.369 1 1 0 72 0 0 44 9103c240-a6a9-4223-9b42-dbd19bf38e2b Unit Z Unit vector parallel to the world {z} axis. c51aaec6-9bc7-40d7-ab2d-705fd2e20e1e Unit Z Z 2553 1260 62 28 2582 1274 Unit multiplication 2414a112-d958-411b-b27f-8cdf9d0d2480 Factor F false 0 2555 1262 12 24 2562.5 1274 1 1 {0} 1 World {z} vector fdc6f14b-d2af-4143-9fa5-98d72e1496f4 Unit vector V false 0 2597 1262 16 24 2605 1274 934ede4a-924a-4973-bb05-0dc4b36fae75 Vector 2Pt Create a vector between two points. 361f13b0-f722-496a-b487-cfffdce2f9cb Vector 2Pt Vec2Pt 2548 1387 64 64 2579 1419 Base point 5e0ee3e7-7ca6-486b-a276-9290daec2c1d Point A A false c54e879a-bb6c-47c6-b366-aaa5a16a426a 83dc0e24-1c12-445e-8647-dffc452baa6a 2 2550 1389 14 20 2558.5 1399 Tip point 69e066b8-5497-4517-9782-d03091f32b49 Point B B false 72b7092f-b51c-4a8b-b457-5cc6f58de91d 5c0c8191-e5d5-44d8-9b74-ad7b50095c12 2 2550 1409 14 20 2558.5 1419 Unitize output e01e96a4-176c-42b4-baed-01d2da98668f Unitize U false 0 2550 1429 14 20 2558.5 1439 1 1 {0} false Vector a09f5810-c5e6-41cf-a732-76ade1d918e1 Vector V false 0 2594 1389 16 30 2602 1404 Vector length 15154730-ac41-4df7-abd3-5125f66ed008 Length L false 0 2594 1419 16 30 2602 1434 bc3e379e-7206-4e7b-b63a-ff61f4b38a3e Construct Plane Construct a plane from an origin point and {x}, {y} axes. true 1b6a1009-88b9-418a-8918-f95aabbb1c52 Construct Plane Pl 2674 1295 66 64 2705 1327 Origin of plane 7a695424-f044-41a8-9bf1-a2cbf28cc226 Origin O false c54e879a-bb6c-47c6-b366-aaa5a16a426a 83dc0e24-1c12-445e-8647-dffc452baa6a 2 2676 1297 14 20 2684.5 1307 1 1 {0} 0 0 0 X-Axis direction of plane c61582e5-1354-459b-babb-382f6952792a X-Axis X false a09f5810-c5e6-41cf-a732-76ade1d918e1 1 2676 1317 14 20 2684.5 1327 1 1 {0} 1 0 0 Y-Axis direction of plane ec988c3e-fd2b-4d2b-aa6a-5e3a9c15a503 Y-Axis Y false fdc6f14b-d2af-4143-9fa5-98d72e1496f4 1 2676 1337 14 20 2684.5 1347 1 1 {0} 0 1 0 Constructed plane f5ef3118-8c76-4765-bdd9-16087c3ebb2d Plane Pl false 0 2720 1297 18 60 2729 1327 079bd9bd-54a0-41d4-98af-db999015f63d VB Script Private Function IsSet(ByVal param As String) As Boolean ' Check if an input parameter has data Dim i As Integer = Component.Params.IndexOfInputParam(param) If i > -1 Then Return Component.Params.Input.ElementAt(i).DataType > 1 ' input parameter DataType of 1 means it's not receiving input (internal or external) Else Msg("error", "Input parameter '" & param & "' not found") Return False End If End Function Private Sub Msg(ByVal type As String, ByVal msg As String) ' Output an error, warning, or informational message Select Case type Case "error" Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, msg) Print("Error: " & msg) Case "warning" Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, msg) Print("Warning: " & msg) Case "info" Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, msg) Print(msg) End Select End Sub ' Solve for the m parameter from length and width (reference {1} equation (34), except b = width and K(k) and E(k) should be K(m) and E(m)) Private Function SolveMFromLenWid(ByVal L As Double, ByVal w As Double) As Double If w = 0 Then Return Defined.M_ZERO_W ' for the boundry condition width = 0, bypass the function and return the known m value End If Dim n As Integer = 1 ' Iteration counter (quit if >MAXIT) Dim lower As Double = 0 ' m must be within this range Dim upper As Double = 1 Dim m As Double Dim cwl As Double Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 cwl = 2 * EllipticE(m) / EllipticK(m) - 1 ' calculate w/L with the test value of m If cwl < w / L Then ' compares the calculated w/L with the actual w/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop Return m End Function ' Solve for the m parameter from length and height (reference {1} equation (33), except K(k) should be K(m) and k = sqrt(m)) ' Note that it's actually possible to find 2 valid values for m (hence 2 width values) at certain height values Private Function SolveMFromLenHt(ByVal L As Double, ByVal h As Double) As List(Of Double) Dim n As Integer = 1 ' Iteration counter (quit if >MAXIT) Dim lower As Double = 0 ' m must be within this range Dim upper As Double = 1 Dim twoWidths As Boolean = h / L >= Defined.DOUBLE_W_HL_RATIO And h / L < Defined.MAX_HL_RATIO ' check to see if h/L is within the range where 2 solutions for the width are possible Dim m As Double Dim mult_m As New List(Of Double) Dim chl As Double If twoWidths Then ' find the first of two possible solutions for m with the following limits: lower = Defined.M_DOUBLE_W ' see constants at bottom of script upper = Defined.M_MAXHEIGHT ' see constants at bottom of script Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 chl = Math.Sqrt(m) / EllipticK(m) ' calculate h/L with the test value of m If chl > h / L Then ' compares the calculated h/L with the actual h/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop mult_m.Add(m) ' then find the second of two possible solutions for m with the following limits: lower = Defined.M_MAXHEIGHT ' see constants at bottom of script upper = 1 Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 chl = Math.Sqrt(m) / EllipticK(m) ' calculate h/L with the test value of m If chl < h / L Then ' compares the calculated h/L with the actual h/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop If m <= Defined.M_MAX Then ' return this m parameter only if it falls within the maximum useful value (above which the curve breaks down) mult_m.Add(m) End If Else ' find the one possible solution for the m parameter upper = Defined.M_DOUBLE_W ' limit the upper end of the search to the maximum value of m for which only one solution exists Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 chl = Math.Sqrt(m) / EllipticK(m) ' calculate h/L with the test value of m If chl > h / L Then ' compares the calculated h/L with the actual h/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop mult_m.Add(m) End If Return mult_m End Function ' Solve for the m parameter from width and height (derived from reference {1} equations (33) and (34) with same notes as above) Private Function SolveMFromWidHt(ByVal w As Double, ByVal h As Double) As Double Dim n As Integer = 1 ' Iteration counter (quit if >MAXIT) Dim lower As Double = 0 ' m must be within this range Dim upper As Double = 1 Dim m As Double Dim cwh As Double Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 cwh = (2 * EllipticE(m) - EllipticK(m)) / Math.Sqrt(m) ' calculate w/h with the test value of m If cwh < w / h Then ' compares the calculated w/h with the actual w/h then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop Return m End Function ' Calculate length based on height and an m parameter, derived from reference {1} equation (33), except K(k) should be K(m) and k = sqrt(m) Private Function Cal_L(ByVal h As Double, ByVal m As Double) As Double Return h * EllipticK(m) / Math.Sqrt(m) End Function ' Calculate width based on length and an m parameter, derived from reference {1} equation (34), except b = width and K(k) and E(k) should be K(m) and E(m) Private Function Cal_W(ByVal L As Double, ByVal m As Double) As Double Return L * (2 * EllipticE(m) / EllipticK(m) - 1) End Function ' Calculate height based on length and an m parameter, from reference {1} equation (33), except K(k) should be K(m) and k = sqrt(m) Private Function Cal_H(ByVal L As Double, ByVal m As Double) As Double Return L * Math.Sqrt(m) / EllipticK(m) End Function ' Calculate the unique m parameter based on a start tangent angle, from reference {2}, just above equation (9a), that states k = Sin(angle / 2 + Pi / 4), ' but as m = k^2 and due to this script's need for an angle rotated 90° versus the one in reference {1}, the following formula is the result ' New note: verified by reference {4}, pg. 78 at the bottom Private Function Cal_M(ByVal a As Double) As Double Return (1 - Math.Cos(a)) / 2 ' equal to Sin^2(a/2) too End Function ' Calculate start tangent angle based on an m parameter, derived from above formula Private Function Cal_A(ByVal m As Double) As Double Return Math.Acos(1 - 2 * m) End Function ' This is the heart of this script, taking the found (or specified) length, width, and angle values along with the found m parameter to create ' a list of points that approximate the shape or form of the elastica. It works by finding the x and y coordinates (which are reversed versus ' the original equations (12a) and (12b) from reference {2} due to the 90° difference in orientation) based on the tangent angle along the curve. ' See reference {2} for more details on how they derived it. Note that to simplify things, the algorithm only calculates the points for half of the ' curve, then mirrors those points along the y-axis. Private Function FindBendForm(ByVal L As Double, ByVal w As Double, ByVal m As Double, ByVal ang As Double, ByVal refPln As Plane) As List(Of Point3d) L = L / 2 ' because the below algorithm is based on the formulas in reference {2} for only half of the curve w = w / 2 ' same If ang = 0 Then ' if angle (and height) = 0, then simply return the start and end points of the straight line Dim out As New List(Of Point3d) out.Add(refPln.PointAt(w, 0, 0)) out.Add(refPln.PointAt(-w, 0, 0)) Return out End If Dim x As Double Dim y As Double Dim halfCurvePts As New List(Of Point3d) Dim fullCurvePts As New List(Of Point3d) Dim translatedPts As New List(Of Point3d) ang -= Math.PI / 2 ' a hack to allow this algorithm to work, since the original curve in paper {2} was rotated 90° Dim angB As Double = ang + (-Math.PI / 2 - ang) / Defined.CURVEDIVS ' angB is the 'lowercase theta' which should be in formula {2}(12b) as the interval ' start [a typo...see equation(3)]. It's necessary to start angB at ang + [interval] instead of just ang due to integration failing at angB = ang halfCurvePts.Add(New Point3d(w, 0, 0)) ' start with this known initial point, as integration will fail when angB = ang ' each point {x, y} is calculated from the tangent angle, angB, that occurs at each point (which is why this iterates from ~ang to -pi/2, the known end condition) Do While Math.Round(angB, Defined.ROUNDTO) >= Math.Round(-Math.PI / 2, Defined.ROUNDTO) y = (Math.Sqrt(2) * Math.Sqrt(Math.Sin(ang) - Math.Sin(angB)) * (w + L)) / (2 * EllipticE(m)) ' note that x and y are swapped vs. (12a) and (12b) x = (L / (Math.Sqrt(2) * EllipticK(m))) * Simpson(angB, -Math.PI / 2, 500, ang) ' calculate the Simpson approximation of the integral (function f below) ' over the interval angB ('lowercase theta') to -pi/2. side note: is 500 too few iterations for the Simson algorithm? If Math.Round(x, Defined.ROUNDTO) = 0 Then x = 0 halfCurvePts.Add(New Point3d(x, y, 0)) angB += (-Math.PI / 2 - ang) / Defined.CURVEDIVS ' onto the next tangent angle Loop ' After finding the x and y values for half of the curve, add the {-x, y} values for the rest of the curve For Each point As Point3d In halfCurvePts If Math.Round(point.X, Defined.ROUNDTO) = 0 Then If Math.Round(point.Y, Defined.ROUNDTO) = 0 Then fullCurvePts.Add(New Point3d(0, 0, 0)) ' special case when width = 0: when x = 0, only duplicate the point when y = 0 too End If Else fullCurvePts.Add(New Point3d(-point.X, point.Y, 0)) End If Next halfCurvePts.Reverse fullCurvePts.AddRange(halfCurvePts) For Each p As Point3d In fullCurvePts translatedPts.Add(refPln.PointAt(p.X, p.Y, p.Z)) ' translate the points from the reference plane to the world plane Next Return translatedPts End Function ' Interpolates the points from FindBendForm to create the Elastica curve. Uses start & end tangents for greater accuracy. Private Function MakeCurve(ByVal pts As List(Of Point3d), ByVal ang As Double, ByVal refPln As Plane) As Curve If ang <> 0 Then Dim ts, te As New Vector3d(refPln.XAxis) ts.Rotate(ang, refPln.ZAxis) te.Rotate(-ang, refPln.ZAxis) Return Curve.CreateInterpolatedCurve(pts, 3, CurveKnotStyle.Chord, ts, te) ' 3rd degree curve with 'Chord' Knot Style Else Return Curve.CreateInterpolatedCurve(pts, 3) ' if angle (and height) = 0, then simply interpolate the straight line (no start/end tangents) End If End Function ' Implements the Simpson approximation for an integral of function f below Public Function Simpson(a As Double, b As Double, n As Integer, theta As Double) As Double 'n should be an even number Dim j As Integer, s1 As Double, s2 As Double, h As Double h = (b - a) / n s1 = 0 s2 = 0 For j = 1 To n - 1 Step 2 s1 = s1 + fn(a + j * h, theta) Next j For j = 2 To n - 2 Step 2 s2 = s2 + fn(a + j * h, theta) Next j Simpson = h / 3 * (fn(a, theta) + 4 * s1 + 2 * s2 + fn(b, theta)) End Function ' Specific calculation for the above integration Public Function fn(x As Double, theta As Double) As Double fn = Math.Sin(x) / (Math.Sqrt(Math.Sin(theta) - Math.Sin(x))) ' from reference {2} formula (12b) End Function ' Return the Complete Elliptic integral of the 1st kind ' Abramowitz and Stegun p.591, formula 17.3.11 ' Code from http://www.codeproject.com/Articles/566614/Elliptic-integrals Public Function EllipticK(ByVal m As Double) As Double Dim sum, term, above, below As Double sum = 1 term = 1 above = 1 below = 2 For i As Integer = 1 To 100 term *= above / below sum += Math.Pow(m, i) * Math.Pow(term, 2) above += 2 below += 2 Next sum *= 0.5 * Math.PI Return sum End Function ' Return the Complete Elliptic integral of the 2nd kind ' Abramowitz and Stegun p.591, formula 17.3.12 ' Code from http://www.codeproject.com/Articles/566614/Elliptic-integrals Public Function EllipticE(ByVal m As Double) As Double Dim sum, term, above, below As Double sum = 1 term = 1 above = 1 below = 2 For i As Integer = 1 To 100 term *= above / below sum -= Math.Pow(m, i) * Math.Pow(term, 2) / above above += 2 below += 2 Next sum *= 0.5 * Math.PI Return sum End Function Friend Partial NotInheritable Class Defined Private Sub New() End Sub ' Note: most of these values for m and h/L ratio were found with Wolfram Alpha and either specific intercepts (x=0) or local minima/maxima. They should be constant. Public Const M_SKETCHY As Double = 0.95 ' value of the m parameter where the curvature near the ends of the curve gets wonky Public Const M_MAX As Double = 0.993 ' maximum useful value of the m parameter, above which this algorithm for the form of the curve breaks down Public Const M_ZERO_W As Double = 0.826114765984970336 ' value of the m parameter when width = 0 Public Const M_MAXHEIGHT As Double = 0.701327460663101223 ' value of the m parameter at maximum possible height of the bent rod/wire Public Const M_DOUBLE_W As Double = 0.180254422335013983 ' minimum value of the m parameter when two width values are possible for a given height and length Public Const DOUBLE_W_HL_RATIO As Double = 0.257342117984635757 ' value of the height/length ratio above which there are two possible width values Public Const MAX_HL_RATIO As Double = 0.403140189705650243 ' maximum possible value of the height/length ratio Public Const MAXERR As Double = 0.0000000001 ' error tolerance Public Const MAXIT As Integer = 100 ' maximum number of iterations Public Const ROUNDTO As Integer = 10 ' number of decimal places to round off to Public Const CURVEDIVS As Integer = 50 ' number of sample points for building the curve (or half-curve as it were) End Class A VB.NET scriptable component 98 86 true efeac80e-aaa9-43ef-acff-f0dc08a37ca1 VB Script VB true 0 ' ----------------------------------------------------------------- ' Elastic Bending Script by Will McElwain ' Created February 2014 ' ' DESCRIPTION: ' This beast creates the so-called 'elastica curve', the shape a long, thin rod or wire makes when it is bent elastically (i.e. not permanently). In this case, force ' is assumed to only be applied horizontally (which would be in line with the rod at rest) and both ends are assumed to be pinned or hinged meaning they are free ' to rotate (as opposed to clamped, when the end tangent angle is fixed, usually horizontally). An interesting finding is that it doesn't matter what the material or ' cross-sectional area is, as long as they're uniform along the entire length. Everything makes the same shape when bent as long as it doesn't cross the threshold ' from elastic to plastic (permanent) deformation (I don't bother to find that limit here, but can be found if the yield stress for a material is known). ' ' Key to the formulas used in this script are elliptic integrals, specifically K(m), the complete elliptic integral of the first kind, and E(m), the complete elliptic ' integral of the second kind. There was a lot of confusion over the 'm' and 'k' parameters for these functions, as some people use them interchangeably, but they are ' not the same. m = k^2 (thus k = Sqrt(m)). I try to use the 'm' parameter exclusively to avoid this confusion. Note that there is a unique 'm' parameter for every ' configuration/shape of the elastica curve. ' ' This script tries to find that unique 'm' parameter based on the inputs. The algorithm starts with a test version of m, evaluates an expression, say 2*E(m)/K(m)-1, ' then compares the result to what it should be (in this case, a known width/length ratio). Iterate until the correct m is found. Once we have m, we can then calculate ' all of the other unknowns, then find points that lie on that curve, then interpolate those points for the actual curve. You can also use Wolfram|Alpha as I did to ' find the m parameter based on the equations in this script (example here: http://tiny.cc/t4tpbx for when say width=45.2 and length=67.1). ' ' Other notes: ' * This script works with negative values for width, which will creat a self-intersecting curve (as it should). The curvature of the elastica starts to break down around ' m=0.95 (~154°), but this script will continue to work until M_MAX, m=0.993 (~169°). If you wish to ignore self-intersecting curves, set ignoreSelfIntersecting to True ' * When the only known values are length and height, it is actually possible for certain ratios of height to length to have two valid m values (thus 2 possible widths ' and angles). This script will return them both. ' * Only the first two valid parameters (of the required ones) will be used, meaning if all four are connected (length, width or a PtB, height, and angle), this script will ' only use length and width (or a PtB). ' * Depending on the magnitude of your inputs (say if they're really small, like if length < 10), you might have to increase the constant ROUNDTO at the bottom ' ' REFERENCES: ' {1} "The elastic rod" by M.E. Pacheco Q. & E. Pina, http://www.scielo.org.mx/pdf/rmfe/v53n2/v53n2a8.pdf ' {2} "An experiment in nonlinear beam theory" by A. Valiente, http://www.deepdyve.com/lp/doc/I3lwnxdfGz , also here: http://tiny.cc/Valiente_AEiNBT ' {3} "Snap buckling, writhing and Loop formation In twisted rods" by V.G.A. GOSS, http://myweb.lsbu.ac.uk/~gossga/thesisFinal.pdf ' {4} "Theory of Elastic Stability" by Stephen Timoshenko, http://www.scribd.com/doc/50402462/Timoshenko-Theory-of-Elastic-Stability (start on p. 76) ' ' INPUT: ' PtA - First anchor point (required) ' PtB - Second anchor point (optional, though 2 out of the 4--length, width, height, angle--need to be specified) ' [note that PtB can be the same as PtA (meaning width would be zero)] ' [also note that if a different width is additionally specified that's not equal to the distance between PtA and PtB, then the end point will not equal PtB anymore] ' Pln - Plane of the bent rod/wire, which bends up in the +y direction. The line between PtA and PtB (if specified) must be parallel to the x-axis of this plane ' ' ** 2 of the following 4 need to be specified ** ' Len - Length of the rod/wire, which needs to be > 0 ' Wid - Width between the endpoints of the curve [note: if PtB is specified in addition, and distance between PtA and PtB <> width, the end point will be relocated ' Ht - Height of the bent rod/wire (when negative, curve will bend downward, relative to the input plane, instead) ' Ang - Inner departure angle or tangent angle (in radians) at the ends of the bent rod/wire. Set up so as width approaches length (thus height approaches zero), angle approaches zero ' ' * Following variables only needed for optional calculating of bending force, not for shape of curve. ' E - Young's modulus (modulus of elasticity) in GPa (=N/m^2) (material-specific. for example, 7075 aluminum is roughly 71.7 GPa) ' I - Second moment of area (or area moment of inertia) in m^4 (cross-section-specific. for example, a hollow rod ' would have I = pi * (outer_diameter^4 - inner_diameter^4) / 32 ' Note: E*I is also known as flexural rigidity or bending stiffness ' ' OUTPUT: ' out - only for debugging messages ' Pts - the list of points that approximate the shape of the elastica ' Crv - the 3rd-degree curve interpolated from those points (with accurate start & end tangents) ' L - the length of the rod/wire ' W - the distance (width) between the endpoints of the rod/wire ' H - the height of the bent rod/wire ' A - the tangent angle at the (start) end of the rod/wire ' F - the force needed to hold the rod/wire in a specific shape (based on the material properties & cross-section) **be sure your units for 'I' match your units for the ' rest of your inputs (length, width, etc.). Also note that the critical buckling load (force) that makes the rod/wire start to bend can be found at height=0 ' ' THANKS TO: ' Mårten Nettelbladt (thegeometryofbending.blogspot.com) ' Daniel Piker (Kangaroo plugin) ' David Rutten (Grasshopper guru) ' Euler & Bernoulli (the O.G.'s) ' ' ----------------------------------------------------------------- Dim ignoreSelfIntersecting As Boolean = False ' set to True if you don't want to output curves where width < 0, which creates a self-intersecting curve Dim inCt As Integer = 0 ' count the number of required parameters that are receiving data Dim length As Double Dim width As System.Object = Nothing ' need to set as Nothing so we can check if it has been assigned a value later Dim height As Double Dim angle As Double Dim m As Double Dim multiple_m As New List(Of Double) Dim AtoB As Line Dim flip_H As Boolean = False ' if height is negative, this flag will be set Dim flip_A As Boolean = False ' if angle is negative, this flag will be set If Not IsSet("Pln") Then Msg("error", "Base plane is not set") Return End If If Not IsSet("PtA") Then Msg("error", "Point A is not set") Return End If If Math.Round(Pln.DistanceTo(PtA), Defined.ROUNDTO) <> 0 Then Msg("error", "Point A is not on the base plane") Return End If Dim refPlane As Plane = Pln ' create a reference plane = input plane and set the origin of it to PtA in case PtA isn't the origin already refPlane.Origin = PtA If IsSet("PtB") Then If Math.Round(Pln.DistanceTo(PtB), Defined.ROUNDTO) <> 0 Then Msg("error", "Point B is not on the base plane") Return End If AtoB = New Line(PtA, PtB) If AtoB.Length <> 0 And Not AtoB.Direction.IsPerpendicularTo(Pln.YAxis) Then Msg("error", "The line between PtA and PtB is not perpendicular to the Y-axis of the specified plane") Return End If inCt += 1 If IsSet("Wid") Then Msg("info", "Wid will override the distance between PtA and PtB. If you do not want this to happen, disconnect PtB or Wid.") width = PtA.DistanceTo(PtB) ' get the width (distance) between PtA and PtB Dim refPtB As Point3d refPlane.RemapToPlaneSpace(PtB, refPtB) If refPtB.X < 0 Then width = -width ' check if PtB is to the left of PtA...if so, width is negative End If If IsSet("Len") Then inCt += 1 If IsSet("Wid") Then inCt += 1 If IsSet("Ht") Then inCt += 1 If IsSet("Ang") Then inCt += 1 If inCt > 2 Then Msg("info", "More parameters set than are required (out of length, width, height, angle). Only using the first two valid ones.") ' check for connected/specified inputs. note: only the first two that it comes across will be used If IsSet("Len") Then ' if length is specified then... If Len <= 0 Then Msg("error", "Length cannot be negative or zero") Return End If If IsSet("Wid") Then ' find height & angle based on length and specified width If Wid > Len Then Msg("error", "Width is greater than length") Return End If If Wid = Len Then ' skip the solver and set the known values height = 0 m = 0 angle = 0 width = Wid Else m = SolveMFromLenWid(Len, Wid) height = Cal_H(Len, m) ' L * Sqrt(m) / K(m) angle = Cal_A(m) ' Acos(1 - 2 * m) width = Wid End If Else If width IsNot Nothing Then ' find height & angle based on length and calculated width (distance between PtA and PtB) If width > Len Then Msg("error", "Width is greater than length") Return End If If width = Len Then ' skip the solver and set the known values height = 0 m = 0 angle = 0 Else m = SolveMFromLenWid(Len, width) height = Cal_H(Len, m) ' L * Sqrt(m) / K(m) angle = Cal_A(m) ' Acos(1 - 2 * m) End If Else If IsSet("Ht") Then ' find width & angle based on length and height ** possible to return 2 results ** If Math.Abs(Ht / Len) > Defined.MAX_HL_RATIO Then Msg("error", "Height not possible with given length") Return End If If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If If Ht = 0 Then ' skip the solver and set the known values width = Len angle = 0 Else multiple_m = SolveMFromLenHt(Len, Ht) ' note that it's possible for two values of m to be found if height is close to max height If multiple_m.Count = 1 Then ' if there's only one m value returned, calculate the width & angle here. we'll deal with multiple m values later m = multiple_m.Item(0) width = Cal_W(Len, m) ' L * (2 * E(m) / K(m) - 1) angle = Cal_A(m) ' Acos(1 - 2 * m) End If End If height = Ht Else If IsSet("Ang") Then ' find width & height based on length and angle If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then ' skip the solver and set the known values width = Len height = 0 Else width = Cal_W(Len, m) ' L * (2 * E(m) / K(m) - 1) height = Cal_H(Len, m) ' L * Sqrt(m) / K(m) End If angle = Ang Else Msg("error", "Need to specify one more parameter in addition to length") Return End If length = Len Else If IsSet("Wid") Then ' if width is specified then... If IsSet("Ht") Then ' find length & angle based on specified width and height If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If If Ht = 0 Then ' skip the solver and set the known values length = Wid angle = 0 Else m = SolveMFromWidHt(Wid, Ht) length = Cal_L(Ht, m) ' h * K(m) / Sqrt(m) angle = Cal_A(m) ' Acos(1 - 2 * m) End If height = Ht Else If IsSet("Ang") Then ' find length & height based on specified width and angle If Wid = 0 Then Msg("error", "Curve not possible with width = 0 and an angle as inputs") Return End If If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then ' skip the solver and set the known values length = Wid height = 0 Else length = Wid / (2 * EllipticE(m) / EllipticK(m) - 1) If length < 0 Then Msg("error", "Curve not possible at specified width and angle (calculated length is negative)") Return End If height = Cal_H(length, m) ' L * Sqrt(m) / K(m) End If angle = Ang Else Msg("error", "Need to specify one more parameter in addition to width (Wid)") Return End If width = Wid Else If width IsNot Nothing Then ' if width is determined by PtA and PtB then... If IsSet("Ht") Then ' find length & angle based on calculated width and height If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If If Ht = 0 Then ' skip the solver and set the known values length = width angle = 0 Else m = SolveMFromWidHt(width, Ht) length = Cal_L(Ht, m) ' h * K(m) / Sqrt(m) angle = Cal_A(m) ' Acos(1 - 2 * m) End If height = Ht Else If IsSet("Ang") Then ' find length & height based on calculated width and angle If width = 0 Then Msg("error", "Curve not possible with width = 0 and an angle as inputs") Return End If If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then ' skip the solver and set the known values length = width height = 0 Else length = width / (2 * EllipticE(m) / EllipticK(m) - 1) If length < 0 Then Msg("error", "Curve not possible at specified width and angle (calculated length is negative)") Return End If height = Cal_H(length, m) ' L * Sqrt(m) / K(m) End If angle = Ang Else Msg("error", "Need to specify one more parameter in addition to PtA and PtB") Return End If Else If IsSet("Ht") Then ' if height is specified then... If IsSet("Ang") Then ' find length & width based on height and angle If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_H = True flip_A = True End If If Ht = 0 Then Msg("error", "Height can't = 0 if only height and angle are specified") Return Else If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = Not flip_A flip_H = Not flip_H End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then Msg("error", "Angle can't = 0 if only height and angle are specified") Return Else length = Cal_L(Ht, m) ' h * K(m) / Sqrt(m) width = Cal_W(length, m) ' L * (2 * E(m) / K(m) - 1) End If angle = Ang End If height = Ht Else Msg("error", "Need to specify one more parameter in addition to height") Return End If Else If IsSet("Ang") Then Msg("error", "Need to specify one more parameter in addition to angle") Return Else Msg("error", "Need to specify two of the four parameters: length, width (or PtB), height, and angle") Return End If If m > Defined.M_MAX Then Msg("error", "Form of curve not solvable with current algorithm and given inputs") Return End If refPlane.Origin = refPlane.PointAt(width / 2, 0, 0) ' adjust the origin of the reference plane so that the curve is centered about the y-axis (start of the curve is at x = -width/2) If multiple_m.Count > 1 Then ' if there is more than one m value returned, calculate the width, angle, and curve for each Dim multi_pts As New DataTree(Of Point3d) Dim multi_crv As New List(Of Curve) Dim tmp_pts As New List(Of Point3d) Dim multi_W, multi_A, multi_F As New List(Of Double) Dim j As Integer = 0 ' used for creating a new branch (GH_Path) for storing pts which is itself a list of points For Each m_val As Double In multiple_m width = Cal_W(length, m_val) 'length * (2 * EllipticE(m_val) / EllipticK(m_val) - 1) If width < 0 And ignoreSelfIntersecting Then Msg("warning", "One curve is self-intersecting. To enable these, set ignoreSelfIntersecting to False") Continue For End If If m_val >= Defined.M_SKETCHY Then Msg("info", "Accuracy of the curve whose width = " & Math.Round(width, 4) & " is not guaranteed") angle = Cal_A(m_val) 'Math.Asin(2 * m_val - 1) refPlane.Origin = refPlane.PointAt(width / 2, 0, 0) ' adjust the origin of the reference plane so that the curve is centered about the y-axis (start of the curve is at x = -width/2) tmp_pts = FindBendForm(length, width, m_val, angle, refPlane) multi_pts.AddRange(tmp_pts, New GH_Path(j)) multi_crv.Add(MakeCurve(tmp_pts, angle, refPlane)) multi_W.Add(width) If flip_A Then angle = -angle multi_A.Add(angle) E = E * 10 ^ 9 ' Young's modulus input E is in GPa, so we convert to Pa here (= N/m^2) multi_F.Add(EllipticK(m_val) ^ 2 * E * I / length ^ 2) ' from reference {4} pg. 79 j += 1 refPlane.Origin = PtA ' reset the reference plane origin to PtA for the next m_val 'Print("length=" & length & ", width=" & width & ", height=" & height & ", angle=" & angle & ", m=" & m_val & ", k=" & Math.Sqrt(m_val) & ", w/L=" & width / length & ", h/L=" & height / length & ", w/h=" & width / height) Next ' assign the outputs Pts = multi_pts Crv = multi_crv L = length W = multi_W If flip_H Then height = -height H = height A = multi_A F = multi_F Else ' only deal with the single m value If m >= Defined.M_SKETCHY Then Msg("info", "Accuracy of the curve at these parameters is not guaranteed") If width < 0 And ignoreSelfIntersecting Then Msg("error", "Curve is self-intersecting. To enable these, set ignoreSelfIntersecting to False") Return End If Pts = FindBendForm(length, width, m, angle, refPlane) Crv = MakeCurve(pts, angle, refPlane) L = length W = width If flip_H Then height = -height H = height If flip_A Then angle = -angle A = angle E = E * 10 ^ 9 ' Young's modulus input E is in GPa, so we convert to Pa here (= N/m^2) F = EllipticK(m) ^ 2 * E * I / length ^ 2 ' from reference {4} pg. 79. Note: the critical buckling (that makes the rod/wire start to bend) can be found at height=0 (width=length) 'height = Math.Sqrt(((2 * Len / 5) ^ 2 - ((Wid - Len / 5) / 2) ^ 2) ' quick approximation discovered by Mårten of 'Geometry of Bending' fame ( http://tiny.cc/it2pbx ) 'width = (Len +/- 2 * Math.Sqrt(4 * Len ^ 2 - 25 * Ht ^ 2)) / 5 ' derived from above 'length = (2 * Math.Sqrt(15 * Ht ^ 2 + 4 * Wid ^ 2) - Wid) / 3 ' derived from above 'Print("length=" & length & ", width=" & width & ", height=" & height & ", angle=" & angle & ", m=" & m & ", k=" & Math.Sqrt(m) & ", w/L=" & width / length & ", h/L=" & height / length & ", w/h=" & width / height) End If 2806 1380 84 184 2848 1472 9 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 8 3ede854e-c753-40eb-84cb-b48008f14fd4 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 true Script Variable PtA 89fe7d9c-b998-43b5-8788-30064fd07e47 PtA PtA true 0 true c54e879a-bb6c-47c6-b366-aaa5a16a426a 83dc0e24-1c12-445e-8647-dffc452baa6a 2 e1937b56-b1da-4c12-8bd8-e34ee81746ef 2808 1382 25 20 2822 1392 true Script Variable PtB d6635d82-91c7-476c-acf5-3f2feae5d91b PtB PtB true 0 true 72b7092f-b51c-4a8b-b457-5cc6f58de91d 5c0c8191-e5d5-44d8-9b74-ad7b50095c12 2 e1937b56-b1da-4c12-8bd8-e34ee81746ef 2808 1402 25 20 2822 1412 true Script Variable Pln 8a5ea10d-8baa-47cd-8970-989b44d90853 Pln Pln true 0 true f5ef3118-8c76-4765-bdd9-16087c3ebb2d 1 3897522d-58e9-4d60-b38c-978ddacfedd8 2808 1422 25 20 2822 1432 true Script Variable Len 4adfaa55-4523-43d7-9887-fcfc9daa5d32 Len Len true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 2808 1442 25 20 2822 1452 true Script Variable Wid 8d352101-30bc-4590-8ef7-3bebef383668 Wid Wid true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 2808 1462 25 20 2822 1472 true Script Variable Ht ed97f943-b85a-4bc0-84db-64b077475aca Ht Ht true 0 true cd2f5af0-b741-48d5-8bbd-e9695d5278d8 1 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 2808 1482 25 20 2822 1492 true Script Variable Ang c30d75f7-bb99-4be5-88aa-f992b8386696 Ang Ang true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 2808 1502 25 20 2822 1512 true Script Variable E 33bfc033-c0d3-4ebb-a5b0-659df3a0b51e E E true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 2808 1522 25 20 2822 1532 true Script Variable I 66f1e54b-d2c1-43dd-8716-e6fa7a1ddeca I I true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 2808 1542 25 20 2822 1552 1 Print, Reflect and Error streams be85f9e8-bde2-44f9-9bb8-80b1010c9d68 out out false 0 2863 1382 25 22 2875.5 1393.25 Output parameter Pts bacf233a-c717-41ce-b241-8552418f09f9 Pts Pts false 0 2863 1404 25 23 2875.5 1415.75 Output parameter Crv 4fa97325-f527-43c0-a006-78cf944c5e40 Crv Crv false 0 2863 1427 25 22 2875.5 1438.25 Output parameter L b9ed95d2-8f4a-49e9-a734-c73e21c200b4 L L false 0 2863 1449 25 23 2875.5 1460.75 Output parameter W 264cbae0-f043-490d-9121-0e45630a0b2f W W false 0 2863 1472 25 22 2875.5 1483.25 Output parameter H 1578bf3a-8211-4a77-85d0-3cd951789283 H H false 0 2863 1494 25 23 2875.5 1505.75 Output parameter A 97c17859-cbbf-4962-a448-d8665c3e706a A A false 0 2863 1517 25 22 2875.5 1528.25 Output parameter F 8f3ba33f-2383-4ae9-b911-b9b6e4553a35 F F false 0 2863 1539 25 23 2875.5 1550.75 c277f778-6fdf-4890-8f78-347efb23c406 Pipe Create a pipe surface around a rail curve. a18b4542-f317-4532-9634-9ef245df4e2c Pipe Pipe 3076 1319 64 64 3107 1351 Base curve 9be76020-39f2-465f-b508-f09d3f48a425 Curve C false 4fa97325-f527-43c0-a006-78cf944c5e40 1 3078 1321 14 20 3086.5 1331 Pipe radius 0f317bff-3048-480b-865a-13a2e1cb5106 Radius R false f2abb0db-802c-4f59-83cb-393711b4a3d9 1 3078 1341 14 20 3086.5 1351 1 1 {0} 1 Specifies the type of caps (0=None, 1=Flat, 2=Round) 38981e04-97f9-4736-a46b-c55825f5b8fe Caps E false 0 3078 1361 14 20 3086.5 1371 1 1 {0} 0 1 Resulting Pipe d118f269-0338-4d1d-b06d-018819a25f1a Pipe P false 0 3122 1321 16 60 3130 1351 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values f2abb0db-802c-4f59-83cb-393711b4a3d9 Panel false 0 0 .25 2955 1314 50 20 0 0 0 2955.289 1314.952 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 26ed0e55-cf0a-436e-9447-805704984e62 Panel false 0 087cd096-ba72-4a81-a462-a9ce3ce43ec4 1 Double click to edit panel content… 3045 1553 105 55 0 0 0 3045.485 1553.607 255;255;250;90 true true true false false true 0d77c51e-584f-44e8-aed2-c2ddf4803888 Degrees Convert an angle specified in radians to degrees bb620c76-8624-412c-8f17-4aa82e09f1b9 Degrees Deg 2955 1562 64 28 2985 1576 Angle in radians e10ae9f2-834e-4fd3-afbb-6416a8f9ca28 Radians R false 97c17859-cbbf-4962-a448-d8665c3e706a 1 2957 1564 13 24 2965 1576 Angle in degrees 087cd096-ba72-4a81-a462-a9ce3ce43ec4 Degrees D false 0 3000 1564 17 24 3008.5 1576 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 4ddb17a4-9e5f-4fb4-9f06-84399d240517 Panel false 0 b9ed95d2-8f4a-49e9-a734-c73e21c200b4 1 Double click to edit panel content… 2941 1424 97 54 0 0 0 2941.968 1424.592 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 97a1d833-23ef-4dd7-a8f6-0467955bf4f5 Panel false 0 264cbae0-f043-490d-9121-0e45630a0b2f 1 Double click to edit panel content… 3053 1458 105 55 0 0 0 3053.527 1458.041 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 8e36a5c6-61f4-4366-8856-2c787fd9a806 Panel false 0 1578bf3a-8211-4a77-85d0-3cd951789283 1 Double click to edit panel content… 2941 1493 97 55 0 0 0 2941.552 1493.67 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 0903a6c2-e102-41a9-8326-3f9533f91f9f Panel false 0 0 -150 1440 1185 50 20 0 0 0 1440.106 1185.658 255;255;250;90 true true true false false true 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 17a61eca-a95a-4f8b-aa55-052523e686bc Panel false 0 0 150 1441 1215 50 20 0 0 0 1441.106 1215.658 255;255;250;90 true true true false false true d1a28e95-cf96-4936-bf34-8bf142d731bf Construct Domain Create a numeric domain from two numeric extremes. 14534673-98f6-45e8-b11f-39da628bc4ca Construct Domain Dom 2193 764 61 44 2224 786 Start value of numeric domain c60598bb-4ac8-4310-9f9f-0d75cea21cfe Domain start A false 64b83d9b-5ae1-4022-b79e-c9135d3cdfc6 1 2195 766 14 20 2203.5 776 1 1 {0} 0 End value of numeric domain c4f2cdcf-3ac3-420a-925f-c2653c042270 Domain end B false 2398bd00-b514-4723-b31f-c436e1ae908b 1 2195 786 14 20 2203.5 796 1 1 {0} 1 Numeric domain between {A} and {B} 035d494c-d9fa-434d-874c-206716be82af Domain I false 0 2239 766 13 40 2245.5 786 9445ca40-cc73-4861-a455-146308676855 Range Create a range of numbers. fbb4ab09-bb2a-42d6-a9a9-345b5f2473bc Range Range 2295 775 64 44 2326 797 Domain of numeric range 0dd6e136-7641-43ed-94ff-d87c8b3a4a7f Domain D false 035d494c-d9fa-434d-874c-206716be82af 1 2297 777 14 20 2305.5 787 1 1 {0} 0 1 Number of steps b991e2eb-aa24-4a37-b83b-4de89b217793 Steps N false cc8db470-d2c6-4bbc-92b0-9305165143f7 1 2297 797 14 20 2305.5 807 1 1 {0} 10 1 Range of numbers 9b2fc3d6-5abb-46f8-a49e-0ae9e8dc1647 Range R false 0 2341 777 16 40 2349 797 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values cc8db470-d2c6-4bbc-92b0-9305165143f7 Number Slider num curves false 0 1969 831 284 20 1969.186 831.0843 0 1 0 400 1 0 200 079bd9bd-54a0-41d4-98af-db999015f63d VB Script Private Function IsSet(ByVal param As String) As Boolean ' Check if an input parameter has data Dim i As Integer = Component.Params.IndexOfInputParam(param) If i > -1 Then Return Component.Params.Input.ElementAt(i).DataType > 1 ' input parameter DataType of 1 means it's not receiving input (internal or external) Else Msg("error", "Input parameter '" & param & "' not found") Return False End If End Function Private Sub Msg(ByVal type As String, ByVal msg As String) ' Output an error, warning, or informational message Select Case type Case "error" Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, msg) Print("Error: " & msg) Case "warning" Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, msg) Print("Warning: " & msg) Case "info" Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, msg) Print(msg) End Select End Sub ' Solve for the m parameter from length and width (reference {1} equation (34), except b = width and K(k) and E(k) should be K(m) and E(m)) Private Function SolveMFromLenWid(ByVal L As Double, ByVal w As Double) As Double If w = 0 Then Return Defined.M_ZERO_W ' for the boundry condition width = 0, bypass the function and return the known m value End If Dim n As Integer = 1 ' Iteration counter (quit if >MAXIT) Dim lower As Double = 0 ' m must be within this range Dim upper As Double = 1 Dim m As Double Dim cwl As Double Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 cwl = 2 * EllipticE(m) / EllipticK(m) - 1 ' calculate w/L with the test value of m If cwl < w / L Then ' compares the calculated w/L with the actual w/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop Return m End Function ' Solve for the m parameter from length and height (reference {1} equation (33), except K(k) should be K(m) and k = sqrt(m)) ' Note that it's actually possible to find 2 valid values for m (hence 2 width values) at certain height values Private Function SolveMFromLenHt(ByVal L As Double, ByVal h As Double) As List(Of Double) Dim n As Integer = 1 ' Iteration counter (quit if >MAXIT) Dim lower As Double = 0 ' m must be within this range Dim upper As Double = 1 Dim twoWidths As Boolean = h / L >= Defined.DOUBLE_W_HL_RATIO And h / L < Defined.MAX_HL_RATIO ' check to see if h/L is within the range where 2 solutions for the width are possible Dim m As Double Dim mult_m As New List(Of Double) Dim chl As Double If twoWidths Then ' find the first of two possible solutions for m with the following limits: lower = Defined.M_DOUBLE_W ' see constants at bottom of script upper = Defined.M_MAXHEIGHT ' see constants at bottom of script Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 chl = Math.Sqrt(m) / EllipticK(m) ' calculate h/L with the test value of m If chl > h / L Then ' compares the calculated h/L with the actual h/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop mult_m.Add(m) ' then find the second of two possible solutions for m with the following limits: lower = Defined.M_MAXHEIGHT ' see constants at bottom of script upper = 1 Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 chl = Math.Sqrt(m) / EllipticK(m) ' calculate h/L with the test value of m If chl < h / L Then ' compares the calculated h/L with the actual h/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop If m <= Defined.M_MAX Then ' return this m parameter only if it falls within the maximum useful value (above which the curve breaks down) mult_m.Add(m) End If Else ' find the one possible solution for the m parameter upper = Defined.M_DOUBLE_W ' limit the upper end of the search to the maximum value of m for which only one solution exists Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 chl = Math.Sqrt(m) / EllipticK(m) ' calculate h/L with the test value of m If chl > h / L Then ' compares the calculated h/L with the actual h/L then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop mult_m.Add(m) End If Return mult_m End Function ' Solve for the m parameter from width and height (derived from reference {1} equations (33) and (34) with same notes as above) Private Function SolveMFromWidHt(ByVal w As Double, ByVal h As Double) As Double Dim n As Integer = 1 ' Iteration counter (quit if >MAXIT) Dim lower As Double = 0 ' m must be within this range Dim upper As Double = 1 Dim m As Double Dim cwh As Double Do While (upper - lower) > Defined.MAXERR AndAlso (n) < Defined.MAXIT ' Repeat until range narrow enough or MAXIT m = (upper + lower) / 2 cwh = (2 * EllipticE(m) - EllipticK(m)) / Math.Sqrt(m) ' calculate w/h with the test value of m If cwh < w / h Then ' compares the calculated w/h with the actual w/h then narrows the range of possible m upper = m Else lower = m End If n += 1 Loop Return m End Function ' Calculate length based on height and an m parameter, derived from reference {1} equation (33), except K(k) should be K(m) and k = sqrt(m) Private Function Cal_L(ByVal h As Double, ByVal m As Double) As Double Return h * EllipticK(m) / Math.Sqrt(m) End Function ' Calculate width based on length and an m parameter, derived from reference {1} equation (34), except b = width and K(k) and E(k) should be K(m) and E(m) Private Function Cal_W(ByVal L As Double, ByVal m As Double) As Double Return L * (2 * EllipticE(m) / EllipticK(m) - 1) End Function ' Calculate height based on length and an m parameter, from reference {1} equation (33), except K(k) should be K(m) and k = sqrt(m) Private Function Cal_H(ByVal L As Double, ByVal m As Double) As Double Return L * Math.Sqrt(m) / EllipticK(m) End Function ' Calculate the unique m parameter based on a start tangent angle, from reference {2}, just above equation (9a), that states k = Sin(angle / 2 + Pi / 4), ' but as m = k^2 and due to this script's need for an angle rotated 90° versus the one in reference {1}, the following formula is the result ' New note: verified by reference {4}, pg. 78 at the bottom Private Function Cal_M(ByVal a As Double) As Double Return (1 - Math.Cos(a)) / 2 ' equal to Sin^2(a/2) too End Function ' Calculate start tangent angle based on an m parameter, derived from above formula Private Function Cal_A(ByVal m As Double) As Double Return Math.Acos(1 - 2 * m) End Function ' This is the heart of this script, taking the found (or specified) length, width, and angle values along with the found m parameter to create ' a list of points that approximate the shape or form of the elastica. It works by finding the x and y coordinates (which are reversed versus ' the original equations (12a) and (12b) from reference {2} due to the 90° difference in orientation) based on the tangent angle along the curve. ' See reference {2} for more details on how they derived it. Note that to simplify things, the algorithm only calculates the points for half of the ' curve, then mirrors those points along the y-axis. Private Function FindBendForm(ByVal L As Double, ByVal w As Double, ByVal m As Double, ByVal ang As Double, ByVal refPln As Plane) As List(Of Point3d) L = L / 2 ' because the below algorithm is based on the formulas in reference {2} for only half of the curve w = w / 2 ' same If ang = 0 Then ' if angle (and height) = 0, then simply return the start and end points of the straight line Dim out As New List(Of Point3d) out.Add(refPln.PointAt(w, 0, 0)) out.Add(refPln.PointAt(-w, 0, 0)) Return out End If Dim x As Double Dim y As Double Dim halfCurvePts As New List(Of Point3d) Dim fullCurvePts As New List(Of Point3d) Dim translatedPts As New List(Of Point3d) ang -= Math.PI / 2 ' a hack to allow this algorithm to work, since the original curve in paper {2} was rotated 90° Dim angB As Double = ang + (-Math.PI / 2 - ang) / Defined.CURVEDIVS ' angB is the 'lowercase theta' which should be in formula {2}(12b) as the interval ' start [a typo...see equation(3)]. It's necessary to start angB at ang + [interval] instead of just ang due to integration failing at angB = ang halfCurvePts.Add(New Point3d(w, 0, 0)) ' start with this known initial point, as integration will fail when angB = ang ' each point {x, y} is calculated from the tangent angle, angB, that occurs at each point (which is why this iterates from ~ang to -pi/2, the known end condition) Do While Math.Round(angB, Defined.ROUNDTO) >= Math.Round(-Math.PI / 2, Defined.ROUNDTO) y = (Math.Sqrt(2) * Math.Sqrt(Math.Sin(ang) - Math.Sin(angB)) * (w + L)) / (2 * EllipticE(m)) ' note that x and y are swapped vs. (12a) and (12b) x = (L / (Math.Sqrt(2) * EllipticK(m))) * Simpson(angB, -Math.PI / 2, 500, ang) ' calculate the Simpson approximation of the integral (function f below) ' over the interval angB ('lowercase theta') to -pi/2. side note: is 500 too few iterations for the Simson algorithm? If Math.Round(x, Defined.ROUNDTO) = 0 Then x = 0 halfCurvePts.Add(New Point3d(x, y, 0)) angB += (-Math.PI / 2 - ang) / Defined.CURVEDIVS ' onto the next tangent angle Loop ' After finding the x and y values for half of the curve, add the {-x, y} values for the rest of the curve For Each point As Point3d In halfCurvePts If Math.Round(point.X, Defined.ROUNDTO) = 0 Then If Math.Round(point.Y, Defined.ROUNDTO) = 0 Then fullCurvePts.Add(New Point3d(0, 0, 0)) ' special case when width = 0: when x = 0, only duplicate the point when y = 0 too End If Else fullCurvePts.Add(New Point3d(-point.X, point.Y, 0)) End If Next halfCurvePts.Reverse fullCurvePts.AddRange(halfCurvePts) For Each p As Point3d In fullCurvePts translatedPts.Add(refPln.PointAt(p.X, p.Y, p.Z)) ' translate the points from the reference plane to the world plane Next Return translatedPts End Function ' Interpolates the points from FindBendForm to create the Elastica curve. Uses start & end tangents for greater accuracy. Private Function MakeCurve(ByVal pts As List(Of Point3d), ByVal ang As Double, ByVal refPln As Plane) As Curve If ang <> 0 Then Dim ts, te As New Vector3d(refPln.XAxis) ts.Rotate(ang, refPln.ZAxis) te.Rotate(-ang, refPln.ZAxis) Return Curve.CreateInterpolatedCurve(pts, 3, CurveKnotStyle.Chord, ts, te) ' 3rd degree curve with 'Chord' Knot Style Else Return Curve.CreateInterpolatedCurve(pts, 3) ' if angle (and height) = 0, then simply interpolate the straight line (no start/end tangents) End If End Function ' Implements the Simpson approximation for an integral of function f below Public Function Simpson(a As Double, b As Double, n As Integer, theta As Double) As Double 'n should be an even number Dim j As Integer, s1 As Double, s2 As Double, h As Double h = (b - a) / n s1 = 0 s2 = 0 For j = 1 To n - 1 Step 2 s1 = s1 + fn(a + j * h, theta) Next j For j = 2 To n - 2 Step 2 s2 = s2 + fn(a + j * h, theta) Next j Simpson = h / 3 * (fn(a, theta) + 4 * s1 + 2 * s2 + fn(b, theta)) End Function ' Specific calculation for the above integration Public Function fn(x As Double, theta As Double) As Double fn = Math.Sin(x) / (Math.Sqrt(Math.Sin(theta) - Math.Sin(x))) ' from reference {2} formula (12b) End Function ' Return the Complete Elliptic integral of the 1st kind ' Abramowitz and Stegun p.591, formula 17.3.11 ' Code from http://www.codeproject.com/Articles/566614/Elliptic-integrals Public Function EllipticK(ByVal m As Double) As Double Dim sum, term, above, below As Double sum = 1 term = 1 above = 1 below = 2 For i As Integer = 1 To 100 term *= above / below sum += Math.Pow(m, i) * Math.Pow(term, 2) above += 2 below += 2 Next sum *= 0.5 * Math.PI Return sum End Function ' Return the Complete Elliptic integral of the 2nd kind ' Abramowitz and Stegun p.591, formula 17.3.12 ' Code from http://www.codeproject.com/Articles/566614/Elliptic-integrals Public Function EllipticE(ByVal m As Double) As Double Dim sum, term, above, below As Double sum = 1 term = 1 above = 1 below = 2 For i As Integer = 1 To 100 term *= above / below sum -= Math.Pow(m, i) * Math.Pow(term, 2) / above above += 2 below += 2 Next sum *= 0.5 * Math.PI Return sum End Function Friend Partial NotInheritable Class Defined Private Sub New() End Sub ' Note: most of these values for m and h/L ratio were found with Wolfram Alpha and either specific intercepts (x=0) or local minima/maxima. They should be constant. Public Const M_SKETCHY As Double = 0.95 ' value of the m parameter where the curvature near the ends of the curve gets wonky Public Const M_MAX As Double = 0.993 ' maximum useful value of the m parameter, above which this algorithm for the form of the curve breaks down Public Const M_ZERO_W As Double = 0.826114765984970336 ' value of the m parameter when width = 0 Public Const M_MAXHEIGHT As Double = 0.701327460663101223 ' value of the m parameter at maximum possible height of the bent rod/wire Public Const M_DOUBLE_W As Double = 0.180254422335013983 ' minimum value of the m parameter when two width values are possible for a given height and length Public Const DOUBLE_W_HL_RATIO As Double = 0.257342117984635757 ' value of the height/length ratio above which there are two possible width values Public Const MAX_HL_RATIO As Double = 0.403140189705650243 ' maximum possible value of the height/length ratio Public Const MAXERR As Double = 0.0000000001 ' error tolerance Public Const MAXIT As Integer = 100 ' maximum number of iterations Public Const ROUNDTO As Integer = 10 ' number of decimal places to round off to Public Const CURVEDIVS As Integer = 50 ' number of sample points for building the curve (or half-curve as it were) End Class A VB.NET scriptable component 98 86 true 63218c71-7758-4168-b163-745e456fb525 VB Script VB true 0 ' ----------------------------------------------------------------- ' Elastic Bending Script by Will McElwain ' Created February 2014 ' ' DESCRIPTION: ' This beast creates the so-called 'elastica curve', the shape a long, thin rod or wire makes when it is bent elastically (i.e. not permanently). In this case, force ' is assumed to only be applied horizontally (which would be in line with the rod at rest) and both ends are assumed to be pinned or hinged meaning they are free ' to rotate (as opposed to clamped, when the end tangent angle is fixed, usually horizontally). An interesting finding is that it doesn't matter what the material or ' cross-sectional area is, as long as they're uniform along the entire length. Everything makes the same shape when bent as long as it doesn't cross the threshold ' from elastic to plastic (permanent) deformation (I don't bother to find that limit here, but can be found if the yield stress for a material is known). ' ' Key to the formulas used in this script are elliptic integrals, specifically K(m), the complete elliptic integral of the first kind, and E(m), the complete elliptic ' integral of the second kind. There was a lot of confusion over the 'm' and 'k' parameters for these functions, as some people use them interchangeably, but they are ' not the same. m = k^2 (thus k = Sqrt(m)). I try to use the 'm' parameter exclusively to avoid this confusion. Note that there is a unique 'm' parameter for every ' configuration/shape of the elastica curve. ' ' This script tries to find that unique 'm' parameter based on the inputs. The algorithm starts with a test version of m, evaluates an expression, say 2*E(m)/K(m)-1, ' then compares the result to what it should be (in this case, a known width/length ratio). Iterate until the correct m is found. Once we have m, we can then calculate ' all of the other unknowns, then find points that lie on that curve, then interpolate those points for the actual curve. You can also use Wolfram|Alpha as I did to ' find the m parameter based on the equations in this script (example here: http://tiny.cc/t4tpbx for when say width=45.2 and length=67.1). ' ' Other notes: ' * This script works with negative values for width, which will creat a self-intersecting curve (as it should). The curvature of the elastica starts to break down around ' m=0.95 (~154°), but this script will continue to work until M_MAX, m=0.993 (~169°). If you wish to ignore self-intersecting curves, set ignoreSelfIntersecting to True ' * When the only known values are length and height, it is actually possible for certain ratios of height to length to have two valid m values (thus 2 possible widths ' and angles). This script will return them both. ' * Only the first two valid parameters (of the required ones) will be used, meaning if all four are connected (length, width or a PtB, height, and angle), this script will ' only use length and width (or a PtB). ' * Depending on the magnitude of your inputs (say if they're really small, like if length < 10), you might have to increase the constant ROUNDTO at the bottom ' ' REFERENCES: ' {1} "The elastic rod" by M.E. Pacheco Q. & E. Pina, http://www.scielo.org.mx/pdf/rmfe/v53n2/v53n2a8.pdf ' {2} "An experiment in nonlinear beam theory" by A. Valiente, http://www.deepdyve.com/lp/doc/I3lwnxdfGz , also here: http://tiny.cc/Valiente_AEiNBT ' {3} "Snap buckling, writhing and Loop formation In twisted rods" by V.G.A. GOSS, http://myweb.lsbu.ac.uk/~gossga/thesisFinal.pdf ' {4} "Theory of Elastic Stability" by Stephen Timoshenko, http://www.scribd.com/doc/50402462/Timoshenko-Theory-of-Elastic-Stability (start on p. 76) ' ' INPUT: ' PtA - First anchor point (required) ' PtB - Second anchor point (optional, though 2 out of the 4--length, width, height, angle--need to be specified) ' [note that PtB can be the same as PtA (meaning width would be zero)] ' [also note that if a different width is additionally specified that's not equal to the distance between PtA and PtB, then the end point will not equal PtB anymore] ' Pln - Plane of the bent rod/wire, which bends up in the +y direction. The line between PtA and PtB (if specified) must be parallel to the x-axis of this plane ' ' ** 2 of the following 4 need to be specified ** ' Len - Length of the rod/wire, which needs to be > 0 ' Wid - Width between the endpoints of the curve [note: if PtB is specified in addition, and distance between PtA and PtB <> width, the end point will be relocated ' Ht - Height of the bent rod/wire (when negative, curve will bend downward, relative to the input plane, instead) ' Ang - Inner departure angle or tangent angle (in radians) at the ends of the bent rod/wire. Set up so as width approaches length (thus height approaches zero), angle approaches zero ' ' * Following variables only needed for optional calculating of bending force, not for shape of curve. ' E - Young's modulus (modulus of elasticity) in GPa (=N/m^2) (material-specific. for example, 7075 aluminum is roughly 71.7 GPa) ' I - Second moment of area (or area moment of inertia) in m^4 (cross-section-specific. for example, a hollow rod ' would have I = pi * (outer_diameter^4 - inner_diameter^4) / 32 ' Note: E*I is also known as flexural rigidity or bending stiffness ' ' OUTPUT: ' out - only for debugging messages ' Pts - the list of points that approximate the shape of the elastica ' Crv - the 3rd-degree curve interpolated from those points (with accurate start & end tangents) ' L - the length of the rod/wire ' W - the distance (width) between the endpoints of the rod/wire ' H - the height of the bent rod/wire ' A - the tangent angle at the (start) end of the rod/wire ' F - the force needed to hold the rod/wire in a specific shape (based on the material properties & cross-section) **be sure your units for 'I' match your units for the ' rest of your inputs (length, width, etc.). Also note that the critical buckling load (force) that makes the rod/wire start to bend can be found at height=0 ' ' THANKS TO: ' Mårten Nettelbladt (thegeometryofbending.blogspot.com) ' Daniel Piker (Kangaroo plugin) ' David Rutten (Grasshopper guru) ' Euler & Bernoulli (the O.G.'s) ' ' ----------------------------------------------------------------- Dim ignoreSelfIntersecting As Boolean = False ' set to True if you don't want to output curves where width < 0, which creates a self-intersecting curve Dim inCt As Integer = 0 ' count the number of required parameters that are receiving data Dim length As Double Dim width As System.Object = Nothing ' need to set as Nothing so we can check if it has been assigned a value later Dim height As Double Dim angle As Double Dim m As Double Dim multiple_m As New List(Of Double) Dim AtoB As Line Dim flip_H As Boolean = False ' if height is negative, this flag will be set Dim flip_A As Boolean = False ' if angle is negative, this flag will be set If Not IsSet("Pln") Then Msg("error", "Base plane is not set") Return End If If Not IsSet("PtA") Then Msg("error", "Point A is not set") Return End If If Math.Round(Pln.DistanceTo(PtA), Defined.ROUNDTO) <> 0 Then Msg("error", "Point A is not on the base plane") Return End If Dim refPlane As Plane = Pln ' create a reference plane = input plane and set the origin of it to PtA in case PtA isn't the origin already refPlane.Origin = PtA If IsSet("PtB") Then If Math.Round(Pln.DistanceTo(PtB), Defined.ROUNDTO) <> 0 Then Msg("error", "Point B is not on the base plane") Return End If AtoB = New Line(PtA, PtB) If AtoB.Length <> 0 And Not AtoB.Direction.IsPerpendicularTo(Pln.YAxis) Then Msg("error", "The line between PtA and PtB is not perpendicular to the Y-axis of the specified plane") Return End If inCt += 1 If IsSet("Wid") Then Msg("info", "Wid will override the distance between PtA and PtB. If you do not want this to happen, disconnect PtB or Wid.") width = PtA.DistanceTo(PtB) ' get the width (distance) between PtA and PtB Dim refPtB As Point3d refPlane.RemapToPlaneSpace(PtB, refPtB) If refPtB.X < 0 Then width = -width ' check if PtB is to the left of PtA...if so, width is negative End If If IsSet("Len") Then inCt += 1 If IsSet("Wid") Then inCt += 1 If IsSet("Ht") Then inCt += 1 If IsSet("Ang") Then inCt += 1 If inCt > 2 Then Msg("info", "More parameters set than are required (out of length, width, height, angle). Only using the first two valid ones.") ' check for connected/specified inputs. note: only the first two that it comes across will be used If IsSet("Len") Then ' if length is specified then... If Len <= 0 Then Msg("error", "Length cannot be negative or zero") Return End If If IsSet("Wid") Then ' find height & angle based on length and specified width If Wid > Len Then Msg("error", "Width is greater than length") Return End If If Wid = Len Then ' skip the solver and set the known values height = 0 m = 0 angle = 0 width = Wid Else m = SolveMFromLenWid(Len, Wid) height = Cal_H(Len, m) ' L * Sqrt(m) / K(m) angle = Cal_A(m) ' Acos(1 - 2 * m) width = Wid End If Else If width IsNot Nothing Then ' find height & angle based on length and calculated width (distance between PtA and PtB) If width > Len Then Msg("error", "Width is greater than length") Return End If If width = Len Then ' skip the solver and set the known values height = 0 m = 0 angle = 0 Else m = SolveMFromLenWid(Len, width) height = Cal_H(Len, m) ' L * Sqrt(m) / K(m) angle = Cal_A(m) ' Acos(1 - 2 * m) End If Else If IsSet("Ht") Then ' find width & angle based on length and height ** possible to return 2 results ** If Math.Abs(Ht / Len) > Defined.MAX_HL_RATIO Then Msg("error", "Height not possible with given length") Return End If If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If If Ht = 0 Then ' skip the solver and set the known values width = Len angle = 0 Else multiple_m = SolveMFromLenHt(Len, Ht) ' note that it's possible for two values of m to be found if height is close to max height If multiple_m.Count = 1 Then ' if there's only one m value returned, calculate the width & angle here. we'll deal with multiple m values later m = multiple_m.Item(0) width = Cal_W(Len, m) ' L * (2 * E(m) / K(m) - 1) angle = Cal_A(m) ' Acos(1 - 2 * m) End If End If height = Ht Else If IsSet("Ang") Then ' find width & height based on length and angle If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then ' skip the solver and set the known values width = Len height = 0 Else width = Cal_W(Len, m) ' L * (2 * E(m) / K(m) - 1) height = Cal_H(Len, m) ' L * Sqrt(m) / K(m) End If angle = Ang Else Msg("error", "Need to specify one more parameter in addition to length") Return End If length = Len Else If IsSet("Wid") Then ' if width is specified then... If IsSet("Ht") Then ' find length & angle based on specified width and height If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If If Ht = 0 Then ' skip the solver and set the known values length = Wid angle = 0 Else m = SolveMFromWidHt(Wid, Ht) length = Cal_L(Ht, m) ' h * K(m) / Sqrt(m) angle = Cal_A(m) ' Acos(1 - 2 * m) End If height = Ht Else If IsSet("Ang") Then ' find length & height based on specified width and angle If Wid = 0 Then Msg("error", "Curve not possible with width = 0 and an angle as inputs") Return End If If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then ' skip the solver and set the known values length = Wid height = 0 Else length = Wid / (2 * EllipticE(m) / EllipticK(m) - 1) If length < 0 Then Msg("error", "Curve not possible at specified width and angle (calculated length is negative)") Return End If height = Cal_H(length, m) ' L * Sqrt(m) / K(m) End If angle = Ang Else Msg("error", "Need to specify one more parameter in addition to width (Wid)") Return End If width = Wid Else If width IsNot Nothing Then ' if width is determined by PtA and PtB then... If IsSet("Ht") Then ' find length & angle based on calculated width and height If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If If Ht = 0 Then ' skip the solver and set the known values length = width angle = 0 Else m = SolveMFromWidHt(width, Ht) length = Cal_L(Ht, m) ' h * K(m) / Sqrt(m) angle = Cal_A(m) ' Acos(1 - 2 * m) End If height = Ht Else If IsSet("Ang") Then ' find length & height based on calculated width and angle If width = 0 Then Msg("error", "Curve not possible with width = 0 and an angle as inputs") Return End If If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = True flip_H = True End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then ' skip the solver and set the known values length = width height = 0 Else length = width / (2 * EllipticE(m) / EllipticK(m) - 1) If length < 0 Then Msg("error", "Curve not possible at specified width and angle (calculated length is negative)") Return End If height = Cal_H(length, m) ' L * Sqrt(m) / K(m) End If angle = Ang Else Msg("error", "Need to specify one more parameter in addition to PtA and PtB") Return End If Else If IsSet("Ht") Then ' if height is specified then... If IsSet("Ang") Then ' find length & width based on height and angle If Ht < 0 Then Ht = -Ht ' if height is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_H = True flip_A = True End If If Ht = 0 Then Msg("error", "Height can't = 0 if only height and angle are specified") Return Else If Ang < 0 Then Ang = -Ang ' if angle is negative, set it to positive (for the calculations) but flip the reference plane about its x-axis refPlane.Transform(Transform.Mirror(New Plane(refPlane.Origin, refPlane.XAxis, refPlane.ZAxis))) flip_A = Not flip_A flip_H = Not flip_H End If m = Cal_M(Ang) ' (1 - Cos(a)) / 2 If Ang = 0 Then Msg("error", "Angle can't = 0 if only height and angle are specified") Return Else length = Cal_L(Ht, m) ' h * K(m) / Sqrt(m) width = Cal_W(length, m) ' L * (2 * E(m) / K(m) - 1) End If angle = Ang End If height = Ht Else Msg("error", "Need to specify one more parameter in addition to height") Return End If Else If IsSet("Ang") Then Msg("error", "Need to specify one more parameter in addition to angle") Return Else Msg("error", "Need to specify two of the four parameters: length, width (or PtB), height, and angle") Return End If If m > Defined.M_MAX Then Msg("error", "Form of curve not solvable with current algorithm and given inputs") Return End If refPlane.Origin = refPlane.PointAt(width / 2, 0, 0) ' adjust the origin of the reference plane so that the curve is centered about the y-axis (start of the curve is at x = -width/2) If multiple_m.Count > 1 Then ' if there is more than one m value returned, calculate the width, angle, and curve for each Dim multi_pts As New DataTree(Of Point3d) Dim multi_crv As New List(Of Curve) Dim tmp_pts As New List(Of Point3d) Dim multi_W, multi_A, multi_F As New List(Of Double) Dim j As Integer = 0 ' used for creating a new branch (GH_Path) for storing pts which is itself a list of points For Each m_val As Double In multiple_m width = Cal_W(length, m_val) 'length * (2 * EllipticE(m_val) / EllipticK(m_val) - 1) If width < 0 And ignoreSelfIntersecting Then Msg("warning", "One curve is self-intersecting. To enable these, set ignoreSelfIntersecting to False") Continue For End If If m_val >= Defined.M_SKETCHY Then Msg("info", "Accuracy of the curve whose width = " & Math.Round(width, 4) & " is not guaranteed") angle = Cal_A(m_val) 'Math.Asin(2 * m_val - 1) refPlane.Origin = refPlane.PointAt(width / 2, 0, 0) ' adjust the origin of the reference plane so that the curve is centered about the y-axis (start of the curve is at x = -width/2) tmp_pts = FindBendForm(length, width, m_val, angle, refPlane) multi_pts.AddRange(tmp_pts, New GH_Path(j)) multi_crv.Add(MakeCurve(tmp_pts, angle, refPlane)) multi_W.Add(width) If flip_A Then angle = -angle multi_A.Add(angle) E = E * 10 ^ 9 ' Young's modulus input E is in GPa, so we convert to Pa here (= N/m^2) multi_F.Add(EllipticK(m_val) ^ 2 * E * I / length ^ 2) ' from reference {4} pg. 79 j += 1 refPlane.Origin = PtA ' reset the reference plane origin to PtA for the next m_val 'Print("length=" & length & ", width=" & width & ", height=" & height & ", angle=" & angle & ", m=" & m_val & ", k=" & Math.Sqrt(m_val) & ", w/L=" & width / length & ", h/L=" & height / length & ", w/h=" & width / height) Next ' assign the outputs Pts = multi_pts Crv = multi_crv L = length W = multi_W If flip_H Then height = -height H = height A = multi_A F = multi_F Else ' only deal with the single m value If m >= Defined.M_SKETCHY Then Msg("info", "Accuracy of the curve at these parameters is not guaranteed") If width < 0 And ignoreSelfIntersecting Then Msg("error", "Curve is self-intersecting. To enable these, set ignoreSelfIntersecting to False") Return End If Pts = FindBendForm(length, width, m, angle, refPlane) Crv = MakeCurve(pts, angle, refPlane) L = length W = width If flip_H Then height = -height H = height If flip_A Then angle = -angle A = angle E = E * 10 ^ 9 ' Young's modulus input E is in GPa, so we convert to Pa here (= N/m^2) F = EllipticK(m) ^ 2 * E * I / length ^ 2 ' from reference {4} pg. 79. Note: the critical buckling (that makes the rod/wire start to bend) can be found at height=0 (width=length) 'height = Math.Sqrt(((2 * Len / 5) ^ 2 - ((Wid - Len / 5) / 2) ^ 2) ' quick approximation discovered by Mårten of 'Geometry of Bending' fame ( http://tiny.cc/it2pbx ) 'width = (Len +/- 2 * Math.Sqrt(4 * Len ^ 2 - 25 * Ht ^ 2)) / 5 ' derived from above 'length = (2 * Math.Sqrt(15 * Ht ^ 2 + 4 * Wid ^ 2) - Wid) / 3 ' derived from above 'Print("length=" & length & ", width=" & width & ", height=" & height & ", angle=" & angle & ", m=" & m & ", k=" & Math.Sqrt(m) & ", w/L=" & width / length & ", h/L=" & height / length & ", w/h=" & width / height) End If 2397 703 84 184 2439 795 9 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 8 3ede854e-c753-40eb-84cb-b48008f14fd4 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 8ec86459-bf01-4409-baee-174d0d2b13d0 true Script Variable PtA c36c2a91-b586-4100-a3d3-5bbd29795b28 PtA PtA true 0 true 75154365-a588-4c21-86a3-ce81e626861d 1 e1937b56-b1da-4c12-8bd8-e34ee81746ef 2399 705 25 20 2413 715 true Script Variable PtB c03912a8-42cd-4a8e-b0e4-d859c1258a99 PtB PtB true 0 true 0 e1937b56-b1da-4c12-8bd8-e34ee81746ef 2399 725 25 20 2413 735 true Script Variable Pln f51b86ed-0366-4427-847a-63fd2fd6bf90 Pln Pln true 0 true f3933593-ac71-4dcf-9de1-01828b79508a 1 3897522d-58e9-4d60-b38c-978ddacfedd8 2399 745 25 20 2413 755 true Script Variable Len 7ea941b6-d28a-421f-a3f1-b6ee6ba45ce3 Len Len true 0 true 2398bd00-b514-4723-b31f-c436e1ae908b 1 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 2399 765 25 20 2413 775 true Script Variable Wid ca56b21d-1bf4-4ef7-9aba-89e8c40123e1 Wid Wid true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 2399 785 25 20 2413 795 true Script Variable Ht 01f793d6-6952-4cc0-9007-804b34e3573a Ht Ht true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 2399 805 25 20 2413 815 true Script Variable Ang d1ecc6ce-5745-410a-98ac-8cd99da220f9 Ang Ang true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 2399 825 25 20 2413 835 true Script Variable E ee84edc7-8985-4013-a1da-ab406c4a251b E E true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 2399 845 25 20 2413 855 true Script Variable I 06df9652-281a-4459-91a6-57c181550607 I I true 0 true 0 8e991e99-5fb8-41e1-928d-1bba8fb9f7d7 2399 865 25 20 2413 875 1 Print, Reflect and Error streams c01c7c67-2ce9-42db-98c8-ac53917d2607 out out false 0 2454 705 25 22 2466.5 716.25 Output parameter Pts 03112bf2-28fb-457f-b3d5-277c7e00bc21 Pts Pts false 0 2454 727 25 23 2466.5 738.75 Output parameter Crv 49ed72a9-4d6b-4881-ac0f-b8700f17fd26 Crv Crv false 0 2454 750 25 22 2466.5 761.25 Output parameter L 88bfd5c3-b1ad-4215-961b-9ab638f2cfd6 L L false 0 2454 772 25 23 2466.5 783.75 Output parameter W 5419ee09-aef9-46c4-abed-2d8045ba1ca7 W W false 0 2454 795 25 22 2466.5 806.25 Output parameter H b01ed3c0-f6fa-4b63-bd7e-3eef2c0d55a4 H H false 0 2454 817 25 23 2466.5 828.75 Output parameter A 7adf1044-78f7-4bcc-874b-e500d7bbbd0d A A false 0 2454 840 25 22 2466.5 851.25 Output parameter F 06232a84-6a80-4dfa-8393-98b404b97208 F F false 0 2454 862 25 23 2466.5 873.75 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values 2398bd00-b514-4723-b31f-c436e1ae908b Number Slider length false 0 1782 726 382 20 1782.348 726.8306 2 1 0 400 0 0 250 3581f42a-9592-4549-bd6b-1c0fc39d067b Construct Point Construct a point from {xyz} coordinates. cf3f354f-4379-4525-be10-5b0dbf31bcff Construct Point Pt 2254 641 67 64 2285 673 {x} coordinate 661f67f9-9359-4ee5-a58c-df76414fabf0 X coordinate X false c2ff491e-b898-432c-8e6a-7db46434934a 1 2256 643 14 20 2264.5 653 1 1 {0} 0 {y} coordinate fe9dc686-4183-43f9-863d-c8c499a24577 Y coordinate Y false 0 2256 663 14 20 2264.5 673 1 1 {0} 0 {z} coordinate 878e78bd-0868-4cae-b9a9-ef4a62dac81e Z coordinate Z false 0 2256 683 14 20 2264.5 693 1 1 {0} 0 Point coordinate 75154365-a588-4c21-86a3-ce81e626861d Point Pt false 0 2300 643 19 60 2309.5 673 d5967b9f-e8ee-436b-a8ad-29fdcecf32d5 Curve Contains a collection of generic curves 934a131f-f506-4502-86d5-aca65a992e3c Curve Crv false 49ed72a9-4d6b-4881-ac0f-b8700f17fd26 1 2525 750 50 24 2550.661 762.5297 17b7152b-d30d-4d50-b9ef-c9fe25576fc2 XY Plane World XY plane. true b2200c91-bbca-48a4-a165-a7f1e2fe3c5f XY Plane XY 2261 707 64 28 2292 721 Origin of plane 8589cc40-e2f3-4654-885c-b57ca3fd526a Origin O false 0 2263 709 14 24 2271.5 721 1 1 {0} 0 0 0 World XY plane f3933593-ac71-4dcf-9de1-01828b79508a Plane P false 0 2307 709 16 24 2315 721 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values c2ff491e-b898-432c-8e6a-7db46434934a Panel false 0 0 0 2170 644 50 20 0 0 0 2170.805 644.7167 255;255;250;90 true true true false false true 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe Scribble true 2131.598 592.215 2482.523 592.215 2482.523 607.2424 2131.598 607.2424 A quick note Microsoft Sans Serif 61857d57-1c68-49a3-ad6f-54433ce11822 false Scribble Scribble 16 Connect the Range component to Wid (ooooh) 2126.598 587.215 360.9243 25.02734 2131.598 592.215 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe Scribble true 29.21105 1018.627 472.5363 1018.376 472.5443 1032.464 29.21903 1032.715 A quick note Microsoft Sans Serif a735556e-8f05-4f8e-b6ab-1cc5ff398382 false Scribble Scribble 15 For Force calc, Young's modulus (material-dependent, in GPa) 24.21105 1013.376 453.3333 24.33868 29.21105 1018.627 59e0b89a-e487-49f8-bab8-b5bab16be14c Panel A panel for custom notes and text values 64b83d9b-5ae1-4022-b79e-c9135d3cdfc6 Panel false 0 0 0 2102 767 50 20 0 0 0 2102.709 767.0847 255;255;250;90 true true true false false true 57da07bd-ecab-415d-9d86-af36d7073abc Number Slider Numeric slider for single values 8162af0c-aab0-4024-a905-8206506ff4fd Number Slider height false 0 1344 308 384 20 1344.632 308.1685 2 1 0 200 -200 0 -56.13 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