[RELEASE] OpenERP 5.0.12
[odoo/odoo.git] / bin / pychart / arrow.py
1 #
2 # Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com)
3
4 # Jockey is free software; you can redistribute it and/or modify it
5 # under the terms of the GNU General Public License as published by the
6 # Free Software Foundation; either version 2, or (at your option) any
7 # later version.
8 #
9 # Jockey is distributed in the hope that it will be useful, but WITHOUT
10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 # for more details.
13 #
14 import line_style
15 import color
16 import chart_object
17 import object_set
18 import math
19 import arrow_doc
20 import canvas
21
22 from pychart_types import *
23 from types import *
24 from scaling import *
25
26 __doc__ = """
27 Arrow is an optional component of a chart that draws line segments with
28 an arrowhead. To draw an arrow, one creates an arrow.T object, and calls
29 its "draw" method usually after area.draw() is called (otherwise, area.draw()
30 may overwrite the arrow). For example, the below code draws an arrow
31 from (10,10) to (20,30).
32
33 ar = area.T(...)
34 a = arrow.T(head_style = 1)
35 ar.draw()
36 a.draw([(10,10), (20,30)])
37 """
38
39 def draw_arrowhead(can, tailx, taily, tipx, tipy, thickness, head_len, style):
40     can.comment("ARROWHEAD tail=(%d,%d) tip=(%d,%d)\n" 
41                 % (tailx, taily, tipx, tipy))
42     
43     halfthickness = thickness/2.0
44     dx = tipx - tailx
45     dy = tipy - taily
46     arrow_len = math.sqrt(dx*dx + dy*dy)
47     angle = math.atan2(dy, dx) * 360 / (2*math.pi)
48     base = arrow_len - head_len
49     can.push_transformation((tailx, taily), None, angle)    
50
51     can.newpath()
52     if style == 0:
53         can.moveto(base, - halfthickness)
54         can.lineto(base, halfthickness)
55         can.lineto(arrow_len, 0)
56         can.closepath()
57     elif style == 1:
58         depth = head_len / 2.5
59         can.moveto(base - depth, -halfthickness)
60         can.lineto(base, 0)
61         can.lineto(base - depth, halfthickness)
62         can.lineto(arrow_len, 0)
63         can.closepath()
64     elif style == 2:
65         can.moveto(base + head_len/2.0, 0)
66         can.path_arc(base + head_len / 2.0, 0, head_len / 2.0, 1.0, 0, 400)
67     elif style == 3:
68         can.moveto(base, 0)
69         can.lineto(base + head_len/2.0, -halfthickness)
70         can.lineto(arrow_len, 0)
71         can.lineto(base + head_len/2.0, halfthickness)
72         can.closepath()
73     else:
74         raise Exception, "Arrow style must be a number between 0 and 3."
75     can.fill()
76     can.pop_transformation()
77     can.comment("end ARROWHEAD.\n")
78
79 def draw_arrowbody(can, tailx, taily, tipx, tipy, head_len):
80     dx = tipx - tailx
81     dy = tipy - taily
82     arrow_len = math.sqrt(dx*dx + dy*dy)
83     angle = math.atan2(dy, dx) * 360 / (2*math.pi)
84     base = arrow_len - head_len
85     can.push_transformation((tailx, taily), None, angle)
86     can.moveto(0, 0)
87     can.lineto(base+head_len*0.1, 0)
88     can.stroke()
89     can.pop_transformation()
90
91
92 class T(chart_object.T):
93     __doc__ = arrow_doc.doc
94     keys = {
95         "thickness" : (UnitType, 4,
96                         "The width of the arrow head."),
97         "head_len": (UnitType, 8,
98                     "The length of the arrow head."),
99         "head_color": (color.T, color.default,
100                       "The color of the arrow head."),
101         "line_style": (line_style.T, line_style.default,
102                        "Line style."),
103         "head_style": (IntType, 1,
104                        "The value of 0 draws a triangular arrow head. The value of 1 draws a swallow-tail arrow head. The value of 2 draws a circular head. The value of 3 draws a diamond-shaped head.")
105             }
106 ##AUTOMATICALLY GENERATED
107
108 ##END AUTOMATICALLY GENERATED
109     def draw(self, points, can = None):
110         """Parameter <points> specifies the
111         list of points the arrow traverses through.
112         It should contain at least two points, i.e.,
113         the tail and tip. Parameter
114         <can> is an optional parameter that specifies the output.
115         <<canvas>>
116         """
117         if can == None: can = canvas.default_canvas()
118         self.type_check()
119         xtip = points[-1][0]
120         ytip = points[-1][1]
121         
122         xtail = points[-2][0]
123         ytail = points[-2][1]
124
125         can.newpath()
126         can.set_line_style(self.line_style)
127         if len(points) > 2:
128             can.moveto(points[0][0], points[0][1])
129             for i in range(1, len(points)-1):
130                 can.lineto(points[i][0], points[i][1])
131
132         draw_arrowbody(can, xscale(xtail), yscale(ytail),
133                        yscale(xtip), yscale(ytip),
134                        nscale(self.head_len))
135
136         can.set_fill_color(self.head_color)
137         draw_arrowhead(can, xscale(xtail), yscale(ytail),
138                        xscale(xtip), yscale(ytip),
139                        nscale(self.thickness),
140                        nscale(self.head_len),
141                        self.head_style)
142         
143         can.setbb(xtail, ytail)
144         can.setbb(xtip, ytip)
145
146 standards = object_set.T()
147 def _intern(a):
148     global standards
149     standards.add(a)
150     return a
151
152 a0 = _intern(T(head_style=0))
153 a1 = _intern(T(head_style=1))
154 a2 = _intern(T(head_style=2))
155 a3 = _intern(T(head_style=3))
156 gray0 = _intern(T(head_style=0, head_color = color.gray50,
157                   line_style=line_style.T(color=color.gray50)))
158 gray1 = _intern(T(head_style=1, head_color = color.gray50,
159                   line_style=line_style.T(color=color.gray50)))
160 gray2 = _intern(T(head_style=2, head_color = color.gray50,
161                   line_style=line_style.T(color=color.gray50)))
162 gray3 = _intern(T(head_style=3, head_color = color.gray50,
163                   line_style=line_style.T(color=color.gray50)))
164
165 fat0 = _intern(T(head_style=0, head_len=12, thickness=10, line_style=line_style.T(width=2)))
166 fat1 = _intern(T(head_style=1, head_len=12, thickness=10, line_style=line_style.T(width=2)))
167 fat2 = _intern(T(head_style=2, head_len=12, thickness=10, line_style=line_style.T(width=2)))
168 fat3 = _intern(T(head_style=3, head_len=12, thickness=10, line_style=line_style.T(width=2)))
169 fatgray0 = _intern(T(head_style=0, head_len=12, thickness=10,
170                       head_color = color.gray50,
171                       line_style=line_style.T(width=2, color=color.gray50)))
172 fatgray1 = _intern(T(head_style=1, head_len=12, thickness=10,
173                       head_color = color.gray50,
174                       line_style=line_style.T(width=2, color=color.gray50)))
175 fatgray2 = _intern(T(head_style=2, head_len=12, thickness=10,
176                       head_color = color.gray50,
177                       line_style=line_style.T(width=2, color=color.gray50)))
178 fatgray3 = _intern(T(head_style=3, head_len=12, thickness=10,
179                       head_color = color.gray50,
180                       line_style=line_style.T(width=2, color=color.gray50)))
181
182 default = a1
183
184