This repository has been archived on 2023-03-25. You can view files and clone it, but cannot push or open issues or pull requests.

#### 189 lines 7.4 KiB Python Raw Permalink Normal View History

 2021-10-25 17:46:31 +02:00 `#! /usr/bin/python3` `#` `# Copyright (C) 2007 John Beard john.j.beard@gmail.com` `#` `# This program 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 2 of the License, or` `# (at your option) any later version.` `#` `# This program 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 this program; if not, write to the Free Software` `# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.` `#` ```""" ``` `This extension allows you to draw a triangle given certain information` ` about side length or angles.` ``` ``` `Measurements of the triangle` ``` ``` ` C(x_c,y_c)` ` /`__` ` / a_c``--__` ` / ``--__ s_a` ` s_b / ``--__` ` /a_a a_b`--__` ` /--------------------------------``B(x_b, y_b)` ` A(x_a,y_a) s_b` ```""" ``` ``` ``` `import sys` `from math import acos, asin, cos, pi, sin, sqrt` ``` ``` `import inkex` ``` ``` `X, Y = range(2)` ``` ``` `def draw_SVG_tri(point1, point2, point3, offset, width, name, parent):` ` style = {'stroke': '#000000', 'stroke-width': str(width), 'fill': 'none'}` ` elem = parent.add(inkex.PathElement())` ` elem.update(**{` ` 'style': style,` ` 'inkscape:label': name,` ` 'd': 'M ' + str(point1[X] + offset[X]) + ',' + str(point1[Y] + offset[Y]) +` ` ' L ' + str(point2[X] + offset[X]) + ',' + str(point2[Y] + offset[Y]) +` ` ' L ' + str(point3[X] + offset[X]) + ',' + str(point3[Y] + offset[Y]) +` ` ' L ' + str(point1[X] + offset[X]) + ',' + str(point1[Y] + offset[Y]) + ' z'})` ` return elem` ``` ``` ``` ``` `def angle_from_3_sides(a, b, c): # return the angle opposite side c` ` cosx = (a * a + b * b - c * c) / (2 * a * b) # use the cosine rule` ` return acos(cosx)` ``` ``` ``` ``` `def third_side_from_enclosed_angle(s_a, s_b, a_c): # return the side opposite a_c` ` c_squared = s_a * s_a + s_b * s_b - 2 * s_a * s_b * cos(a_c)` ` if c_squared > 0:` ` return sqrt(c_squared)` ` else:` ` return 0 # means we have an invalid or degenerate triangle (zero is caught at the drawing stage)` ``` ``` ``` ``` `def pt_on_circ(radius, angle): # return the x,y coordinate of the polar coordinate` ` x = radius * cos(angle)` ` y = radius * sin(angle)` ` return [x, y]` ``` ``` ``` ``` `def v_add(point1, point2): # add an offset to coordinates` ` return [point1[X] + point2[X], point1[Y] + point2[Y]]` ``` ``` ``` ``` `def is_valid_tri_from_sides(a, b, c): # check whether triangle with sides a,b,c is valid` ` return (a + b) > c and (a + c) > b and (b + c) > a and a > 0 and b > 0 and c > 0 # two sides must always be greater than the third` ` # no zero-length sides, no degenerate case` ``` ``` ``` ``` `def draw_tri_from_3_sides(s_a, s_b, s_c, offset, width, parent): # draw a triangle from three sides (with a given offset` ` if is_valid_tri_from_sides(s_a, s_b, s_c):` ` a_b = angle_from_3_sides(s_a, s_c, s_b)` ``` ``` ` a = (0, 0) # a is the origin` ` b = v_add(a, (s_c, 0)) # point B is horizontal from the origin` ` c = v_add(b, pt_on_circ(s_a, pi - a_b)) # get point c` ` c[1] = -c[1]` ``` ``` ` offx = max(b[0], c[0]) / 2 # b or c could be the furthest right` ` offy = c[1] / 2 # c is the highest point` ` offset = (offset[0] - offx, offset[1] - offy) # add the centre of the triangle to the offset` ``` ``` ` draw_SVG_tri(a, b, c, offset, width, 'Triangle', parent)` ` else:` ` inkex.errormsg('Invalid Triangle Specifications.')` ``` ``` ``` ``` `class Triangle(inkex.EffectExtension):` ` def add_arguments(self, pars):` ` pars.add_argument("--unit", default="mm", help="Units")` ` pars.add_argument("--s_a", type=float, default=100.0, help="Side Length a")` ` pars.add_argument("--s_b", type=float, default=100.0, help="Side Length b")` ` pars.add_argument("--s_c", type=float, default=100.0, help="Side Length c")` ` pars.add_argument("--a_a", type=float, default=60.0, help="Angle a")` ` pars.add_argument("--a_b", type=float, default=30.0, help="Angle b")` ` pars.add_argument("--a_c", type=float, default=90.0, help="Angle c")` ` pars.add_argument("--mode", default='3_sides', help="Side Length c")` ``` ``` ` def effect(self):` ` tri = self.svg.get_current_layer()` ` offset = self.svg.namedview.center` ` self.options.s_a = self.svg.unittouu(str(self.options.s_a) + self.options.unit)` ` self.options.s_b = self.svg.unittouu(str(self.options.s_b) + self.options.unit)` ` self.options.s_c = self.svg.unittouu(str(self.options.s_c) + self.options.unit)` ` stroke_width = self.svg.unittouu('1px')` ``` ``` ` if self.options.mode == '3_sides':` ` s_a = self.options.s_a` ` s_b = self.options.s_b` ` s_c = self.options.s_c` ` draw_tri_from_3_sides(s_a, s_b, s_c, offset, stroke_width, tri)` ``` ``` ` elif self.options.mode == 's_ab_a_c':` ` s_a = self.options.s_a` ` s_b = self.options.s_b` ` a_c = self.options.a_c * pi / 180 # in rad` ``` ``` ` s_c = third_side_from_enclosed_angle(s_a, s_b, a_c)` ` draw_tri_from_3_sides(s_a, s_b, s_c, offset, stroke_width, tri)` ``` ``` ` elif self.options.mode == 's_ab_a_a':` ` s_a = self.options.s_a` ` s_b = self.options.s_b` ` a_a = self.options.a_a * pi / 180 # in rad` ``` ``` ` if (a_a < pi / 2.0) and (s_a < s_b) and (s_a > s_b * sin(a_a)): # this is an ambiguous case` ` ambiguous = True # we will give both answers` ` else:` ` ambiguous = False` ``` ``` ` sin_a_b = s_b * sin(a_a) / s_a` ``` ``` ` if (sin_a_b <= 1) and (sin_a_b >= -1): # check the solution is possible` ` a_b = asin(sin_a_b) # acute solution` ` a_c = pi - a_a - a_b` ` error = False` ` else:` ` sys.stderr.write('Error:Invalid Triangle Specifications.\n') # signal an error` ` error = True` ``` ``` ` if not error and (a_b < pi) and (a_c < pi): # check that the solution is valid, if so draw acute solution` ` s_c = third_side_from_enclosed_angle(s_a, s_b, a_c)` ` draw_tri_from_3_sides(s_a, s_b, s_c, offset, stroke_width, tri)` ``` ``` ` if not error and ((a_b > pi) or (a_c > pi) or ambiguous): # we want the obtuse solution` ` a_b = pi - a_b` ` a_c = pi - a_a - a_b` ` s_c = third_side_from_enclosed_angle(s_a, s_b, a_c)` ` draw_tri_from_3_sides(s_a, s_b, s_c, offset, stroke_width, tri)` ``` ``` ` elif self.options.mode == 's_a_a_ab':` ` s_a = self.options.s_a` ` a_a = self.options.a_a * pi / 180 # in rad` ` a_b = self.options.a_b * pi / 180 # in rad` ``` ``` ` a_c = pi - a_a - a_b` ` s_b = s_a * sin(a_b) / sin(a_a)` ` s_c = s_a * sin(a_c) / sin(a_a)` ``` ``` ` draw_tri_from_3_sides(s_a, s_b, s_c, offset, stroke_width, tri)` ``` ``` ` elif self.options.mode == 's_c_a_ab':` ` s_c = self.options.s_c` ` a_a = self.options.a_a * pi / 180 # in rad` ` a_b = self.options.a_b * pi / 180 # in rad` ``` ``` ` a_c = pi - a_a - a_b` ` s_a = s_c * sin(a_a) / sin(a_c)` ` s_b = s_c * sin(a_b) / sin(a_c)` ``` ``` ` draw_tri_from_3_sides(s_a, s_b, s_c, offset, stroke_width, tri)` ``` ``` ``` ``` `if __name__ == '__main__':` ` Triangle().run()`