Files
wxWidgets/wxPython/wx/lib/ogl/_oglmisc.py
Robin Dunn b2f6eb0606 Since everything in the submodules is to appear in the pacakge
namespace rename the submodule to have a leading underscore to make it
easier to document it that way.


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@27634 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2004-06-04 20:12:01 +00:00

417 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
#----------------------------------------------------------------------------
from __future__ import division
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 + (height - max_height) / 2
else:
yoffset = ypos - height / 2
yOffset = ypos
else:
yoffset = 0.0
yOffset = 0.0
if formatMode & FORMAT_CENTRE_HORIZ:
xoffset = xpos - width / 2
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 + 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
if formatMode & FORMAT_CENTRE_VERT:
yoffset = ypos
else:
yoffset = ypos - height / 2
# +1 to allow for rounding errors
dc.SetClippingRegion(xpos - width / 2, ypos - height / 2, 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, x1 - width / 2, x1 + width / 2, x1 + width / 2, x1 - width / 2]
yvec = [y1 - height / 2, y1 + height / 2, y1 + height / 2, y1 - height / 2, y1 - height / 2]
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(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
b1 = height1 / 2
# 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 = ((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