""" It removes "from __future__ import division", fixes a couple of bugs and adds a lot of whitespace. Since I also removed an instance of [::-1] for list reversing, I think this ought to work on older pythons (I have not tested though). """ git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@27884 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
		
			
				
	
	
		
			416 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			416 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # -*- coding: iso-8859-1 -*-
 | |
| #----------------------------------------------------------------------------
 | |
| # Name:         oglmisc.py
 | |
| # Purpose:      Miscellaneous OGL support functions
 | |
| #
 | |
| # Author:       Pierre Hjälm (from C++ original by Julian Smart)
 | |
| #
 | |
| # Created:      2004-05-08
 | |
| # RCS-ID:       $Id$
 | |
| # Copyright:    (c) 2004 Pierre Hjälm - 1998 Julian Smart
 | |
| # Licence:      wxWindows license
 | |
| #----------------------------------------------------------------------------
 | |
| 
 | |
| import math
 | |
| 
 | |
| import wx
 | |
| 
 | |
| # Control point types
 | |
| # Rectangle and most other shapes
 | |
| CONTROL_POINT_VERTICAL = 1
 | |
| CONTROL_POINT_HORIZONTAL = 2
 | |
| CONTROL_POINT_DIAGONAL = 3
 | |
| 
 | |
| # Line
 | |
| CONTROL_POINT_ENDPOINT_TO = 4
 | |
| CONTROL_POINT_ENDPOINT_FROM = 5
 | |
| CONTROL_POINT_LINE = 6
 | |
| 
 | |
| # Types of formatting: can be combined in a bit list
 | |
| FORMAT_NONE = 0             # Left justification
 | |
| FORMAT_CENTRE_HORIZ = 1     # Centre horizontally
 | |
| FORMAT_CENTRE_VERT = 2      # Centre vertically
 | |
| FORMAT_SIZE_TO_CONTENTS = 4 # Resize shape to contents
 | |
| 
 | |
| # Attachment modes
 | |
| ATTACHMENT_MODE_NONE, ATTACHMENT_MODE_EDGE, ATTACHMENT_MODE_BRANCHING = 0, 1, 2
 | |
| 
 | |
| # Shadow mode
 | |
| SHADOW_NONE, SHADOW_LEFT, SHADOW_RIGHT = 0, 1, 2
 | |
| 
 | |
| OP_CLICK_LEFT, OP_CLICK_RIGHT, OP_DRAG_LEFT, OP_DRAG_RIGHT = 1, 2, 4, 8
 | |
| OP_ALL = OP_CLICK_LEFT | OP_CLICK_RIGHT | OP_DRAG_LEFT | OP_DRAG_RIGHT
 | |
| 
 | |
| # Sub-modes for branching attachment mode
 | |
| BRANCHING_ATTACHMENT_NORMAL = 1
 | |
| BRANCHING_ATTACHMENT_BLOB = 2
 | |
| 
 | |
| # logical function to use when drawing rubberband boxes, etc.
 | |
| OGLRBLF = wx.INVERT
 | |
| 
 | |
| CONTROL_POINT_SIZE = 6
 | |
| 
 | |
| # Types of arrowhead
 | |
| # (i) Built-in
 | |
| ARROW_HOLLOW_CIRCLE   = 1
 | |
| ARROW_FILLED_CIRCLE   = 2
 | |
| ARROW_ARROW           = 3
 | |
| ARROW_SINGLE_OBLIQUE  = 4
 | |
| ARROW_DOUBLE_OBLIQUE  = 5
 | |
| # (ii) Custom
 | |
| ARROW_METAFILE        = 20
 | |
| 
 | |
| # Position of arrow on line
 | |
| ARROW_POSITION_START  = 0
 | |
| ARROW_POSITION_END    = 1
 | |
| ARROW_POSITION_MIDDLE = 2
 | |
| 
 | |
| # Line alignment flags
 | |
| # Vertical by default
 | |
| LINE_ALIGNMENT_HORIZ              = 1
 | |
| LINE_ALIGNMENT_VERT               = 0
 | |
| LINE_ALIGNMENT_TO_NEXT_HANDLE     = 2
 | |
| LINE_ALIGNMENT_NONE               = 0
 | |
| 
 | |
| 
 | |
| 
 | |
| # Format a string to a list of strings that fit in the given box.
 | |
| # Interpret %n and 10 or 13 as a new line.
 | |
| def FormatText(dc, text, width, height, formatMode):
 | |
|     i = 0
 | |
|     word = ""
 | |
|     word_list = []
 | |
|     end_word = False
 | |
|     new_line = False
 | |
|     while i < len(text):
 | |
|         if text[i] == "%":
 | |
|             i += 1
 | |
|             if i == len(text):
 | |
|                 word += "%"
 | |
|             else:
 | |
|                 if text[i] == "n":
 | |
|                     new_line = True
 | |
|                     end_word = True
 | |
|                     i += 1
 | |
|                 else:
 | |
|                     word += "%" + text[i]
 | |
|                     i += 1
 | |
|         elif text[i] in ["\012","\015"]:
 | |
|             new_line = True
 | |
|             end_word = True
 | |
|             i += 1
 | |
|         elif text[i] == " ":
 | |
|             end_word = True
 | |
|             i += 1
 | |
|         else:
 | |
|             word += text[i]
 | |
|             i += 1
 | |
| 
 | |
|         if i == len(text):
 | |
|             end_word = True
 | |
| 
 | |
|         if end_word:
 | |
|             word_list.append(word)
 | |
|             word = ""
 | |
|             end_word = False
 | |
|         if new_line:
 | |
|             word_list.append(None)
 | |
|             new_line = False
 | |
| 
 | |
|     # Now, make a list of strings which can fit in the box
 | |
|     string_list = []
 | |
|     buffer = ""
 | |
|     for s in word_list:
 | |
|         oldBuffer = buffer
 | |
|         if s is None:
 | |
|             # FORCE NEW LINE
 | |
|             if len(buffer) > 0:
 | |
|                 string_list.append(buffer)
 | |
|             buffer = ""
 | |
|         else:
 | |
|             if len(buffer):
 | |
|                 buffer += " "
 | |
|             buffer += s
 | |
|             x, y = dc.GetTextExtent(buffer)
 | |
| 
 | |
|             # Don't fit within the bounding box if we're fitting
 | |
|             # shape to contents
 | |
|             if (x > width) and not (formatMode & FORMAT_SIZE_TO_CONTENTS):
 | |
|                 # Deal with first word being wider than box
 | |
|                 if len(oldBuffer):
 | |
|                     string_list.append(oldBuffer)
 | |
|                 buffer = s
 | |
|     if len(buffer):
 | |
|         string_list.append(buffer)
 | |
| 
 | |
|     return string_list
 | |
| 
 | |
| 
 | |
| 
 | |
| def GetCentredTextExtent(dc, text_list, xpos = 0, ypos = 0, width = 0, height = 0):
 | |
|     if not text_list:
 | |
|         return 0, 0
 | |
| 
 | |
|     max_width = 0
 | |
|     for line in text_list:
 | |
|         current_width, char_height = dc.GetTextExtent(line)
 | |
|         if current_width > max_width:
 | |
|             max_width = current_width
 | |
| 
 | |
|     return max_width, len(text_list) * char_height
 | |
| 
 | |
| 
 | |
| 
 | |
| def CentreText(dc, text_list, xpos, ypos, width, height, formatMode):
 | |
|     if not text_list:
 | |
|         return
 | |
| 
 | |
|     # First, get maximum dimensions of box enclosing text
 | |
|     char_height = 0
 | |
|     max_width = 0
 | |
|     current_width = 0
 | |
| 
 | |
|     # Store text extents for speed
 | |
|     widths = []
 | |
|     for line in text_list:
 | |
|         current_width, char_height = dc.GetTextExtent(line.GetText())
 | |
|         widths.append(current_width)
 | |
|         if current_width > max_width:
 | |
|             max_width = current_width
 | |
| 
 | |
|     max_height = len(text_list) * char_height
 | |
| 
 | |
|     if formatMode & FORMAT_CENTRE_VERT:
 | |
|         if max_height < height:
 | |
|             yoffset = ypos - height / 2.0 + (height - max_height) / 2.0
 | |
|         else:
 | |
|             yoffset = ypos - height / 2.0
 | |
|         yOffset = ypos
 | |
|     else:
 | |
|         yoffset = 0.0
 | |
|         yOffset = 0.0
 | |
| 
 | |
|     if formatMode & FORMAT_CENTRE_HORIZ:
 | |
|         xoffset = xpos - width / 2.0
 | |
|         xOffset = xpos
 | |
|     else:
 | |
|         xoffset = 0.0
 | |
|         xOffset = 0.0
 | |
| 
 | |
|     for i, line in enumerate(text_list):
 | |
|         if formatMode & FORMAT_CENTRE_HORIZ and widths[i] < width:
 | |
|             x = (width - widths[i]) / 2.0 + xoffset
 | |
|         else:
 | |
|             x = xoffset
 | |
|         y = i * char_height + yoffset
 | |
| 
 | |
|         line.SetX(x - xOffset)
 | |
|         line.SetY(y - yOffset)
 | |
|         
 | |
| 
 | |
| 
 | |
| def DrawFormattedText(dc, text_list, xpos, ypos, width, height, formatMode):
 | |
|     if formatMode & FORMAT_CENTRE_HORIZ:
 | |
|         xoffset = xpos
 | |
|     else:
 | |
|         xoffset = xpos - width / 2.0
 | |
| 
 | |
|     if formatMode & FORMAT_CENTRE_VERT:
 | |
|         yoffset = ypos
 | |
|     else:
 | |
|         yoffset = ypos - height / 2.0
 | |
| 
 | |
|     # +1 to allow for rounding errors
 | |
|     dc.SetClippingRegion(xpos - width / 2.0, ypos - height / 2.0, width + 1, height + 1)
 | |
| 
 | |
|     for line in text_list:
 | |
|         dc.DrawText(line.GetText(), xoffset + line.GetX(), yoffset + line.GetY())
 | |
| 
 | |
|     dc.DestroyClippingRegion()
 | |
| 
 | |
| 
 | |
| 
 | |
| def RoughlyEqual(val1, val2, tol = 0.00001):
 | |
|     return val1 < (val2 + tol) and val1 > (val2 - tol) and \
 | |
|            val2 < (val1 + tol) and val2 > (val1 - tol)
 | |
| 
 | |
| 
 | |
| 
 | |
| def FindEndForBox(width, height, x1, y1, x2, y2):
 | |
|     xvec = [x1 - width / 2.0, x1 - width / 2.0, x1 + width / 2.0, x1 + width / 2.0, x1 - width / 2.0]
 | |
|     yvec = [y1 - height / 2.0, y1 + height / 2.0, y1 + height / 2.0, y1 - height / 2.0, y1 - height / 2.0]
 | |
| 
 | |
|     return FindEndForPolyline(xvec, yvec, x2, y2, x1, y1)
 | |
| 
 | |
| 
 | |
| 
 | |
| def CheckLineIntersection(x1, y1, x2, y2, x3, y3, x4, y4):
 | |
|     denominator_term = (y4 - y3) * (x2 - x1) - (y2 - y1) * (x4 - x3)
 | |
|     numerator_term = (x3 - x1) * (y4 - y3) + (x4 - x3) * (y1 - y3)
 | |
| 
 | |
|     length_ratio = 1.0
 | |
|     k_line = 1.0
 | |
| 
 | |
|     # Check for parallel lines
 | |
|     if denominator_term < 0.005 and denominator_term > -0.005:
 | |
|         line_constant = -1.0
 | |
|     else:
 | |
|         line_constant = float(numerator_term) / denominator_term
 | |
| 
 | |
|     # Check for intersection
 | |
|     if line_constant < 1.0 and line_constant > 0.0:
 | |
|         # Now must check that other line hits
 | |
|         if (y4 - y3) < 0.005 and (y4 - y3) > -0.005:
 | |
|             k_line = (x1 - x3 + line_constant * (x2 - x1)) / (x4 - x3)
 | |
|         else:
 | |
|             k_line = (y1 - y3 + line_constant * (y2 - y1)) / (y4 - y3)
 | |
|         if k_line >= 0 and k_line < 1:
 | |
|             length_ratio = line_constant
 | |
|         else:
 | |
|             k_line = 1
 | |
| 
 | |
|     return length_ratio, k_line
 | |
| 
 | |
| 
 | |
| 
 | |
| def FindEndForPolyline(xvec, yvec, x1, y1, x2, y2):
 | |
|     lastx = xvec[0]
 | |
|     lasty = yvec[0]
 | |
| 
 | |
|     min_ratio = 1.0
 | |
| 
 | |
|     for i in range(1, len(xvec)):
 | |
|         line_ratio, other_ratio = CheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[i], yvec[i])
 | |
|         lastx = xvec[i]
 | |
|         lasty = yvec[i]
 | |
| 
 | |
|         if line_ratio < min_ratio:
 | |
|             min_ratio = line_ratio
 | |
| 
 | |
|     # Do last (implicit) line if last and first doubles are not identical
 | |
|     if not (xvec[0] == lastx and yvec[0] == lasty):
 | |
|         line_ratio, other_ratio = CheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[0], yvec[0])
 | |
|         if line_ratio < min_ratio:
 | |
|             min_ratio = line_ratio
 | |
| 
 | |
|     return x1 + (x2 - x1) * min_ratio, y1 + (y2 - y1) * min_ratio
 | |
| 
 | |
| 
 | |
| 
 | |
| def PolylineHitTest(xvec, yvec, x1, y1, x2, y2):
 | |
|     isAHit = False
 | |
|     lastx = xvec[0]
 | |
|     lasty = yvec[0]
 | |
| 
 | |
|     min_ratio = 1.0
 | |
| 
 | |
|     for i in range(1, len(xvec)):
 | |
|         line_ratio, other_ratio = CheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[i], yvec[i])
 | |
|         if line_ratio != 1.0:
 | |
|             isAHit = True
 | |
|         lastx = xvec[i]
 | |
|         lasty = yvec[i]
 | |
| 
 | |
|         if line_ratio < min_ratio:
 | |
|             min_ratio = line_ratio
 | |
| 
 | |
|     # Do last (implicit) line if last and first doubles are not identical
 | |
|     if not (xvec[0] == lastx and yvec[0] == lasty):
 | |
|         line_ratio, other_ratio = CheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[0], yvec[0])
 | |
|         if line_ratio != 1.0:
 | |
|             isAHit = True
 | |
| 
 | |
|     return isAHit
 | |
| 
 | |
| 
 | |
| 
 | |
| def GraphicsStraightenLine(point1, point2):
 | |
|     dx = point2[0] - point1[0]
 | |
|     dy = point2[1] - point1[1]
 | |
| 
 | |
|     if dx == 0:
 | |
|         return
 | |
|     elif abs(float(dy) / dx) > 1:
 | |
|         point2[0] = point1[0]
 | |
|     else:
 | |
|         point2[1] = point1[0]
 | |
| 
 | |
| 
 | |
| 
 | |
| def GetPointOnLine(x1, y1, x2, y2, length):
 | |
|     l = math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
 | |
|     if l < 0.01:
 | |
|         l = 0.01
 | |
| 
 | |
|     i_bar = (x2 - x1) / l
 | |
|     j_bar = (y2 - y1) / l
 | |
| 
 | |
|     return -length * i_bar + x2, -length * j_bar + y2
 | |
| 
 | |
| 
 | |
| 
 | |
| def GetArrowPoints(x1, y1, x2, y2, length, width):
 | |
|     l = math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
 | |
| 
 | |
|     if l < 0.01:
 | |
|         l = 0.01
 | |
| 
 | |
|     i_bar = (x2 - x1) / l
 | |
|     j_bar = (y2 - y1) / l
 | |
|     
 | |
|     x3 = -length * i_bar + x2
 | |
|     y3 = -length * j_bar + y2
 | |
| 
 | |
|     return x2, y2, width * -j_bar + x3, width * i_bar + y3, -width * -j_bar + x3, -width * i_bar + y3
 | |
| 
 | |
| 
 | |
| 
 | |
| def DrawArcToEllipse(x1, y1, width1, height1, x2, y2, x3, y3):
 | |
|     a1 = width1 / 2.0
 | |
|     b1 = height1 / 2.0
 | |
| 
 | |
|     # Check that x2 != x3
 | |
|     if abs(x2 - x3) < 0.05:
 | |
|         x4 = x2
 | |
|         if y3 > y2:
 | |
|             y4 = y1 - math.sqrt((b1 * b1 - (((x2 - x1) * (x2 - x1)) * (b1 * b1) / (a1 * a1))))
 | |
|         else:
 | |
|             y4 = y1 + math.sqrt((b1 * b1 - (((x2 - x1) * (x2 - x1)) * (b1 * b1) / (a1 * a1))))
 | |
|         return x4, y4
 | |
| 
 | |
|     # Calculate the x and y coordinates of the point where arc intersects ellipse
 | |
|     A = (1 / (a1 * a1))
 | |
|     B = ((y3 - y2) * (y3 - y2)) / ((x3 - x2) * (x3 - x2) * b1 * b1)
 | |
|     C = (2 * (y3 - y2) * (y2 - y1)) / ((x3 - x2) * b1 * b1)
 | |
|     D = ((y2 - y1) * (y2 - y1)) / (b1 * b1)
 | |
|     E = (A + B)
 | |
|     F = (C - (2 * A * x1) - (2 * B * x2))
 | |
|     G = ((A * x1 * x1) + (B * x2 * x2) - (C * x2) + D - 1)
 | |
|     H = (float(y3 - y2) / (x2 - x2))
 | |
|     K = ((F * F) - (4 * E * G))
 | |
| 
 | |
|     if K >= 0:
 | |
|         # In this case the line intersects the ellipse, so calculate intersection
 | |
|         if x2 >= x1:
 | |
|             ellipse1_x = ((F * -1) + math.sqrt(K)) / (2 * E)
 | |
|             ellipse1_y = ((H * (ellipse1_x - x2)) + y2)
 | |
|         else:
 | |
|             ellipse1_x = (((F * -1) - math.sqrt(K)) / (2 * E))
 | |
|             ellipse1_y = ((H * (ellipse1_x - x2)) + y2)
 | |
|     else:
 | |
|         # in this case, arc does not intersect ellipse, so just draw arc
 | |
|         ellipse1_x = x3
 | |
|         ellipse1_y = y3
 | |
| 
 | |
|     return ellipse1_x, ellipse1_y
 | |
| 
 | |
| 
 | |
| 
 | |
| def FindEndForCircle(radius, x1, y1, x2, y2):
 | |
|     H = math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
 | |
| 
 | |
|     if H == 0:
 | |
|         return x1, y1
 | |
|     else:
 | |
|         return radius * (x2 - x1) / H + x1, radius * (y2 - y1) / H + y1
 |