diff --git a/◯ᗩIᗝI⚭◯⚪◯⚭IᗝIᗩ◯ⵙ◯ᗩIᗝI⚭◯⚪◯⚭IᗝIᗩ◯/◯✤ᴥᗩ◯ⵙ◯ᗩᴥ✤◯/◯ᗱᗴᴥᗩᗯ✤⏀Ⓞᔓᔕ◯ⵙ◯ᔓᔕⓄ⏀✤ᗯᗩᴥᗱᗴ◯/◯ᗝⵈ◯ⵙ◯ⵈᗝ◯/◯ᔓᔕⓄᴥᗱᗴᑐᑕⓄИNꖴ옷ᴥ◯⚪◯ᴥ옷ꖴИNⓄᑐᑕᗱᗴᴥⓄᔓᔕ◯ⵙ◯ᔓᔕⓄᴥᗱᗴᑐᑕⓄИNꖴ옷ᴥ◯⚪◯ᴥ옷ꖴИNⓄᑐᑕᗱᗴᴥⓄᔓᔕ◯/◯ᴥᗱᗴߦⓄ옷ᔓᔕᗩᴥᕤᕦ◯⚪◯ᕤᕦᴥᗩᔓᔕ옷Ⓞߦᗱᗴᴥ◯ⵙ◯ᴥᗱᗴߦⓄ옷ᔓᔕᗩᴥᕤᕦ◯⚪◯ᕤᕦᴥᗩᔓᔕ옷Ⓞߦᗱᗴᴥ◯/XHG.⠀⠀⠀⠀◯⠀ᗩᑐᑕꖴ✤ᔓᔕᗩᙁᗱᗴ⠀◯⠀⠀⠀⠀ⵙ⠀⠀⠀⠀◯⠀ᗱᗴᙁᗩᔓᔕ✤ꖴᑐᑕᗩ⠀◯⠀⠀⠀⠀.GHX b/◯ᗩIᗝI⚭◯⚪◯⚭IᗝIᗩ◯ⵙ◯ᗩIᗝI⚭◯⚪◯⚭IᗝIᗩ◯/◯✤ᴥᗩ◯ⵙ◯ᗩᴥ✤◯/◯ᗱᗴᴥᗩᗯ✤⏀Ⓞᔓᔕ◯ⵙ◯ᔓᔕⓄ⏀✤ᗯᗩᴥᗱᗴ◯/◯ᗝⵈ◯ⵙ◯ⵈᗝ◯/◯ᔓᔕⓄᴥᗱᗴᑐᑕⓄИNꖴ옷ᴥ◯⚪◯ᴥ옷ꖴИNⓄᑐᑕᗱᗴᴥⓄᔓᔕ◯ⵙ◯ᔓᔕⓄᴥᗱᗴᑐᑕⓄИNꖴ옷ᴥ◯⚪◯ᴥ옷ꖴИNⓄᑐᑕᗱᗴᴥⓄᔓᔕ◯/◯ᴥᗱᗴߦⓄ옷ᔓᔕᗩᴥᕤᕦ◯⚪◯ᕤᕦᴥᗩᔓᔕ옷Ⓞߦᗱᗴᴥ◯ⵙ◯ᴥᗱᗴߦⓄ옷ᔓᔕᗩᴥᕤᕦ◯⚪◯ᕤᕦᴥᗩᔓᔕ옷Ⓞߦᗱᗴᴥ◯/XHG.⠀⠀⠀⠀◯⠀ᗩᑐᑕꖴ✤ᔓᔕᗩᙁᗱᗴ⠀◯⠀⠀⠀⠀ⵙ⠀⠀⠀⠀◯⠀ᗱᗴᙁᗩᔓᔕ✤ꖴᑐᑕᗩ⠀◯⠀⠀⠀⠀.GHX new file mode 100644 index 00000000..c93e386e --- /dev/null +++ b/◯ᗩIᗝI⚭◯⚪◯⚭IᗝIᗩ◯ⵙ◯ᗩIᗝI⚭◯⚪◯⚭IᗝIᗩ◯/◯✤ᴥᗩ◯ⵙ◯ᗩᴥ✤◯/◯ᗱᗴᴥᗩᗯ✤⏀Ⓞᔓᔕ◯ⵙ◯ᔓᔕⓄ⏀✤ᗯᗩᴥᗱᗴ◯/◯ᗝⵈ◯ⵙ◯ⵈᗝ◯/◯ᔓᔕⓄᴥᗱᗴᑐᑕⓄИNꖴ옷ᴥ◯⚪◯ᴥ옷ꖴИNⓄᑐᑕᗱᗴᴥⓄᔓᔕ◯ⵙ◯ᔓᔕⓄᴥᗱᗴᑐᑕⓄИNꖴ옷ᴥ◯⚪◯ᴥ옷ꖴИNⓄᑐᑕᗱᗴᴥⓄᔓᔕ◯/◯ᴥᗱᗴߦⓄ옷ᔓᔕᗩᴥᕤᕦ◯⚪◯ᕤᕦᴥᗩᔓᔕ옷Ⓞߦᗱᗴᴥ◯ⵙ◯ᴥᗱᗴߦⓄ옷ᔓᔕᗩᴥᕤᕦ◯⚪◯ᕤᕦᴥᗩᔓᔕ옷Ⓞߦᗱᗴᴥ◯/XHG.⠀⠀⠀⠀◯⠀ᗩᑐᑕꖴ✤ᔓᔕᗩᙁᗱᗴ⠀◯⠀⠀⠀⠀ⵙ⠀⠀⠀⠀◯⠀ᗱᗴᙁᗩᔓᔕ✤ꖴᑐᑕᗩ⠀◯⠀⠀⠀⠀.GHX @@ -0,0 +1,20039 @@ + + + + + + + + 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 + + + + + + + + + + + + + + iVBORw0KGgoAAAANSUhEUgAAAJYAAABkCAIAAADrOV6nAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAACr1SURBVHhe7X1XVFxJtuX7m3lf89Wv15p5H9Pvc9brj561erpfTRtVybuqkjdlVSp5nIQ3wnuTCem992SSDpOYxHufmXjvhAeBfFX1NLNvXkQBEsghnqjmrrMuceNGxI2MHSfixDkngn/6p93rF9ACS7vXjm2B5e6H+pfuXjuwBQDcLoQ7ELdVVd6FcGfjh9rvQrgL4Y5vgR3/A3a5cBfCHd8CO/4H7HLhLoQ7vgV2/A/Y5cJdCHd8C+z4H7DLhbsQ7vgW2PE/YJcLdyEsLa2qqqqursa93H292CRlZWUVFRUr8SUlJXhEltWR63LhFa6XloaUKPCl2TcpELlQWmVlJb6O+0rJ6+pGJkPhuDb6+rqqvljCNneKd+VC/E6j0SgUCuVyuc1mKywsJH8/fgbZyjU1NUhjNptJ2PCD8ZiTkyORSJAerYkL8JOoIICMyIJyrFYrEiBMlkZ2FCQjS0B2FELmxR2FI4Cv4BX5RdxJwMgGRS6Ulpubi2TIWFBQgNKQBgF8iKwzWThipFKpQCBAYvInkKCSwJOVJCtMVslut+fl5eEtWcI244fPvROEZAf87LPPPDw84uPjw8PDU1JSLBaLTqfDz1Or1WismJiY2NhYCoWCjwHpoqKi5ubm77///vLlyxcvXsSPR/qsrCyUo1QqtVptXV0dHr/66quoqCg+nw9UEI/IzMxM9JLi4uL6+vrr16/fuHHj2rVreNRoNHiVn58vk8mQBV8EAIgPCgri8Xhk0+NqbGyMjIzcs2cPvv773/+eTqcDOYPBoNfrk5OTUQ3kQg2bmppCQ0NPnjwZGBgoFotRW5QMLPHp7OxsFIsAcuGn4bfjZ+LCdz09PfF7UU98aEdCePjwYT8/Pw6HgxbE70f77t+/H8h99NFHd+/e/fLLL7/99lu0iJeX19WrV1ksVnt7+zfffAMAjh07lp6efuTIkaNHjwYEBKArfP311yjkypUrJ06cuHPnjo+Pz+nTp7/44gtEIv3nn3+OTtDS0oJi0QOio6MzMjLwFt/C/bvvvkMWsmegTQ8cOID4hoYGNDcaGsglJCT86U9/Anh/+MMf8N1bt27t27cvLCwMnz5//jy+iAK7u7v9/f3xUeRF70FpqBI+ik+fO3cuODgYVUX47NmzSIw7EqD74rp06RL6B0rDF7cZxXflQnTGgwcPJiYmKhQKtCx+CS7E+Pr6gpPws729vRFGs6KZ0IUxALpcLjyCZdHuiATeaAggjUZBJPKCoW/evAng8RatBi45fvw4WBlFMZlMtBFaCkh8+umnp06dCgkJwUgOAFABoIhyqFQquByFs9lssCxqFRcX53Q6UT7eosOhhqgSykeYxB79A7nQdfr6+m7fvo1X6JEYzFF/dCwMLQigqyFjREQEEuCOOqMQVBUJkH7v3r3ol3i1wyBEd8PQgUZEr8edy+UyGAz8MDAlei66M7gHAbAgXqFB0QQYncAQaBSABHgwgqGJwbsikQgpMXIiAaBCCUiP4RcxAJ5Go2Fw+93vfofhDmMd8ANIaEcMceAAZAcAABIjJzoH2hS5kB3YY7bDIFxbW4sRFSWjGklJSZjnEMAnwLUoCheqjR6A/oG6IS8GEgwAaWlpeIVIdFAMIeheCKOeQBcdET/h448/xm9E9RCDV0ijUqnI6Xw7r3fiQrKi6OlkM6H2ZJMhZnXbkS2IGMQjDWYUxCCMDosesNLEpJiAlGQJZBayTGRBz0AjkuIJsiMNLmRHSjzigqwBdAE2JjmUTEooK+IMAiuFk69W6klKTKQ8gg/hjux4S36avFaqRBaCGHQ+fAuQk6WRafB25YvbhuIWQLgNdQU8gArX5iIfmg9NCcloG9qRFHq34UOvbN6dAeErf8Y/coI1EJZV2Hdpx7XAGgjN6oZd2nEtsAZCC3/yvZJVMG1kT2bS7+kZ4xsS/Z6BOYGU77Umv6TC10BoFd17f5QtmjAJRszq2gJrU4GlyWZq2Iiy9bV6Xle2eOL9VeaXVPL2QWiTzrHjS3v7Oh4/WVh4OP/0h0dPf3j4Iv3w0+P7Dyc1vBqrcOqX1NDv77dsE4S5kilFeofnzYCBgV6LQWPKLZqamhx52TU6Oto/2KPm1WbvQvh6g+I2QZgjmchk9ft7xTmcLa0tjY1NzUPDwysIArbB59fQ0FBPX6eaW5MtnH5/PfeXVPI2QYgmy5POqeiuuvq6iYmJ0dGRgYEBqDZgQIBKBWv23t4+gIcL8V3dbVpBQ45w5pfU0O/vt2wfhDYZMRdCmdXV1eFyugYHh2RSKRSbMAjAmNDX2+dobWlqaenv7+/odLKSsvPli/mKuVzpVLZ4/P39/l9AydsEITEXUts9b/i52hxFhbkKvXFocAiKTeipYeGDWW5kZEijlKWkpNXUNQ0ODYSHR3hfojJjSyRpTRbhWKHyPrD8BTT3+/gJ2wQh5kI9a8DfKwEa7/b2tvrmlq6uzt7eXljyoPVva2vDY1NzS0N9fVNTM9g0IjQx6rY6JcQS6inyv8aM9lWr6e0AMmd3pfGCjLNNEKL32WSzakYblJm9vT3dXV0w4HV1dZEOFgg4nS6Ygts7cLU3NtfJGJV25aMCxTzG0izeYEZUwZ0rtKjbCnCkTT5rFY29j+68Q8vcTgjnmLFFxcX2psa60rJyGH5bW1vBf7gQcDy/AG1DY62CVZUjWpZIscYHlrnS6dRQq9elVEWGE4+7KK50uG2CEDMZ5sJb1283NNabDGq+WA52gwvFixfgrKuvkjMrckRrJNJs0XiRahH43fo6SZRSX6C8v4siieI2QYg5zMAZ8vdMKIctr7gwv8judDg2grC2vlLGKF8Hobu6YwWKuUx2D1CUpDXu8uK2QkjOhUqaCz5jNbUVcPQqKS16KZWW220FVim9PEf80nXhWL58VsvsuvV1oo7VbZPtrh3fjQsxS+VIJl9KGymp5bQmSXqNNL12I5IQr+pM/OGNl4NjBcp5YUqd9yVKjngcsu4OFUO2qtpvP5DmiCfVtBEZZUBOHVxHiMxkjb2kcYX3MnnOTEGzXtCaKWjZkPgtJsEwJr+Nf+QY5sXkEHO4t7RQubBVbbFDy3lLCPOkGM26ywrDHC23Oxz+Hc6A7raATmeAq9kP5Gjxyc1KyeJMFSjuY6wjqUi5qKK3uVyOh4/vz9+fnp2fmnsZ3V+YuTc5qOHVvdJSgfWG/3UWJ77cPSm+RzPZB17420CIkVPHHPDzinm4+MXS0qmFyaOLk0dcDR8PDx1aWjq7tHRmaelku+uSx2UqJ66SE1+BVuYmVNAii309YvsHekqL8osr6+bn5iYnJ16kqamp0XuDGv6rLRVYZmSyez2/TcWoiyp94A39/qr3NhDmSaclKU7PW15PHny7tHSho/FQXdU+o+5PzY2Hl5YA6sWlpfO93Ve/POcT7qWM9dPE+Gni/HWB1/g3rnr39HSWFefbSytgnSD12riGh4dfsFQAwldbKqCvoYbnhXoKP7ThFLNMrng6RzydK5nOk86AEMAjSdbN5og3Hk7eBkK32eE+Mza/w/X53PyJ8dHPZqdOzk6emJk8MT9zan765Nz8Z6UFX/LiXSXax1jAkVSiecKOL6mtrRkfHx8ZHoY6Gwq2HvfV2dmJR6AIMwXu3T3tbnvhqyFETbDM8LvKYH9IwylEuUx2t4LeoKA3aditalarjuvU89uUjCYFvVHJaIGOaQtRfEsIUQOMXUpGpZxepGSWKBnF5F1BJ0hGK9SwGtH1Vo8eebI5KNhq62qHhgb7+/pgqYDvM3bAmEwm7E0BkH29vW4EB9o7HApmdYH8ARQCkIk2lWvQmaawusBwahaMfgDD6TjqbOZPmnQlbR3tXd19VmvOuXNn/+OjP4aGhvX3Dzud7fAl1rCd8GHYKtXEW0MIfh/PEc/liOdfRtBHQ5P585jgNvkO+HnGtbQ2ldjzM01WDJ9QkGLfEO7YNAS+NGiVGXRmc6urv78nIiwxKciszHABHjerzYPbNkLo+XAq+k8cTrEEypfPwSBj4A5G3pGx2WI4l+fklF68+MXly9i1cek3//ZvUqmutLQxJ9ce4pumZfZuVYd7FwjfYNQmFGxwvLjh19ICE29DUUUVeA7abfAf9iRATdPX24Ntg3nZuTW1Df19vTA23fgyMfqOKvgWP/AG566XhBph0zA6geVLl/OID7rJpUUVFqqwxthuJXiebBp9lB5d5HuVfuOLpFOffZFOw+4LU0RE6q9+9Styh80///N/vXzZUyIx8/kaL29PepR9qwTpbYLQrZ2Z4ySUQb2GGbCrsxNqbgyeMFPA3oQAHjEjAlRYKppbG5iJ1gLZIvo1umoWd0CYXJcQaAi4zgq5JeAnViM+b61eJlcyCbnU45tkKFHxdhtRJLRFmawe3yt0fD09wqZn90EODwtN4vCUfIH20OFj/+Nff/3f//XX/+cPf2AwZWyOMpXCjPQTQtjZfIJ4fQl2WyFU0hzYhOByOmAUhEUCF5TacL9AYI2loqlWxa3Nfa7mxhiFbk5afaHgBl8GXGdLKc0YNlfrgNCUigwH1KdZ3MHtUrwRli8YMq9/GRftqyIHfCx1bNJZSowuOoKRGCMIC6R8fvSrE8e/ueMVkxwnio1kR4UxtKyOdYLC6wP2YsptghDMBPcnX4/o6poqW7ZRIleRlgrSzEQGyAuP9Q3Vcmbly9TcYOUZCLei1Po732dE3VEBQqhen/McoXjjJVZ5fpti5A0B0ffNi+hSsGXe+Co+PiATn16Z29yqwQkjdxhkEdxDTYA03GjJGDN/DEuOd8FsXd5tghDzvDIDc6F/TW1VttWoN1udDidmxRcvMCX04DLGemPT6nqjRcCXScFG7+8oinTHaikGzAo1AsY0NaOjSLXw4mCVjeYTTL0+wRfypfpe4IT+hHk61EOIoXtdGrzdSHu85Y4H2wQhAMh3uz9l55qcbc1NLXUNTTUvpcbm2oqaIgm9dANLxbIMhTYCQjJqC3guPSIPKK40DdgUHjeIp4TnQguBwW2lfdH94Seu4pVrhdVaYZVG8EqqVPEqs/j9L6KIYqnhuTe/TjQLRtb59SCxkTuqyugGZTKJvOhwJv5yjJbRD47ceVyIGuOX4NdKmAVynk3G3Zh4Ngk7V8fpIEcbsBEybkDjbqFuHLJomKcIELp9MgizItgCzv8RPjLfKzRaVIGBO4DRDAxqkyxI2YU9/V0dXViz9fcNDPT09nX19HavIjz2DQz29Q9A14AELc5mJaveJp23rBJ0wX9KWtuVC1Gi1AaoatfjIZi06hrqa1qb6jqKbFUhATHXLvtwmbLWxu6aytaSwjqjsC9HvGXeXNvHhW4UxzEuvXJLCkY5YrgTjbm784g6o0tD63kpqWndWkYvykwLy/b+Lg0yDlgTWKK5IeUDMxW9Heo9iD8hHoKkYBM8qRLi07DLODYqITIqNSoiUSQ15+TgTJWSFbJaSiRirVxhEfDUGq0tJ7ckLCBFkd62wmroWxgkfS5T4wP1qxcGkIrxWKJ5xIqpysstWFr66ae//ZCekbb/wCeffnbU0+vm9Mzkjz89m5weSY3UWgSvUFm8PptuK4SvXy13ynHgp5eVldjrigvr7Pm1Rfk15cVNFSXNpUUNCNsLaosL6kqKao2ashw4OWY4IePE+KmhpgF45NCHdkcYAXl6Kzehyuf7tNi4BIOhkMVWpVO4ERHJTJZao7EplTkrpNHkRkUkBATFBQcncfl6o7EoINCfGm4j0UIvxECNbgEI0VfcBrUxfAVvVbS2tLvZkd6qsye+y7VZ4PGMJRMOSMEefDjKfvLJJzivYXJysqu7MyDQLyOiaOetC98Qv3uQgIi5RJvb1tFd3+Boae1odXSK5Roqk2ey2OD/1tTc1tDobGvvybYWyiiOMt1TDKQpoRY0LhylsEwkRk7ZrHsQJvReGPFskvuM5CxtZrZOVwAyGOw6nU2lylGrc1eTRpOHR/KuVBlTIjTkBg9ghkmXcjfnyoVoDbOLXLrgK9A5gMth+QKji1IaInykarUWqvumpsYrV74PDg764ouLv/nN/4Tr18BAf0trQ5h/mobRA6590zZ5afoPlwuh0Q/0pGTQOKlJDE+fuxyeTizL8bnmefPchZhIqlJtS4xNu+kZLJVZ2RyJ17VwVkw5HDKKNQ/BGRjisNaOuqPkJlRiIiSWIoSKbr5Q9oBLyVZrLQq5RaHMViqtCoUVEK4msCMBsDYf+CFerjDoeK2FigduDd9EYlDW1QvREJfcCgQCv4zIfIhO7LgyfIXoNJiPBVNKsdVeUoDTE3DOzl/+8pff/va3ON4De/Ph9Gy2mDQsBybXrVrzfMAQiqZ9byakUujUNG5IWBKTn8WLTemK+ZeJ+P+S5/tnOiOTQhWmUnDOSSaDKbx+2TfMQ+pe9bPAiATPyWdZsSWQaMiJEHCmhmX7X2fGRCeazCXx0SneXkFXvveKTuJn6vKVBJzLhDOrKGkcGgOKPysiFcqs+FCZmt4F7R1cPTDjYsAEQ4OzARiMaH7XGBbh6OpREXyvY3fL0hvlGbBUuDK57QZBF+54BCloH4ql4g0UpG83XMAEocrojIpI5wk0HK6ayVYxkzPY0eGsuEhmdDgtXcjmqKCvAsXGpotTmkq1TyC7gxGxXsRYiuHOrl4s1T5GZ0ej85OqWXHld65QY6Lj9foCokCGNCggmkKTa9S54MXnlK1WWoODo297Btz0DJEr8/T6fP9A31OHb6ErQH/m1hTOkK6tkbflcDZHX8GYv+43EvZCCWEsdBOMhYS9cIW20NKE7364XEisyoXjCZHiqIiMmEhGTCQ9JpodHcsnKSbKHUPE02LCOVDHkOtCyIqYrjARwvUbDuDi1EZ42RCzoGwG93zpAuZChcqEERJTnU6Xr8ZQiUH1ZwitxKMyRyYxMMGIimylyhTqR5FSHOgf7iU8Ma1itMSKPvK2AkBu+VL9TXv8GgixWPmgCJ3XIhjHZgwDe3AjwlszH7LPGvswJi2sLrDG8LvGxJJRQmkGtNCBFcgeMFPNCpVRLjcLRXpMeypVNjBbR0AUQ6hKhdPzLGKpTsGoL1Qski0LcRT9A0ZmKNXccu9//q6rNRCqGK3vgRwbl+lQMwlSMQhaCbgfl2uiZjo1LJeGuTGxXEivY3e9aH4j3a4gaJCa8Vg/3e3vqdFRCdrMAj5HfuO6t29QNDVdBo6UycwrBNgQliusGGARFom14vTKYvVjYlOOZIIRbcemANiV3Ht0Zs28+2b+fQufuL8RIUs2sbrfArvYGgixTxOErX4gMrxCL415nWS9PfCvWF/U8id6CJMhDh/s7ibcLxDGKXZEauLPa1WDTI/NbEUFFYr0drv6AaE+lRLWu5XhCHMVBkA4SolTmwJuMCMj49SaPIFQx2BIk1JYdIYSOEmlpmWSmRUyU0w0NT6BmZDEFoqMSqU52C8hITALbAeehtsj3MnhmWcVTanZZRqhRCNUqAUK3DcnrUiZKSFIJ1ZqhHKVQJ7JdblRfFepYg2EUCph4QJ7HloG4Z+pv6+np3t1DJkM0CCwLtmamIH+7u4uaLJW53U3ew88LPp6u4vtBXn5NpxqWlpanGvLb3W6Skrs1TXVQ8NDBO69PS+Wv6YagwOo2Ni9Meykuf5dQHJwNiu2FAYpLavbPSkSIj4JJ6F+w3QoW6QnZUnleoLP5BBhMIqaJRLjzxBKTTKpMS42IzaOdjc8lcfH0aPZOHDP+zsq1ifoBxhIsZ7DPJ3JnDZqI4cG9nS2HWh3HujpOtjduSH1dB5qd+1vbtzb1LC3zYHEB7o696n4OquAUDu8I62BsK3NBXK1OUFkeIVcrhdiXjvZ+qLIjO1tMBdWlGOTU2lFFVz0y8vKyxqaGvNsuTALd3S2v2Y1UDj6RFl5MSCM989KCNRH+siDb/FgQ4i+owSiOlYPqT0BolgXZiQYpLJMsTiLmAiV2QAPEK4j7D6WSs2YEfFWIFKnRGQWKR4S9ufnuxvRLcy8B9bM6IHeve2OI66WI11tRztdG1J32xG7dY9e9pFK9ZEt7+OejqOd7fv1YmOu+JHbFeqdUFwDYb6tcJupsLC4qLC4sMBeWEAGimx5qEPRG1UD62WFVI8VCNb15BIeUj6cbpgxxVgOAksgGuEjTw423/6eEhERq1SYU1K5QYFRHt53MZDKpCYgupqkEuPzRyNPoFRzmgrkC4ANPE1wIbHfeDwhsEDEuzzYexj4tTYd6XAebXcsU1vr0XWEt+VF+yqK95fZ99mL9gFsoJgUEyJOacewv5UQ8hPqdyJx42qg717XFmhx0jqBERWLRZgUeIk1Qbc44XdjoJRJSWL6+UeERlDYbI2bBZchFIkMQmEmh6uRiLMQFomy+EJ1VAA/izuMQlhxpXDngWEk+Kbo5DEPm+36owdwujw9N3nq4f2zD+bOgBZnX04P5s8+Wjj76P7Zh/NnF+dOP1o8n5J6JdxHa1c/tAjmTdx5M2/e9KbEvW8Vzq7hwopiJ6jSff+Z7A7ErItcjrE7Nktmd1SVuGrK2qtL2xBYudeWdxClrcr7WqW567AuI1nbqlKHRdX8Uis/2cEBJwAGDxXKH9ASswRijXucJCZCsdgA/Q6PpxMK9UK+FswnEmjvhqdwuDo3nAak8fW//e3pUAgy8K6AxkfD7AQjsmJcBdm3hrsO9PafeLZwpiT/48a6w41VB4eHTy4tfbX0/77YjP4Ob+mz9ORYLX3cxB9R8dQ6iVBDyL6vIOxqz5RKDHIp7joklvDV3Pw1EE7PTIAmp+6RgWWaJWKmpsdXR07NjBPJZjdLNjs3hTNk4MvkbHMMDPZ2djoHhgcGh/udbS1Eaavyvk5p+DpyrfvozOzk+MTooycLWVmGWH+d2wljs3kFMFPidAKRisfTcLlakRB8pmexNJ9+HnL0WIBPAEshN4lFehpNAkYEqAKBni9Qhdym6pi95Cjqlo8msRzUM+cbK+6OD+wdHT3x7P6p8mJMikc7mg9PTgDCL5YA0iZEOLx/ivVprugnWUZjXc3Jwb69Ha79EIggFm1EvV0HMYM6WvY21n/sbN3X3XlgeHBPniVgDYSkDxJcH1ackcjAipPLSjzptrR5Mjil1dXWmMwmi8UKLzzi/wrU1uM/DcBlDQfNrHZ5ep3S8C0kQ01e/CiWIUaz9vqlILehf8O1NlYCSSGGkNAIiVh/2yvg2g3fGx5BcfGczMycg4cC/tuvL/6vU6oz3hohXw1eFAoy+XwdGJTFkcrotZgLV3cOQJjJnMk3h8wvHJmbOjE9/vnDhVOLcwQtzJxEzBxc2jehGXi+H9QK5DnCZ3JaY1316d7OQ66Ww5gyN5GJeruOVdn35un+rJD80WL5a2P94cHefTZL4BoI6xurt5YaGmtQIDZeY+NubX1VY1NtVU1ZSVkRXC628EMOVwOfK+HEVkMHtgkX5hOuEjnBQRESiSE5gZGQQA+9m5RGEctkxuQk/r//0e9f/nf0n0+ligRKYlAVYHTV8viZTJaYm1KCpf3KFi0ESGWbAT4czFIVq1zFLAMpmaVKRqmCUUIQnaRl9/Z1ATm9WE6zZ/H686TzSkZzdcWpno5DjqbDEIIIVnYe63guGa2ISAh0uo7VVx6ATFRbcaC4cG9z45H+7hcglNDK3gOVS2grVIbtuzjNYmu/Isoo1rCc8M1Z7RvxMizHbNL7GQlZHJ4CkxymOrHYKBDoOByNXJ4VFSf85HDo1VvJUrEuJDDcwyuMydYI+DoeX+3nE54cbBEk1cCxChu1sEAUJNcKkmskaa0ySqcyo0dN79cxhwzsMRNvwiqcgXt7nmTBJoN4/LhQ8YQg5ZMi5dPn9KxI+cyuelaoXCzWPOUmlvb3nHu4SIhF96dPP14499AtE0HqeTB3FkLQzzR3BkITZKIni+ceu8Wix49OFOV6r+FCg7hlC8kodayQSebMkrSSj1v4CRSVRdS5OUvU9jrSOebClCglIORyYf3QcDmYEQkCiiKhTiICZmrAFh2RHAJfXq6Wx9UKBNo7t/3hKgdfZBjrCfJVQ8GN5QosWZBxoIOFyhv2LKjxIKxiDRN0k4cwImHHgB861jNIDFGIzA4tD0mJQYagG2Ivjzv3J77F1rAnTy5M9n/WUnmgqnh/Y82hH55hNt1UJoLEtHShuvLaGggnp8empu/h7qaVgPtxioxcoXtkzKr0q5MReYdH+4dH+geHeodG+voGusYnRxEzMjowhT2gRN7V5S+Xtqb85Y9uVg18HcXOL0yVV5TDlWFzD+BCzIXB+qCgEL4gUyDIjIxIunThVkQUDWEORw1yY6lmc9SYApcjuRoaQ5QakWVXEYfgrBCMHlCa58seZwsf54hwf2jhL5p497M4c3rWNORMVcaInDIgSekRJnXyElycOAcrppkR1QDKCK8hiR1XnRKSe8crbGbsS1fLoSePL4x0Ha8o2ddQe6i99dgPTy8u/XRx6cdNael8eenlddoZ4hQY0s8a5/iQh8KQF4SJ1Y94C7ECyVZHrk7W3uYqg9eL3Y6NLzk52VlZxsrq2uxsK3bAQMxB3tXlu0trRfxKaYgh5Z3XqQYUcbV1Vde/99Ey4Bm2oTdDvnw+IzIvMOAuj6/lctRUCj82Mj0lhQ/k2GzVasIsSEYC0XSaQE6vL5CvOSMFCjYTf1zF0+sVjEwpO1PK0cs4ejlJXANIwctS8LOUfKNSQJBKaNaIrFpxtk6Mu1ktMqn5BmGNXfWThFI9OXYOQuxPj88v/XhhaelLgsB/r8QP6C6dqSr2WQthu6MN1OG+r6MXIxHz0kh3Rmwww+IB7r7QXuI0p/qGWqerFSeWVFaVtXc6N8m45rsbl/9zsg5nX39XXr412IsBH+pNrT9jsBdmxBsYLDEJD6DCncVSriJgqYyPoycn8sCOSEaji0Ju0+CUDeuVm8sJiZfYmJcxVJR/ZWRoT1f7/q6O/b3dBzah/p4DHW37Whr3NNTtcbXu7es+MDz0V4MyxcR9rKDXTt0DhBd+fHz+7z9ceAXbrWPKpdMNFb5rINQqcraQdMpcUKYqD6RX2RDWq22gN/1EprJgczKoC+R8m0X46tMvsO00/q6UyZIw6Ng/RgyeAInJxP+JWSElm63wugWntWQuT8tkKrlctfdtz2/PhMb76yDIwF4PjQ8WiJmsicLcq/09+9taj4A2WQzgFdYDpbkfW1R/Usj+w2rd42w92t+z16ikZAt/ktGqpu5ha/tbQlhfsZYLDax7HwaN6FlDKyROaxCmVYrSqkASKg48qZNl1ONOxoAEqRXYm/Ki98M6obRQsZAYrAsIDGYzpdeueHj5hHt7h4SEpkKuWYuiHExJxiCQQROG+NBUtE7gB8kF288ghgiS6oI8hNnmC24d6VEQsRIAuXAnwp3uRywDyPju9uNYD9SUH6irPFhq39fmODbUd5jNuEa5W61m1k9PYIP7p3//8djS348vLX32JnSovvLKGi7UcJrejqAI1nCbtNzmZeI1a3nNKAqPb1qgit1gEQ278RiHLR7GuWJ7RU8fDintamvvrK2rLyktKywqIk5xa+vo6OzuBPV0WvSlRu4rVP5YydGjC/z9wul0cUw0JS6ecds76I5fPKY9BkO+mkj8EIN7GpUjTq8uUjyA6gf8B7sHPGjCPGQXzt5sd119uHhyYfYMFgMP7p9ZhNAPQX/hHKkIhb50ZpzQnT6CUnT2DKFHXTj39OH5pw/OPcbC4PEZlery7atsuEnoJSyzJs2kpFrUb0YmdbJOoFsD4cjo0NvR6Njw4BAMh7Ay9sKZHU7uvf09o/eGcb7FyOjg65c5PDJwb2JQxLSaeYQLr131MO2uRa02ajUWlSbHYimx5RfBQIj/WajR6LKyCrVqk0JlLi6po6eLufGvWNpj1QglS3pcVgZdiFGUnA7BZ3S67KUQIh4QpqaxGXE2HMu4om6FOIozBOhRtY76rxx1eyemzty/d9JVeail4VBBzp6JqbNzQ58X5O8d7vzUUXe4rvJAWeFeGCjKivdPjJ8mRJW/fUHQ0lmNxFecPIC1kIm3mMVZNHKI+5sSVORrICRt6BACcToowriwbbO9ox2Ry6862smNnORLpCRfwTLf3ET8376CAsJ+C1E0N8/W0NRssZjhS4k0L82FeGwIxS41shBc+Na9e2MsNv3SuRAYyu9cZn536YpEqgnwDb/tHwvPa7M5x8/PFxrRkpIyCkUY5H/XwztcLLakUameV8M2V7C5/ammYoKFgJCAjY4TbLkUqhjh1QRofX0Cg8PS2CwloKXRxN4egenhNjDx8z0bhDijog33t38/OnDw4cMzD6c/dzUcHBs83tN5eHbm5MzIp93tRwa6joI1Z+596mg52OlA/Kmlv53HnPecPrNnJxjYi69SR7zalLgGwvZ2HArqanPfV4h47HA/dmz6qs3R6sBZ2/ivZ7XYUN/QWNfc0pRfkFdbV41usL5AsvwXCmzvaMPx+Gmp1DBPsTStWU51hvowKBQOFgBoawgXGRlM/K+8x48fms3WhAT42WKukvL4uvC7SVjz5W2q5oZnW0KQ1tfP3w2MlM2SX/nOIyySBlbD4yqShIXERURm0BlyQAuVt7eXH3aeYs0OBQ3pL+o++Wqown5jcurU2ODpsaGz0xPnIVhOj5+fHD03MXZuBoGxcxOjZ6fvnZudOD+Dt2PnxkfO3hteprGxYyYVTlfaaqu9KSv3LcmAjHlmY57FZAOZjeQdj/kWo81EvH09MuQasyxiWjE8JMidvRpmR1R0KviGms5Lo3Jj49MSklLjE1MjoxNTUlmUdB4oLZ0dG8nKITYCbtZnwUbMWPsdn1AaXZqRIaHTpRHhyQmJHATwuJrQVwAzYoBrUjIjNdxYqnkKicnrUhrOmiZPYIRoigM8cJIcZCuQ/E2IyJJeq+dgFbsFu9TWcCF8lreWNG9RIK0LizByeYc79pilx5pSozVpMToQJTYzLSYzJUpLicnEZmgyMiVKxUkqeuXWWeKMb/kDaqwhlcJJTxeD8I8n6TQpGV5NJH6IwT0hkSaiVhUoiEUhNtfB9x62X3KXk/tkIHjpw/w0C2+2NyMJXFKJ3VvvaLJf7wpMbLv6AGi1wYjYTCudy5PMbkpzr7N13b01bjI8gJNG5VGpIoIowrQ0AZUqXn4kIsUZ6SLfO+FxiZyMDALX5BSWn0eygTMIjsFyEM4c7rP7tmAAXAve2/ujfrje3O/ePdevC5UL8UEanzt30qliiEKALTmJFXI3NT1dgsfnBFz53j5hicm8dBJmquimxw0cEwYVq/vApAlsO8XSYotOjcGAPJnFntWzZgzu+xvSrFU4/Q8EoU0+x44r9fYIoqYvY0NJEyQlc6npq7mQeEWOogjgnpBID/KkWYmxnZi3wIg4RBNWiy1hRAzFWnaLXpFq0iZlqZKNmpTNSJ1i1qZYM1Oz9akWXYpJg/QJGi72oi79E3khtOUd/4MqEOI79pglhWvDIuOiYyigmFhqbByVDK+mmNjlRyQIDo3hp1a4WXDZDcfA6cdeVGKqfu4hgECOe9cL7i8lckPMi/rbbNF9GdPS2bEPzoyd7XuhrtuEBvr2t7s+aar7S03Vnx0tH/f37hsZ/otRRf0HgpBUT+OAO2Z8ASuxiP0axEooZCfajbzB1cclgP9g/0uPyIfimxBNRePYV6yktSjozSAlvYXYZcDCPgKnikFEKt3xClrzi0cdA0I50+psPdzpOuJsPkLo5zam3s7jJYSi9c8K+f81W/a4HEf7evYblRn/WBCixYFivgymvrnXp3XHXaAEA7cfoilmROJAI/GMmtNQU1uH86taW1yNja0lJeXFxeV2e2ldXVNLK4x3OGHH1djUqBXUrnOzyxEvqDh5ztZDcCt1Nh3paj8ObWpXG6Fc7Wojwz/H9HQeb6o9VF99qLnuUGX5AYDd17PPoqH/o0H4amXH6wz+WGLi+CKcbsNPqskWTadFawuLSgyG3PzCmtzc4oaGevxzdQolLcuYa7fXGPS5+QXVpWU1lDgFjooA6sQ2ORiNFfDPeMRIFCzOnYRnKRwsoFmFuhVq1WePzj9dPIcw9K6Eb+rcmR+fXIBDBlwxfnxM2DRgXHz28NwPz06pxeG7EL4dqMS/W8A51Xe9pN+cDvYPCITHxjdfX0tMFYpFWj6fl5aWZrcXcThyOk189ert+GSeVGKKiIz47nxI9B11uLcEBK/iy+cS4uI9FsfOlpXs+/HJxXzzX83mPSOjp2DvzTP+Od/6SVX5vuysPY21h0psn5Tn72tqOkI4qZJaVkLRelGrvb4L4dtBSJxuAw019p+Kkht8vSJZbDmWJXS6nMOBiYqZkpJUU1OVQeNjnyl22NAZCh5fE+gbnRpmxckc2KoPkqc70sPL4yKDH82e7u0+/sPjCz3OoxUVB8bHTj1ZONPuPOJqPjrS91lr/ZHBvs8HOo7XVx3s6vp06W9f/mwWXrogE+9C+G5bUgCkXfmQHp+bmJJOY0gyaCIKlZuSmh4ZHRcbn5SQlIEYBhPxghQKHRolWDyg2VmhfNlTDdf8w1PCe/iHR1CCw+vC7Qn+Ezjsq2UnjHWB1Yb7pRO2rNRdLnxrLlzOiKMPFfTW+HBxUpSMpMRIkDwhQpocLV+JjA8Xiqm1SLxmrhUuqLn6Z48/X/rbRQLCzZ2dXny7dCLfmLQGQjzsXjuxBZaX9rt/dnQL/H8vKf57xZJDFgAAAABJRU5ErkJggg== + + + + + \ No newline at end of file