]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/ogl/oglmisc.py
Added a Python port of the OGL library, deprecated the C++ wrapped version.
[wxWidgets.git] / wxPython / wx / lib / ogl / oglmisc.py
1 # -*- coding: iso-8859-1 -*-
2 #----------------------------------------------------------------------------
3 # Name: oglmisc.py
4 # Purpose: Miscellaneous OGL support functions
5 #
6 # Author: Pierre Hjälm (from C++ original by Julian Smart)
7 #
8 # Created: 20040508
9 # RCS-ID:
10 # Copyright: (c) 2004 Pierre Hjälm - 1998 Julian Smart
11 # Licence: wxWindows license
12 #----------------------------------------------------------------------------
13
14 from __future__ import division
15 from math import *
16
17 import wx
18
19 # Control point types
20 # Rectangle and most other shapes
21 CONTROL_POINT_VERTICAL = 1
22 CONTROL_POINT_HORIZONTAL = 2
23 CONTROL_POINT_DIAGONAL = 3
24
25 # Line
26 CONTROL_POINT_ENDPOINT_TO = 4
27 CONTROL_POINT_ENDPOINT_FROM = 5
28 CONTROL_POINT_LINE = 6
29
30 # Types of formatting: can be combined in a bit list
31 FORMAT_NONE = 0 # Left justification
32 FORMAT_CENTRE_HORIZ = 1 # Centre horizontally
33 FORMAT_CENTRE_VERT = 2 # Centre vertically
34 FORMAT_SIZE_TO_CONTENTS = 4 # Resize shape to contents
35
36 # Attachment modes
37 ATTACHMENT_MODE_NONE, ATTACHMENT_MODE_EDGE, ATTACHMENT_MODE_BRANCHING = 0, 1, 2
38
39 # Shadow mode
40 SHADOW_NONE, SHADOW_LEFT, SHADOW_RIGHT = 0, 1, 2
41
42 OP_CLICK_LEFT, OP_CLICK_RIGHT, OP_DRAG_LEFT, OP_DRAG_RIGHT = 1, 2, 4, 8
43 OP_ALL = OP_CLICK_LEFT | OP_CLICK_RIGHT | OP_DRAG_LEFT | OP_DRAG_RIGHT
44
45 # Sub-modes for branching attachment mode
46 BRANCHING_ATTACHMENT_NORMAL = 1
47 BRANCHING_ATTACHMENT_BLOB = 2
48
49 # logical function to use when drawing rubberband boxes, etc.
50 OGLRBLF = wx.INVERT
51
52 CONTROL_POINT_SIZE = 6
53
54 # Types of arrowhead
55 # (i) Built-in
56 ARROW_HOLLOW_CIRCLE = 1
57 ARROW_FILLED_CIRCLE = 2
58 ARROW_ARROW = 3
59 ARROW_SINGLE_OBLIQUE = 4
60 ARROW_DOUBLE_OBLIQUE = 5
61 # (ii) Custom
62 ARROW_METAFILE = 20
63
64 # Position of arrow on line
65 ARROW_POSITION_START = 0
66 ARROW_POSITION_END = 1
67 ARROW_POSITION_MIDDLE = 2
68
69 # Line alignment flags
70 # Vertical by default
71 LINE_ALIGNMENT_HORIZ = 1
72 LINE_ALIGNMENT_VERT = 0
73 LINE_ALIGNMENT_TO_NEXT_HANDLE = 2
74 LINE_ALIGNMENT_NONE = 0
75
76
77
78 # Format a string to a list of strings that fit in the given box.
79 # Interpret %n and 10 or 13 as a new line.
80 def FormatText(dc, text, width, height, formatMode):
81 i = 0
82 word=""
83 word_list = []
84 end_word = False
85 new_line = False
86 while i<len(text):
87 if text[i]=="%":
88 i += 1
89 if i == len(text):
90 word+="%"
91 else:
92 if text[i]=="n":
93 new_line = True
94 end_word = True
95 i += 1
96 else:
97 word+="%"+text[i]
98 i += 1
99 elif text[i] in ["\012","\015"]:
100 new_line = True
101 end_word = True
102 i += 1
103 elif text[i]==" ":
104 end_word = True
105 i += 1
106 else:
107 word += text[i]
108 i += 1
109
110 if i == len(text):
111 end_word = True
112
113 if end_word:
114 word_list.append(word)
115 word=""
116 end_word = False
117 if new_line:
118 word_list.append(None)
119 new_line = False
120
121 # Now, make a list of strings which can fit in the box
122 string_list = []
123 buffer=""
124 for s in word_list:
125 oldBuffer = buffer
126 if s is None:
127 # FORCE NEW LINE
128 if len(buffer)>0:
129 string_list.append(buffer)
130 buffer=""
131 else:
132 if len(buffer):
133 buffer+=" "
134 buffer += s
135 x, y = dc.GetTextExtent(buffer)
136
137 # Don't fit within the bounding box if we're fitting
138 # shape to contents
139 if (x>width) and not (formatMode & FORMAT_SIZE_TO_CONTENTS):
140 # Deal with first word being wider than box
141 if len(oldBuffer):
142 string_list.append(oldBuffer)
143 buffer = s
144 if len(buffer):
145 string_list.append(buffer)
146
147 return string_list
148
149
150
151 def GetCentredTextExtent(dc, text_list, xpos = 0, ypos = 0, width = 0, height = 0):
152 if not text_list:
153 return 0, 0
154
155 max_width = 0
156 for line in text_list:
157 current_width, char_height = dc.GetTextExtent(line)
158 if current_width>max_width:
159 max_width = current_width
160
161 return max_width, len(text_list) * char_height
162
163
164
165 def CentreText(dc, text_list, xpos, ypos, width, height, formatMode):
166 if not text_list:
167 return
168
169 # First, get maximum dimensions of box enclosing text
170 char_height = 0
171 max_width = 0
172 current_width = 0
173
174 # Store text extents for speed
175 widths = []
176 for line in text_list:
177 current_width, char_height = dc.GetTextExtent(line.GetText())
178 widths.append(current_width)
179 if current_width>max_width:
180 max_width = current_width
181
182 max_height = len(text_list) * char_height
183
184 if formatMode & FORMAT_CENTRE_VERT:
185 if max_height<height:
186 yoffset = ypos - height / 2 + (height - max_height) / 2
187 else:
188 yoffset = ypos - height / 2
189 yOffset = ypos
190 else:
191 yoffset = 0.0
192 yOffset = 0.0
193
194 if formatMode & FORMAT_CENTRE_HORIZ:
195 xoffset = xpos - width / 2
196 xOffset = xpos
197 else:
198 xoffset = 0.0
199 xOffset = 0.0
200
201 for i, line in enumerate(text_list):
202 if formatMode & FORMAT_CENTRE_HORIZ and widths[i]<width:
203 x = (width - widths[i]) / 2 + xoffset
204 else:
205 x = xoffset
206 y = i * char_height + yoffset
207
208 line.SetX(x - xOffset)
209 line.SetY(y - yOffset)
210
211
212
213 def DrawFormattedText(dc, text_list, xpos, ypos, width, height, formatMode):
214 if formatMode & FORMAT_CENTRE_HORIZ:
215 xoffset = xpos
216 else:
217 xoffset = xpos - width / 2
218
219 if formatMode & FORMAT_CENTRE_VERT:
220 yoffset = ypos
221 else:
222 yoffset = ypos - height / 2
223
224 # +1 to allow for rounding errors
225 dc.SetClippingRegion(xpos - width / 2, ypos - height / 2, width + 1, height + 1)
226
227 for line in text_list:
228 dc.DrawText(line.GetText(), xoffset + line.GetX(), yoffset + line.GetY())
229
230 dc.DestroyClippingRegion()
231
232
233
234 def RoughlyEqual(val1, val2, tol = 0.00001):
235 return val1<(val2 + tol) and val1>(val2 - tol) and \
236 val2<(val1 + tol) and val2>(val1 - tol)
237
238
239
240 def FindEndForBox(width, height, x1, y1, x2, y2):
241 xvec = [x1 - width / 2, x1 - width / 2, x1 + width / 2, x1 + width / 2, x1 - width / 2]
242 yvec = [y1 - height / 2, y1 + height / 2, y1 + height / 2, y1 - height / 2, y1 - height / 2]
243
244 return FindEndForPolyline(xvec, yvec, x2, y2, x1, y1)
245
246
247
248 def CheckLineIntersection(x1, y1, x2, y2, x3, y3, x4, y4):
249 denominator_term = (y4 - y3) * (x2 - x1) - (y2 - y1) * (x4 - x3)
250 numerator_term = (x3 - x1) * (y4 - y3) + (x4 - x3) * (y1 - y3)
251
252 length_ratio = 1.0
253 k_line = 1.0
254
255 # Check for parallel lines
256 if denominator_term<0.005 and denominator_term>-0.005:
257 line_constant=-1.0
258 else:
259 line_constant = float(numerator_term) / denominator_term
260
261 # Check for intersection
262 if line_constant<1.0 and line_constant>0.0:
263 # Now must check that other line hits
264 if (y4 - y3)<0.005 and (y4 - y3)>-0.005:
265 k_line = (x1 - x3 + line_constant * (x2 - x1)) / (x4 - x3)
266 else:
267 k_line = (y1 - y3 + line_constant * (y2 - y1)) / (y4 - y3)
268 if k_line >= 0 and k_line<1:
269 length_ratio = line_constant
270 else:
271 k_line = 1
272
273 return length_ratio, k_line
274
275
276
277 def FindEndForPolyline(xvec, yvec, x1, y1, x2, y2):
278 lastx = xvec[0]
279 lasty = yvec[0]
280
281 min_ratio = 1.0
282
283 for i in range(1, len(xvec)):
284 line_ratio, other_ratio = CheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[i], yvec[i])
285 lastx = xvec[i]
286 lasty = yvec[i]
287
288 if line_ratio<min_ratio:
289 min_ratio = line_ratio
290
291 # Do last (implicit) line if last and first doubles are not identical
292 if not (xvec[0] == lastx and yvec[0] == lasty):
293 line_ratio, other_ratio = CheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[0], yvec[0])
294 if line_ratio<min_ratio:
295 min_ratio = line_ratio
296
297 return x1 + (x2 - x1) * min_ratio, y1 + (y2 - y1) * min_ratio
298
299
300
301 def PolylineHitTest(xvec, yvec, x1, y1, x2, y2):
302 isAHit = False
303 lastx = xvec[0]
304 lasty = yvec[0]
305
306 min_ratio = 1.0
307
308 for i in range(1, len(xvec)):
309 line_ratio, other_ratio = CheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[i], yvec[i])
310 if line_ratio != 1.0:
311 isAHit = True
312 lastx = xvec[i]
313 lasty = yvec[i]
314
315 if line_ratio<min_ratio:
316 min_ratio = line_ratio
317
318 # Do last (implicit) line if last and first doubles are not identical
319 if not (xvec[0] == lastx and yvec[0] == lasty):
320 line_ratio, other_ratio = CheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[0], yvec[0])
321 if line_ratio != 1.0:
322 isAHit = True
323
324 return isAHit
325
326
327
328 def GraphicsStraightenLine(point1, point2):
329 dx = point2[0] - point1[0]
330 dy = point2[1] - point1[1]
331
332 if dx == 0:
333 return
334 elif abs(dy / dx)>1:
335 point2[0] = point1[0]
336 else:
337 point2[1] = point1[0]
338
339
340
341 def GetPointOnLine(x1, y1, x2, y2, length):
342 l = sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
343 if l<0.01:
344 l = 0.01
345
346 i_bar = (x2 - x1) / l
347 j_bar = (y2 - y1) / l
348
349 return -length * i_bar + x2,-length * j_bar + y2
350
351
352
353 def GetArrowPoints(x1, y1, x2, y2, length, width):
354 l = sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
355
356 if l<0.01:
357 l = 0.01
358
359 i_bar = (x2 - x1) / l
360 j_bar = (y2 - y1) / l
361
362 x3=-length * i_bar + x2
363 y3=-length * j_bar + y2
364
365 return x2, y2, width*-j_bar + x3, width * i_bar + y3,-width*-j_bar + x3,-width * i_bar + y3
366
367
368
369 def DrawArcToEllipse(x1, y1, width1, height1, x2, y2, x3, y3):
370 a1 = width1 / 2
371 b1 = height1 / 2
372
373 # Check that x2 != x3
374 if abs(x2 - x3)<0.05:
375 x4 = x2
376 if y3>y2:
377 y4 = y1 - sqrt((b1 * b1 - (((x2 - x1) * (x2 - x1)) * (b1 * b1) / (a1 * a1))))
378 else:
379 y4 = y1 + sqrt((b1 * b1 - (((x2 - x1) * (x2 - x1)) * (b1 * b1) / (a1 * a1))))
380 return x4, y4
381
382 # Calculate the x and y coordinates of the point where arc intersects ellipse
383 A = (1 / (a1 * a1))
384 B = ((y3 - y2) * (y3 - y2)) / ((x3 - x2) * (x3 - x2) * b1 * b1)
385 C = (2 * (y3 - y2) * (y2 - y1)) / ((x3 - x2) * b1 * b1)
386 D = ((y2 - y1) * (y2 - y1)) / (b1 * b1)
387 E = (A + B)
388 F = (C - (2 * A * x1) - (2 * B * x2))
389 G = ((A * x1 * x1) + (B * x2 * x2) - (C * x2) + D - 1)
390 H = ((y3 - y2) / (x32 - x2))
391 K = ((F * F) - (4 * E * G))
392
393 if K >= 0:
394 # In this case the line intersects the ellipse, so calculate intersection
395 if x2 >= x1:
396 ellipse1_x = ((F*-1) + sqrt(K)) / (2 * E)
397 ellipse1_y = ((H * (ellipse1_x - x2)) + y2)
398 else:
399 ellipse1_x = (((F*-1) - sqrt(K)) / (2 * E))
400 ellipse1_y = ((H * (ellipse1_x - x2)) + y2)
401 else:
402 # in this case, arc does not intersect ellipse, so just draw arc
403 ellipse1_x = x3
404 ellipse1_y = y3
405
406 return ellipse1_x, ellipse1_y
407
408
409
410 def FindEndForCircle(radius, x1, y1, x2, y2):
411 H = sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
412
413 if H == 0:
414 return x1, y1
415 else:
416 return radius * (x2 - x1) / H + x1, radius * (y2 - y1) / H + y1