1 #!/usr/bin/env python
2 '''
3 Copyright (C) 2007 John Beard john.j.beard@gmail.com
5 ##This extension allows you to draw a triangle given certain information
6 ## about side length or angles.
8 ##Measurements of the triangle
10 C(x_c,y_c)
11 /`__
12 / a_c``--__
13 / ``--__ s_a
14 s_b / ``--__
15 /a_a a_b`--__
16 /--------------------------------``B(x_b, y_b)
17 A(x_a,y_a) s_b
20 This program is free software; you can redistribute it and/or modify
21 it under the terms of the GNU General Public License as published by
22 the Free Software Foundation; either version 2 of the License, or
23 (at your option) any later version.
25 This program is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
30 You should have received a copy of the GNU General Public License
31 along with this program; if not, write to the Free Software
32 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 '''
35 import inkex
36 import simplestyle, sys
37 from math import *
39 def draw_SVG_tri( (x1, y1), (x2, y2), (x3, y3), (ox,oy), width, name, parent):
40 style = { 'stroke': '#000000', 'stroke-width':str(width), 'fill': 'none' }
41 tri_attribs = {'style':simplestyle.formatStyle(style),
42 inkex.addNS('label','inkscape'):name,
43 'd':'M '+str(x1+ox)+','+str(y1+oy)+
44 ' L '+str(x2+ox)+','+str(y2+oy)+
45 ' L '+str(x3+ox)+','+str(y3+oy)+
46 ' L '+str(x1+ox)+','+str(y1+oy)+' z'}
47 inkex.etree.SubElement(parent, inkex.addNS('path','svg'), tri_attribs )
49 def angle_from_3_sides(a, b, c): #return the angle opposite side c
50 cosx = (a*a + b*b - c*c)/(2*a*b) #use the cosine rule
51 return acos(cosx)
53 def third_side_from_enclosed_angle(s_a,s_b,a_c): #return the side opposite a_c
54 c_squared = s_a*s_a + s_b*s_b -2*s_a*s_b*cos(a_c)
55 if c_squared > 0:
56 return sqrt(c_squared)
57 else:
58 return 0 #means we have an invalid or degenerate triangle (zero is caught at the drawing stage)
60 def pt_on_circ(radius, angle): #return the x,y coordinate of the polar coordinate
61 x = radius * cos(angle)
62 y = radius * sin(angle)
63 return [x, y]
65 def v_add( (x1,y1),(x2,y2) ):#add an offset to coordinates
66 return [x1+x2, y1+y2]
68 def is_valid_tri_from_sides(a,b,c):#check whether triangle with sides a,b,c is valid
69 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
70 #no zero-length sides, no degenerate case
72 def draw_tri_from_3_sides(s_a, s_b, s_c, offset, parent): #draw a triangle from three sides (with a given offset
73 if is_valid_tri_from_sides(s_a,s_b,s_c):
74 a_b = angle_from_3_sides(s_a, s_c, s_b)
76 a = (0,0) #a is the origin
77 b = v_add(a, (s_c, 0)) #point B is horizontal from the origin
78 c = v_add(b, pt_on_circ(s_a, pi-a_b) ) #get point c
79 c[1] = -c[1]
81 offx = max(b[0],c[0])/2 #b or c could be the furthest right
82 offy = c[1]/2 #c is the highest point
83 offset = ( offset[0]-offx , offset[1]-offy ) #add the centre of the triangle to the offset
85 draw_SVG_tri(a, b, c , offset, 2, 'Triangle', parent)
86 else:
87 sys.stderr.write('Error:Invalid Triangle Specifications.\n')
89 class Grid_Polar(inkex.Effect):
90 def __init__(self):
91 inkex.Effect.__init__(self)
92 self.OptionParser.add_option("--s_a",
93 action="store", type="float",
94 dest="s_a", default=100.0,
95 help="Side Length a")
96 self.OptionParser.add_option("--s_b",
97 action="store", type="float",
98 dest="s_b", default=100.0,
99 help="Side Length b")
100 self.OptionParser.add_option("--s_c",
101 action="store", type="float",
102 dest="s_c", default=100.0,
103 help="Side Length c")
104 self.OptionParser.add_option("--a_a",
105 action="store", type="float",
106 dest="a_a", default=60.0,
107 help="Angle a")
108 self.OptionParser.add_option("--a_b",
109 action="store", type="float",
110 dest="a_b", default=30.0,
111 help="Angle b")
112 self.OptionParser.add_option("--a_c",
113 action="store", type="float",
114 dest="a_c", default=90.0,
115 help="Angle c")
116 self.OptionParser.add_option("--mode",
117 action="store", type="string",
118 dest="mode", default='3_sides',
119 help="Side Length c")
121 def effect(self):
123 tri = self.current_layer
124 offset = (self.view_center[0],self.view_center[1]) #the offset require to centre the triangle
126 if self.options.mode == '3_sides':
127 s_a = self.options.s_a
128 s_b = self.options.s_b
129 s_c = self.options.s_c
130 draw_tri_from_3_sides(s_a, s_b, s_c, offset, tri)
132 elif self.options.mode == 's_ab_a_c':
133 s_a = self.options.s_a
134 s_b = self.options.s_b
135 a_c = self.options.a_c*pi/180 #in rad
137 s_c = third_side_from_enclosed_angle(s_a,s_b,a_c)
138 draw_tri_from_3_sides(s_a, s_b, s_c, offset, tri)
140 elif self.options.mode == 's_ab_a_a':
141 s_a = self.options.s_a
142 s_b = self.options.s_b
143 a_a = self.options.a_a*pi/180 #in rad
145 if (a_a < pi/2.0) and (s_a < s_b) and (s_a > s_b*sin(a_a) ): #this is an ambigous case
146 ambiguous=True#we will give both answers
147 else:
148 ambiguous=False
150 sin_a_b = s_b*sin(a_a)/s_a
152 if (sin_a_b <= 1) and (sin_a_b >= -1):#check the solution is possible
153 a_b = asin(sin_a_b) #acute solution
154 a_c = pi - a_a - a_b
155 error=False
156 else:
157 sys.stderr.write('Error:Invalid Triangle Specifications.\n')#signal an error
158 error=True
160 if not(error) and (a_b < pi) and (a_c < pi): #check that the solution is valid, if so draw acute solution
161 s_c = third_side_from_enclosed_angle(s_a,s_b,a_c)
162 draw_tri_from_3_sides(s_a, s_b, s_c, offset, tri)
164 if not(error) and ((a_b > pi) or (a_c > pi) or ambiguous):#we want the obtuse solution
165 a_b = pi - a_b
166 a_c = pi - a_a - a_b
167 s_c = third_side_from_enclosed_angle(s_a,s_b,a_c)
168 draw_tri_from_3_sides(s_a, s_b, s_c, offset, tri)
170 elif self.options.mode == 's_a_a_ab':
171 s_a = self.options.s_a
172 a_a = self.options.a_a*pi/180 #in rad
173 a_b = self.options.a_b*pi/180 #in rad
175 a_c = pi - a_a - a_b
176 s_b = s_a*sin(a_b)/sin(a_a)
177 s_c = s_a*sin(a_c)/sin(a_a)
179 draw_tri_from_3_sides(s_a, s_b, s_c, offset, tri)
181 elif self.options.mode == 's_c_a_ab':
182 s_c = self.options.s_c
183 a_a = self.options.a_a*pi/180 #in rad
184 a_b = self.options.a_b*pi/180 #in rad
186 a_c = pi - a_a - a_b
187 s_a = s_c*sin(a_a)/sin(a_c)
188 s_b = s_c*sin(a_b)/sin(a_c)
190 draw_tri_from_3_sides(s_a, s_b, s_c, offset, tri)
192 if __name__ == '__main__':
193 e = Grid_Polar()
194 e.affect()
197 # vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 encoding=utf-8 textwidth=99