1 # -*- coding: iso-8859-1 -*-
2 #----------------------------------------------------------------------------
4 # Purpose: Miscellaneous OGL support functions
6 # Author: Pierre Hjälm (from C++ original by Julian Smart)
10 # Copyright: (c) 2004 Pierre Hjälm - 1998 Julian Smart
11 # Licence: wxWindows license
12 #----------------------------------------------------------------------------
19 # Rectangle and most other shapes
20 CONTROL_POINT_VERTICAL = 1
21 CONTROL_POINT_HORIZONTAL = 2
22 CONTROL_POINT_DIAGONAL = 3
25 CONTROL_POINT_ENDPOINT_TO = 4
26 CONTROL_POINT_ENDPOINT_FROM = 5
27 CONTROL_POINT_LINE = 6
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
36 ATTACHMENT_MODE_NONE, ATTACHMENT_MODE_EDGE, ATTACHMENT_MODE_BRANCHING = 0, 1, 2
39 SHADOW_NONE, SHADOW_LEFT, SHADOW_RIGHT = 0, 1, 2
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
44 # Sub-modes for branching attachment mode
45 BRANCHING_ATTACHMENT_NORMAL = 1
46 BRANCHING_ATTACHMENT_BLOB = 2
48 # logical function to use when drawing rubberband boxes, etc.
51 CONTROL_POINT_SIZE = 6
55 ARROW_HOLLOW_CIRCLE = 1
56 ARROW_FILLED_CIRCLE = 2
58 ARROW_SINGLE_OBLIQUE = 4
59 ARROW_DOUBLE_OBLIQUE = 5
63 # Position of arrow on line
64 ARROW_POSITION_START = 0
65 ARROW_POSITION_END = 1
66 ARROW_POSITION_MIDDLE = 2
68 # Line alignment flags
70 LINE_ALIGNMENT_HORIZ = 1
71 LINE_ALIGNMENT_VERT = 0
72 LINE_ALIGNMENT_TO_NEXT_HANDLE = 2
73 LINE_ALIGNMENT_NONE = 0
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):
98 elif text[i] in ["\012","\015"]:
113 word_list.append(word)
117 word_list.append(None)
120 # Now, make a list of strings which can fit in the box
128 string_list.append(buffer)
134 x, y = dc.GetTextExtent(buffer)
136 # Don't fit within the bounding box if we're fitting
138 if (x > width) and not (formatMode & FORMAT_SIZE_TO_CONTENTS):
139 # Deal with first word being wider than box
141 string_list.append(oldBuffer)
144 string_list.append(buffer)
150 def GetCentredTextExtent(dc, text_list, xpos = 0, ypos = 0, width = 0, height = 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
160 return max_width, len(text_list) * char_height
164 def CentreText(dc, text_list, xpos, ypos, width, height, formatMode):
168 # First, get maximum dimensions of box enclosing text
173 # Store text extents for speed
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
181 max_height = len(text_list) * char_height
183 if formatMode & FORMAT_CENTRE_VERT:
184 if max_height < height:
185 yoffset = ypos - height / 2.0 + (height - max_height) / 2.0
187 yoffset = ypos - height / 2.0
193 if formatMode & FORMAT_CENTRE_HORIZ:
194 xoffset = xpos - width / 2.0
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
205 y = i * char_height + yoffset
207 line.SetX(x - xOffset)
208 line.SetY(y - yOffset)
212 def DrawFormattedText(dc, text_list, xpos, ypos, width, height, formatMode):
213 if formatMode & FORMAT_CENTRE_HORIZ:
216 xoffset = xpos - width / 2.0
218 if formatMode & FORMAT_CENTRE_VERT:
221 yoffset = ypos - height / 2.0
223 # +1 to allow for rounding errors
224 dc.SetClippingRegion(xpos - width / 2.0, ypos - height / 2.0, width + 1, height + 1)
226 for line in text_list:
227 dc.DrawText(line.GetText(), xoffset + line.GetX(), yoffset + line.GetY())
229 dc.DestroyClippingRegion()
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)
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]
243 return FindEndForPolyline(xvec, yvec, x2, y2, x1, y1)
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)
254 # Check for parallel lines
255 if denominator_term < 0.005 and denominator_term > -0.005:
258 line_constant = float(numerator_term) / denominator_term
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)
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
272 return length_ratio, k_line
276 def FindEndForPolyline(xvec, yvec, x1, y1, x2, y2):
282 for i in range(1, len(xvec)):
283 line_ratio, other_ratio = CheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[i], yvec[i])
287 if line_ratio < min_ratio:
288 min_ratio = line_ratio
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
296 return x1 + (x2 - x1) * min_ratio, y1 + (y2 - y1) * min_ratio
300 def PolylineHitTest(xvec, yvec, x1, y1, x2, y2):
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:
314 if line_ratio < min_ratio:
315 min_ratio = line_ratio
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:
327 def GraphicsStraightenLine(point1, point2):
328 dx = point2[0] - point1[0]
329 dy = point2[1] - point1[1]
333 elif abs(float(dy) / dx) > 1:
334 point2[0] = point1[0]
336 point2[1] = point1[1]
340 def GetPointOnLine(x1, y1, x2, y2, length):
341 l = math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
345 i_bar = (x2 - x1) / l
346 j_bar = (y2 - y1) / l
348 return -length * i_bar + x2, -length * j_bar + y2
352 def GetArrowPoints(x1, y1, x2, y2, length, width):
353 l = math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
358 i_bar = (x2 - x1) / l
359 j_bar = (y2 - y1) / l
361 x3 = -length * i_bar + x2
362 y3 = -length * j_bar + y2
364 return x2, y2, width * -j_bar + x3, width * i_bar + y3, -width * -j_bar + x3, -width * i_bar + y3
368 def DrawArcToEllipse(x1, y1, width1, height1, x2, y2, x3, y3):
372 # Check that x2 != x3
373 if abs(x2 - x3) < 0.05:
376 y4 = y1 - math.sqrt((b1 * b1 - (((x2 - x1) * (x2 - x1)) * (b1 * b1) / (a1 * a1))))
378 y4 = y1 + math.sqrt((b1 * b1 - (((x2 - x1) * (x2 - x1)) * (b1 * b1) / (a1 * a1))))
381 # Calculate the x and y coordinates of the point where arc intersects ellipse
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)
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))
393 # In this case the line intersects the ellipse, so calculate intersection
395 ellipse1_x = ((F * -1) + math.sqrt(K)) / (2 * E)
396 ellipse1_y = ((H * (ellipse1_x - x2)) + y2)
398 ellipse1_x = (((F * -1) - math.sqrt(K)) / (2 * E))
399 ellipse1_y = ((H * (ellipse1_x - x2)) + y2)
401 # in this case, arc does not intersect ellipse, so just draw arc
405 return ellipse1_x, ellipse1_y
409 def FindEndForCircle(radius, x1, y1, x2, y2):
410 H = math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
415 return radius * (x2 - x1) / H + x1, radius * (y2 - y1) / H + y1