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