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 #----------------------------------------------------------------------------
14 from __future__
import division
20 # Rectangle and most other shapes
21 CONTROL_POINT_VERTICAL
= 1
22 CONTROL_POINT_HORIZONTAL
= 2
23 CONTROL_POINT_DIAGONAL
= 3
26 CONTROL_POINT_ENDPOINT_TO
= 4
27 CONTROL_POINT_ENDPOINT_FROM
= 5
28 CONTROL_POINT_LINE
= 6
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
37 ATTACHMENT_MODE_NONE
, ATTACHMENT_MODE_EDGE
, ATTACHMENT_MODE_BRANCHING
= 0, 1, 2
40 SHADOW_NONE
, SHADOW_LEFT
, SHADOW_RIGHT
= 0, 1, 2
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
45 # Sub-modes for branching attachment mode
46 BRANCHING_ATTACHMENT_NORMAL
= 1
47 BRANCHING_ATTACHMENT_BLOB
= 2
49 # logical function to use when drawing rubberband boxes, etc.
52 CONTROL_POINT_SIZE
= 6
56 ARROW_HOLLOW_CIRCLE
= 1
57 ARROW_FILLED_CIRCLE
= 2
59 ARROW_SINGLE_OBLIQUE
= 4
60 ARROW_DOUBLE_OBLIQUE
= 5
64 # Position of arrow on line
65 ARROW_POSITION_START
= 0
66 ARROW_POSITION_END
= 1
67 ARROW_POSITION_MIDDLE
= 2
69 # Line alignment flags
71 LINE_ALIGNMENT_HORIZ
= 1
72 LINE_ALIGNMENT_VERT
= 0
73 LINE_ALIGNMENT_TO_NEXT_HANDLE
= 2
74 LINE_ALIGNMENT_NONE
= 0
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
):
99 elif text
[i
] in ["\012","\015"]:
114 word_list
.append(word
)
118 word_list
.append(None)
121 # Now, make a list of strings which can fit in the box
129 string_list
.append(buffer)
135 x
, y
= dc
.GetTextExtent(buffer)
137 # Don't fit within the bounding box if we're fitting
139 if (x
>width
) and not (formatMode
& FORMAT_SIZE_TO_CONTENTS
):
140 # Deal with first word being wider than box
142 string_list
.append(oldBuffer
)
145 string_list
.append(buffer)
151 def GetCentredTextExtent(dc
, text_list
, xpos
= 0, ypos
= 0, width
= 0, height
= 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
161 return max_width
, len(text_list
) * char_height
165 def CentreText(dc
, text_list
, xpos
, ypos
, width
, height
, formatMode
):
169 # First, get maximum dimensions of box enclosing text
174 # Store text extents for speed
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
182 max_height
= len(text_list
) * char_height
184 if formatMode
& FORMAT_CENTRE_VERT
:
185 if max_height
<height
:
186 yoffset
= ypos
- height
/ 2 + (height
- max_height
) / 2
188 yoffset
= ypos
- height
/ 2
194 if formatMode
& FORMAT_CENTRE_HORIZ
:
195 xoffset
= xpos
- width
/ 2
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
206 y
= i
* char_height
+ yoffset
208 line
.SetX(x
- xOffset
)
209 line
.SetY(y
- yOffset
)
213 def DrawFormattedText(dc
, text_list
, xpos
, ypos
, width
, height
, formatMode
):
214 if formatMode
& FORMAT_CENTRE_HORIZ
:
217 xoffset
= xpos
- width
/ 2
219 if formatMode
& FORMAT_CENTRE_VERT
:
222 yoffset
= ypos
- height
/ 2
224 # +1 to allow for rounding errors
225 dc
.SetClippingRegion(xpos
- width
/ 2, ypos
- height
/ 2, width
+ 1, height
+ 1)
227 for line
in text_list
:
228 dc
.DrawText(line
.GetText(), xoffset
+ line
.GetX(), yoffset
+ line
.GetY())
230 dc
.DestroyClippingRegion()
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
)
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]
244 return FindEndForPolyline(xvec
, yvec
, x2
, y2
, x1
, y1
)
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
)
255 # Check for parallel lines
256 if denominator_term
<0.005 and denominator_term
>-0.005:
259 line_constant
= float(numerator_term
) / denominator_term
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
)
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
273 return length_ratio
, k_line
277 def FindEndForPolyline(xvec
, yvec
, x1
, y1
, x2
, y2
):
283 for i
in range(1, len(xvec
)):
284 line_ratio
, other_ratio
= CheckLineIntersection(x1
, y1
, x2
, y2
, lastx
, lasty
, xvec
[i
], yvec
[i
])
288 if line_ratio
<min_ratio
:
289 min_ratio
= line_ratio
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
297 return x1
+ (x2
- x1
) * min_ratio
, y1
+ (y2
- y1
) * min_ratio
301 def PolylineHitTest(xvec
, yvec
, x1
, y1
, x2
, y2
):
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:
315 if line_ratio
<min_ratio
:
316 min_ratio
= line_ratio
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:
328 def GraphicsStraightenLine(point1
, point2
):
329 dx
= point2
[0] - point1
[0]
330 dy
= point2
[1] - point1
[1]
335 point2
[0] = point1
[0]
337 point2
[1] = point1
[0]
341 def GetPointOnLine(x1
, y1
, x2
, y2
, length
):
342 l
= sqrt((x2
- x1
) * (x2
- x1
) + (y2
- y1
) * (y2
- y1
))
346 i_bar
= (x2
- x1
) / l
347 j_bar
= (y2
- y1
) / l
349 return -length
* i_bar
+ x2
,-length
* j_bar
+ y2
353 def GetArrowPoints(x1
, y1
, x2
, y2
, length
, width
):
354 l
= sqrt((x2
- x1
) * (x2
- x1
) + (y2
- y1
) * (y2
- y1
))
359 i_bar
= (x2
- x1
) / l
360 j_bar
= (y2
- y1
) / l
362 x3
=-length
* i_bar
+ x2
363 y3
=-length
* j_bar
+ y2
365 return x2
, y2
, width
*-j_bar
+ x3
, width
* i_bar
+ y3
,-width
*-j_bar
+ x3
,-width
* i_bar
+ y3
369 def DrawArcToEllipse(x1
, y1
, width1
, height1
, x2
, y2
, x3
, y3
):
373 # Check that x2 != x3
374 if abs(x2
- x3
)<0.05:
377 y4
= y1
- sqrt((b1
* b1
- (((x2
- x1
) * (x2
- x1
)) * (b1
* b1
) / (a1
* a1
))))
379 y4
= y1
+ sqrt((b1
* b1
- (((x2
- x1
) * (x2
- x1
)) * (b1
* b1
) / (a1
* a1
))))
382 # Calculate the x and y coordinates of the point where arc intersects ellipse
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
)
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
) / (x2
- x2
))
391 K
= ((F
* F
) - (4 * E
* G
))
394 # In this case the line intersects the ellipse, so calculate intersection
396 ellipse1_x
= ((F
*-1) + sqrt(K
)) / (2 * E
)
397 ellipse1_y
= ((H
* (ellipse1_x
- x2
)) + y2
)
399 ellipse1_x
= (((F
*-1) - sqrt(K
)) / (2 * E
))
400 ellipse1_y
= ((H
* (ellipse1_x
- x2
)) + y2
)
402 # in this case, arc does not intersect ellipse, so just draw arc
406 return ellipse1_x
, ellipse1_y
410 def FindEndForCircle(radius
, x1
, y1
, x2
, y2
):
411 H
= sqrt((x2
- x1
) * (x2
- x1
) + (y2
- y1
) * (y2
- y1
))
416 return radius
* (x2
- x1
) / H
+ x1
, radius
* (y2
- y1
) / H
+ y1