diff --git a/readme.md b/readme.md index 28ee733..018b7ee 100644 --- a/readme.md +++ b/readme.md @@ -20,23 +20,23 @@ See [usage](docs/usage.md) for requirements, installation instructions and a usa Vitamins A-I Vitamins J-Q Vitamins R-Z Printed Utilities Core Utilities Axials Jack Rails Box Annotation BOM Ball_bearings KP_pillow_blocks Ring_terminals Butt_box Bezier Clip - Batteries LDRs Rockers Cable_grommets Dogbones Global - Belts LED_meters Rod Carriers Fillet Polyholes - Blowers LEDs SCS_bearing_blocks Corner_block Gears Rounded_rectangle - Bulldogs Leadnuts SK_brackets Door_hinge Hanging_hole Sphere - Buttons Light_strips SMDs Door_latch Horiholes Teardrops - Cable_strips Linear_bearings SSRs Fan_guard Layout - Cameras Magnets Screws Fixing_block Maths - Circlips Mains_sockets Sealing_strip Flat_hinge Offset - Components Microswitches Sheets Foot Quadrant - DIP Microview Spades Handle Round - D_connectors Modules Spools PCB_mount Rounded_cylinder - Displays Nuts Springs PSU_shroud Rounded_polygon - Extrusion_brackets O_ring Stepper_motors Printed_box Sector - Extrusions Opengrab Swiss_clips Ribbon_clamp Sweep - Fans PCB Toggles SSR_shroud Thread - Fuseholder PCBs Transformers Screw_knob Tube - Geared_steppers PSUs Tubings Socket_box + Batteries LDRs Rockers Cable_grommets Catenary Global + Belts LED_meters Rod Carriers Dogbones Polyholes + Blowers LEDs SCS_bearing_blocks Corner_block Fillet Rounded_rectangle + Bulldogs Leadnuts SK_brackets Door_hinge Gears Sphere + Buttons Light_strips SMDs Door_latch Hanging_hole Teardrops + Cable_strips Linear_bearings SSRs Fan_guard Horiholes + Cameras Magnets Screws Fixing_block Layout + Circlips Mains_sockets Sealing_strip Flat_hinge Maths + Components Microswitches Sheets Foot Offset + DIP Microview Spades Handle Quadrant + D_connectors Modules Spools PCB_mount Round + Displays Nuts Springs PSU_shroud Rounded_cylinder + Extrusion_brackets O_ring Stepper_motors Printed_box Rounded_polygon + Extrusions Opengrab Swiss_clips Ribbon_clamp Sector + Fans PCB Toggles SSR_shroud Sweep + Fuseholder PCBs Transformers Screw_knob Thread + Geared_steppers PSUs Tubings Socket_box Tube Green_terminals Panel_meters Variacs Strap_handle Hot_ends Pillars Veroboard Hygrometer Pin_headers Washers @@ -5245,6 +5245,36 @@ Bezier curves and function to get and adjust the length or minimum z point. ![bezier](tests/png/bezier.png) +Top + +--- + +## Catenary +Catenary curve to model hanging wires, etc. + +Although the equation of the curve is simply ```y = a cosh(x / a)``` there is no explicit formula to calculate the constant ```a``` or the range of ```x``` given the +length of the cable and the end point coordinates. See . The Newton-Raphson method is used to find +```a``` numerically, see . + +The coordinates of the lowest point on the curve can be retrieved by calling ```catenary_points()``` with ```steps``` equal to zero. + + +[utils/catenary.scad](utils/catenary.scad) Implementation. + +[tests/catenary.scad](tests/catenary.scad) Code for this example. + +### Functions +| Function | Description | +|:--- |:--- | +| ```catenary(t, a)``` | Parametric catenary function linear along the length of the curve. | +| ```catenary_ds_by_da(d, a)``` | First derivative of the length with respect to ```a```. | +| ```catenary_find_a(d, l, a = 1)``` | Find the catenary constant ```a```, given half the horizontal span and the length. | +| ```catenary_points(l, x, y, steps = 100)``` | Returns a list of 2D points on the curve that goes from the origin to ```(x,y)``` and has length ```l```. | +| ```catenary_s(d, a)``` | Length of a symmetric catenary with width ```2d```. | + +![catenary](tests/png/catenary.png) + + Top --- diff --git a/tests/catenary.scad b/tests/catenary.scad new file mode 100644 index 0000000..2d35330 --- /dev/null +++ b/tests/catenary.scad @@ -0,0 +1,56 @@ +// +// NopSCADlib Copyright Chris Palmer 2020 +// nop.head@gmail.com +// hydraraptor.blogspot.com +// +// This file is part of NopSCADlib. +// +// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the +// GNU General Public License as published by the Free Software Foundation, either version 3 of +// the License, or (at your option) any later version. +// +// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with NopSCADlib. +// If not, see . +// +l = 250; // [1: 1000] +x = 200; // [1: 1000] +y = 50; //[-500 : 500] + +include <../utils/core/core.scad> +use <../utils/catenary.scad> +use <../utils/sweep.scad> +use <../utils/annotation.scad> + +module catenaries() { + // + // catenary curve path from control points + // + curve = [for(p = catenary_points(l, x, y)) [p.x, p.y, 0]]; + // + // Draw the curve + // + r = 0.5; + sweep(curve, circle_points(r, $fn = 64)); + // + // Minimum Z + // + min_z = catenary_points(l, x, y, 0); + + color("blue") { + translate([min_z.x, min_z.y + r]) + rotate([-90, 0, 0]) + arrow(); + + translate([min_z.x, min_z.y - r]) + rotate([90, 0, 0]) + arrow(); + } +} + +if($preview) + rotate(is_undef($bom) ? 0 : [70, 0, 315]) + catenaries(); diff --git a/tests/png/catenary.png b/tests/png/catenary.png new file mode 100644 index 0000000..7efef20 Binary files /dev/null and b/tests/png/catenary.png differ diff --git a/utils/catenary.scad b/utils/catenary.scad new file mode 100644 index 0000000..ff879f6 --- /dev/null +++ b/utils/catenary.scad @@ -0,0 +1,53 @@ +// +// NopSCADlib Copyright Chris Palmer 2020 +// nop.head@gmail.com +// hydraraptor.blogspot.com +// +// This file is part of NopSCADlib. +// +// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the +// GNU General Public License as published by the Free Software Foundation, either version 3 of +// the License, or (at your option) any later version. +// +// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with NopSCADlib. +// If not, see . +// + +// +//! Catenary curve to model hanging wires, etc. +//! +//! Although the equation of the curve is simply ```y = a cosh(x / a)``` there is no explicit formula to calculate the constant ```a``` or the range of ```x``` given the +//! length of the cable and the end point coordinates. See . The Newton-Raphson method is used to find +//! ```a``` numerically, see . +//! +//! The coordinates of the lowest point on the curve can be retrieved by calling ```catenary_points()``` with ```steps``` equal to zero. +// +include +use + +function catenary(t, a) = let(u = argsinh(t)) a * [u, cosh(u)]; //! Parametric catenary function linear along the length of the curve. +function catenary_s(d, a) = 2 * a * sinh(d / a); //! Length of a symmetric catenary with width ```2d```. +function catenary_ds_by_da(d, a) = 2 * sinh(d / a) - 2 * d / a * cosh(d / a); //! First derivative of the length with respect to ```a```. + +function catenary_find_a(d, l, a = 1) = //! Find the catenary constant ```a```, given half the horizontal span and the length. + assert(l > 2 * d, "Not long enough to span the gap") assert(d) + abs(catenary_s(d, a) - l) < 0.0001 ? a + : catenary_find_a(d, l, max(a - (catenary_s(d, a) - l) / catenary_ds_by_da(d, a), 0.001)); + +function catenary_points(l, x, y, steps = 100) = //! Returns a list of 2D points on the curve that goes from the origin to ```(x,y)``` and has length ```l```. + let( + d = x / 2, + a = catenary_find_a(d, sqrt(sqr(l) - sqr(y)), d / 2), // Find a to get the correct length + offset = argsinh(y / catenary_s(d, a)), + t0 = sinh(-d / a + offset), + t1 = sinh( d / a + offset), + h = a * cosh(-d / a + offset) - a, + lowest = offset > d / a ? [0, 0] : offset < -d / a ? [x, y] : [d - offset * a, -h], + //dummy = echo(l = l, d = d, a = a, t0=t0, t1=t1, sinh(d / a), s = catenary_s(d, a), offset = offset * a, h = h), + p0 = catenary(t0, a) + ) + steps ? [for(t = [t0 : (t1 - t0) / steps : t1]) catenary(t, a) - p0] : lowest;