3
\$\begingroup\$

I've written the following module to generate arbitrary triangles based on a subset of lengths and angles, like what https://www.calculator.net/triangle-calculator.html provides. It works well enough but as-written it's fairly repetitive. I'm new to OpenSCAD so I'd love some feedback if there are cleaner ways to express this (or obviously if there are bugs/edge cases I've missed).

// Creates a 2D triangle with the given sides and/or angles.
// Any three parameters can be used to describe the triangle, e.g.
// a=3,b=4,c=5; a=3,b=4,c_angle=90; and a=3,b_angle=53.13,c_angle=90
// all describe the same triangle.
// Side c runs along the x axis, with side a leaving the origin at a_angle.
// Reference: https://www.calculator.net/triangle-calculator.html
module triangle(a=0,b=0,c=0,a_angle=0,b_angle=0,c_angle=0) {
    assert(a_angle==a_angle && b_angle==b_angle && c_angle==c_angle,
           "Invalid triangle: impossible angles");
        
    // returns c
    function shared_angle(a, b, c_angle) = sqrt(pow(b,2)+pow(a,2)-2*b*a*cos(c_angle));

    // returns c_angle
    function opposing_angle(a, a_angle, b) = asin(b*sin(a_angle)/a);

    // returns angles A, B, C if they sum to 180
    // if one angle is unset (0) it will be set to the difference of the other two
    function triangle_angles(a, b, c) =
      let (
        a = a > 0 ? a : assert(b!=0 && c!=0, "Insufficient angles") 180 - b - c,
        b = b > 0 ? b : assert(a!=0 && c!=0, "Insufficient angles") 180 - a - c,
        c = c > 0 ? c : assert(a!=0 && b!=0, "Insufficient angles") 180 - a - b
      )
      assert(a + b + c == 180, "Invalid triangle: impossible angles")
      [a,b,c];

    // returns b
    function angle_ratio(a, angle_a, angle_b) = a*sin(angle_b)/sin(angle_a);

    // Three Sides
    if (a>0 && b>0 && c>0) {
        assert(a_angle==0 && b_angle==0 && c_angle==0, "Too many arguments");
        let ( a_angle = acos((pow(b,2)+pow(c,2)-pow(a,2))/(2*b*c)) ) {
            assert(a_angle==a_angle, "Invalid triangle: impossible lengths");
            assert(sin(a_angle)!= 0, "Invalid triangle: colinear");
            polygon(points = [[0, 0], [c, 0], [b * cos(a_angle), b * sin(a_angle)]]);
        }
    // Shared Angle
    } else if (a>0 && b>0 && c_angle>0) {
        assert(c==0 && a_angle==0 && b_angle==0, "Too many arguments");
        triangle(a,b,shared_angle(a,b,c_angle));
    } else if (a>0 && c>0 && b_angle>0) {
        assert(b==0 && a_angle==0 && c_angle==0, "Too many arguments");
        triangle(a,shared_angle(a,c,b_angle),c);
    } else if (b>0 && c>0 && a_angle>0) {
        assert(a==0 && b_angle==0 && c_angle==0, "Too many arguments");
        triangle(shared_angle(b,c,a_angle),b,c);
    // Opposing Angle
    } else if (a>0 && a_angle>0 && b>0) {
        assert(c==0 && c_angle==0 && b_angle==0, "Too many arguments");
        triangle(a=a,b=b,c_angle=opposing_angle(a,a_angle,b));
    } else if (a>0 && a_angle>0 && c>0) {
        assert(b==0 && b_angle==0 && c_angle==0, "Too many arguments");
        triangle(a=a,c=c,b_angle=opposing_angle(a,a_angle,c));
    } else if (b>0 && b_angle>0 && a>0) {
        assert(c==0 && c_angle==0 && a_angle==0, "Too many arguments");
        triangle(a=a,b=b,c_angle=opposing_angle(b,b_angle,a));
    } else if (b>0 && b_angle>0 && c>0) {
        assert(a==0 && a_angle==0 && c_angle==0, "Too many arguments");
        triangle(a=a,c=c,a_angle=opposing_angle(b,b_angle,c));
    } else if (c>0 && c_angle>0 && a>0) {
        assert(b==0 && b_angle==0 && a_angle==0, "Too many arguments");
        triangle(a=a,c=c,b_angle=opposing_angle(c,c_angle,a));
    } else if (c>0 && c_angle>0 && b>0) {
        assert(a == 0 && a_angle == 0 && b_angle == 0, "Too many arguments");
        triangle(b = b, c = c, a_angle = opposing_angle(c, c_angle, b));
    // Two+ Angles
    } else if (a>0 && b==0 && c==0) {
        angles = triangle_angles(a_angle, b_angle, c_angle);
        triangle(a=a, b=angle_ratio(a,angles[0],angles[1]), c=angle_ratio(a,angles[0],angles[2]));
    } else if (b>0 && a==0 && c==0) {
        angles = triangle_angles(a_angle, b_angle, c_angle);
        triangle(a=angle_ratio(b,angles[1],angles[0]), b=b, c=angle_ratio(b,angles[1],angles[2]));
    } else if (c>0 && a==0 && b==0) {
        angles = triangle_angles(a_angle, b_angle, c_angle);
        triangle(a=angle_ratio(c,angles[2],angles[0]), b=angle_ratio(c,angles[2],angles[1]), c=c);
    } else {
        assert(false, "Insufficient arguments");
    }
}

Like the calculator, only three arguments are needed to specify a triangle. So for example these all define the same shape:

triangle(a=3, b=4, c=5);
triangle(a=3, b=4, c_angle=90);
triangle(a=3, b_angle=53.13, c_angle=90);
\$\endgroup\$
3
  • \$\begingroup\$ Hello, probably I am wrong but would it be possible define a triangle with just two sides and the angle between them (I don't know openscad) ? \$\endgroup\$ Jun 3, 2021 at 7:42
  • \$\begingroup\$ Right, that's the idea. Any three arguments are sufficient. \$\endgroup\$
    – dimo414
    Jun 3, 2021 at 17:34
  • \$\begingroup\$ I read again your post, please ignore my previous question and thanks for the added details. \$\endgroup\$ Jun 3, 2021 at 20:38

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Browse other questions tagged or ask your own question.