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