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