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