Added a Python port of the OGL library, deprecated the C++ wrapped version.
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@27447 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
14
wxPython/wx/lib/ogl/__init__.py
Normal file
14
wxPython/wx/lib/ogl/__init__.py
Normal file
@@ -0,0 +1,14 @@
|
||||
"""
|
||||
The Object Graphics Library provides for simple drawing and manipulation
|
||||
of 2D objects.
|
||||
"""
|
||||
|
||||
__all__ = ["basic", "diagram", "canvas", "lines", "bmpshape", "divided", "composit"]
|
||||
|
||||
from basic import *
|
||||
from diagram import *
|
||||
from canvas import *
|
||||
from lines import *
|
||||
from bmpshape import *
|
||||
from divided import *
|
||||
from composit import *
|
3173
wxPython/wx/lib/ogl/basic.py
Normal file
3173
wxPython/wx/lib/ogl/basic.py
Normal file
File diff suppressed because it is too large
Load Diff
66
wxPython/wx/lib/ogl/bmpshape.py
Normal file
66
wxPython/wx/lib/ogl/bmpshape.py
Normal file
@@ -0,0 +1,66 @@
|
||||
# -*- coding: iso-8859-1 -*-
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: bmpshape.py
|
||||
# Purpose: Bitmap shape
|
||||
#
|
||||
# Author: Pierre Hj<48>lm (from C++ original by Julian Smart)
|
||||
#
|
||||
# Created: 20040508
|
||||
# RCS-ID:
|
||||
# Copyright: (c) 2004 Pierre Hj<48>lm - 1998 Julian Smart
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
from __future__ import division
|
||||
|
||||
from basic import RectangleShape
|
||||
|
||||
|
||||
class BitmapShape(RectangleShape):
|
||||
"""Draws a bitmap (non-resizable)."""
|
||||
def __init__(self):
|
||||
RectangleShape.__init__(self, 100, 50)
|
||||
self._filename=""
|
||||
|
||||
def OnDraw(self, dc):
|
||||
if not self._bitmap.Ok():
|
||||
return
|
||||
|
||||
x = self._xpos-self._bitmap.GetWidth() / 2
|
||||
y = self._ypos-self._bitmap.GetHeight() / 2
|
||||
dc.DrawBitmap(self._bitmap, x, y, True)
|
||||
|
||||
def SetSize(self, w, h, recursive = True):
|
||||
if self._bitmap.Ok():
|
||||
w = self._bitmap.GetWidth()
|
||||
h = self._bitmap.GetHeight()
|
||||
|
||||
self.SetAttachmentSize(w, h)
|
||||
|
||||
self._width = w
|
||||
self._height = h
|
||||
|
||||
self.SetDefaultRegionSize()
|
||||
|
||||
def GetBitmap(self):
|
||||
"""Return a the bitmap associated with this shape."""
|
||||
return self._bitmap
|
||||
|
||||
def SetBitmap(self, bitmap):
|
||||
"""Set the bitmap associated with this shape.
|
||||
|
||||
You can delete the bitmap from the calling application, since
|
||||
reference counting will take care of holding on to the internal bitmap
|
||||
data.
|
||||
"""
|
||||
self._bitmap = bitmap
|
||||
if self._bitmap.Ok():
|
||||
self.SetSize(self._bitmap.GetWidth(), self._bitmap.GetHeight())
|
||||
|
||||
def SetFilename(self, f):
|
||||
"""Set the bitmap filename."""
|
||||
self._filename = f
|
||||
|
||||
def GetFilename(self):
|
||||
"""Return the bitmap filename."""
|
||||
return self._filename
|
360
wxPython/wx/lib/ogl/canvas.py
Normal file
360
wxPython/wx/lib/ogl/canvas.py
Normal file
@@ -0,0 +1,360 @@
|
||||
# -*- coding: iso-8859-1 -*-
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: canvas.py
|
||||
# Purpose: The canvas class
|
||||
#
|
||||
# Author: Pierre Hj<48>lm (from C++ original by Julian Smart)
|
||||
#
|
||||
# Created: 20040508
|
||||
# RCS-ID:
|
||||
# Copyright: (c) 2004 Pierre Hj<48>lm - 1998 Julian Smart
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
from __future__ import division
|
||||
|
||||
import wx
|
||||
from lines import LineShape
|
||||
from composit import *
|
||||
|
||||
NoDragging, StartDraggingLeft, ContinueDraggingLeft, StartDraggingRight, ContinueDraggingRight = 0, 1, 2, 3, 4
|
||||
|
||||
KEY_SHIFT, KEY_CTRL = 1, 2
|
||||
|
||||
|
||||
|
||||
# Helper function: True if 'contains' wholly contains 'contained'.
|
||||
def WhollyContains(contains, contained):
|
||||
xp1, yp1 = contains.GetX(), contains.GetY()
|
||||
xp2, yp2 = contained.GetX(), contained.GetY()
|
||||
|
||||
w1, h1 = contains.GetBoundingBoxMax()
|
||||
w2, h2 = contained.GetBoundingBoxMax()
|
||||
|
||||
left1 = xp1-w1 / 2.0
|
||||
top1 = yp1-h1 / 2.0
|
||||
right1 = xp1 + w1 / 2.0
|
||||
bottom1 = yp1 + h1 / 2.0
|
||||
|
||||
left2 = xp2-w2 / 2.0
|
||||
top2 = yp2-h2 / 2.0
|
||||
right2 = xp2 + w2 / 2.0
|
||||
bottom2 = yp2 + h2 / 2.0
|
||||
|
||||
return ((left1 <= left2) and (top1 <= top2) and (right1 >= right2) and (bottom1 >= bottom2))
|
||||
|
||||
|
||||
|
||||
class ShapeCanvas(wx.ScrolledWindow):
|
||||
def __init__(self, parent = None, id=-1, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.BORDER, name="ShapeCanvas"):
|
||||
wx.ScrolledWindow.__init__(self, parent, id, pos, size, style, name)
|
||||
|
||||
self._shapeDiagram = None
|
||||
self._dragState = NoDragging
|
||||
self._draggedShape = None
|
||||
self._oldDragX = 0
|
||||
self._oldDragY = 0
|
||||
self._firstDragX = 0
|
||||
self._firstDragY = 0
|
||||
self._checkTolerance = True
|
||||
|
||||
wx.EVT_PAINT(self, self.OnPaint)
|
||||
wx.EVT_MOUSE_EVENTS(self, self.OnMouseEvent)
|
||||
|
||||
def SetDiagram(self, diag):
|
||||
self._shapeDiagram = diag
|
||||
|
||||
def GetDiagram(self):
|
||||
return self._shapeDiagram
|
||||
|
||||
def OnPaint(self, evt):
|
||||
dc = wx.PaintDC(self)
|
||||
self.PrepareDC(dc)
|
||||
|
||||
dc.SetBackground(wx.Brush(self.GetBackgroundColour(), wx.SOLID))
|
||||
dc.Clear()
|
||||
|
||||
if self.GetDiagram():
|
||||
self.GetDiagram().Redraw(dc)
|
||||
|
||||
def OnMouseEvent(self, evt):
|
||||
dc = wx.ClientDC(self)
|
||||
self.PrepareDC(dc)
|
||||
|
||||
x, y = evt.GetLogicalPosition(dc)
|
||||
|
||||
keys = 0
|
||||
if evt.ShiftDown():
|
||||
keys |= KEY_SHIFT
|
||||
if evt.ControlDown():
|
||||
keys |= KEY_CTRL
|
||||
|
||||
dragging = evt.Dragging()
|
||||
|
||||
# Check if we're within the tolerance for mouse movements.
|
||||
# If we're very close to the position we started dragging
|
||||
# from, this may not be an intentional drag at all.
|
||||
if dragging:
|
||||
dx = abs(dc.LogicalToDeviceX(x-self._firstDragX))
|
||||
dy = abs(dc.LogicalToDeviceY(y-self._firstDragY))
|
||||
if self._checkTolerance and (dx <= self.GetDiagram().GetMouseTolerance()) and (dy <= self.GetDiagram().GetMouseTolerance()):
|
||||
return
|
||||
# If we've ignored the tolerance once, then ALWAYS ignore
|
||||
# tolerance in this drag, even if we come back within
|
||||
# the tolerance range.
|
||||
self._checkTolerance = False
|
||||
|
||||
# Dragging - note that the effect of dragging is left entirely up
|
||||
# to the object, so no movement is done unless explicitly done by
|
||||
# object.
|
||||
if dragging and self._draggedShape and self._dragState == StartDraggingLeft:
|
||||
self._dragState = ContinueDraggingLeft
|
||||
|
||||
# If the object isn't m_draggable, transfer message to canvas
|
||||
if self._draggedShape.Draggable():
|
||||
self._draggedShape.GetEventHandler().OnBeginDragLeft(x, y, keys, self._draggedAttachment)
|
||||
else:
|
||||
self._draggedShape = None
|
||||
self.OnBeginDragLeft(x, y, keys)
|
||||
|
||||
self._oldDragX, self._oldDragY = x, y
|
||||
|
||||
elif dragging and self._draggedShape and self._dragState == ContinueDraggingLeft:
|
||||
# Continue dragging
|
||||
self._draggedShape.GetEventHandler().OnDragLeft(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
|
||||
self._draggedShape.GetEventHandler().OnDragLeft(True, x, y, keys, self._draggedAttachment)
|
||||
self._oldDragX, self._oldDragY = x, y
|
||||
|
||||
elif evt.LeftUp and self._draggedShape and self._dragState == ContinueDraggingLeft:
|
||||
self._dragState = NoDragging
|
||||
self._checkTolerance = True
|
||||
|
||||
self._draggedShape.GetEventHandler().OnDragLeft(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
|
||||
self._draggedShape.GetEventHandler().OnEndDragLeft(x, y, keys, self._draggedAttachment)
|
||||
self._draggedShape = None
|
||||
|
||||
elif dragging and self._draggedShape and self._dragState == StartDraggingRight:
|
||||
self._dragState = ContinueDraggingRight
|
||||
if self._draggedShape.Draggable:
|
||||
self._draggedShape.GetEventHandler().OnBeginDragRight(x, y, keys, self._draggedAttachment)
|
||||
else:
|
||||
self._draggedShape = None
|
||||
self.OnBeginDragRight(x, y, keys)
|
||||
self._oldDragX, self._oldDragY = x, y
|
||||
|
||||
elif dragging and self._draggedShape and self._dragState == ContinueDraggingRight:
|
||||
# Continue dragging
|
||||
self._draggedShape.GetEventHandler().OnDragRight(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
|
||||
self._draggedShape.GetEventHandler().OnDragRight(True, x, y, keys, self._draggedAttachment)
|
||||
self._oldDragX, self._oldDragY = x, y
|
||||
|
||||
elif evt.RightUp() and self._draggedShape and self._dragState == ContinueDraggingRight:
|
||||
self._dragState = NoDragging
|
||||
self._checkTolerance = True
|
||||
|
||||
self._draggedShape.GetEventHandler().OnDragRight(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
|
||||
self._draggedShape.GetEventHandler().OnEndDragRight(x, y, keys, self._draggedAttachment)
|
||||
self._draggedShape = None
|
||||
|
||||
# All following events sent to canvas, not object
|
||||
elif dragging and not self._draggedShape and self._dragState == StartDraggingLeft:
|
||||
self._dragState = ContinueDraggingLeft
|
||||
self.OnBeginDragLeft(x, y, keys)
|
||||
self._oldDragX, self._oldDragY = x, y
|
||||
|
||||
elif dragging and not self._draggedShape and self._dragState == ContinueDraggingLeft:
|
||||
# Continue dragging
|
||||
self.OnDragLeft(False, self._oldDragX, self._oldDragY, keys)
|
||||
self.OnDragLeft(True, x, y, keys)
|
||||
self._oldDragX, self._oldDragY = x, y
|
||||
|
||||
elif evt.LeftUp() and not self._draggedShape and self._dragState == ContinueDraggingLeft:
|
||||
self._dragState = NoDragging
|
||||
self._checkTolerance = True
|
||||
|
||||
self.OnDragLeft(False, self._oldDragX, self._oldDragY, keys)
|
||||
self.OnEndDragLeft(x, y, keys)
|
||||
self._draggedShape = None
|
||||
|
||||
elif dragging and not self._draggedShape and self._dragState == StartDraggingRight:
|
||||
self._dragState = ContinueDraggingRight
|
||||
self.OnBeginDragRight(x, y, keys)
|
||||
self._oldDragX, self._oldDragY = x, y
|
||||
|
||||
elif dragging and not self._draggedShape and self._dragState == ContinueDraggingRight:
|
||||
# Continue dragging
|
||||
self.OnDragRight(False, self._oldDragX, self._oldDragY, keys)
|
||||
self.OnDragRight(True, x, y, keys)
|
||||
self._oldDragX, self._oldDragY = x, y
|
||||
|
||||
elif evt.RightUp() and not self._draggedShape and self._dragState == ContinueDraggingRight:
|
||||
self._dragState = NoDragging
|
||||
self._checkTolerance = True
|
||||
|
||||
self.OnDragRight(False, self._oldDragX, self._oldDragY, keys)
|
||||
self.OnEndDragRight(x, y, keys)
|
||||
self._draggedShape = None
|
||||
|
||||
# Non-dragging events
|
||||
elif evt.IsButton():
|
||||
self._checkTolerance = True
|
||||
|
||||
# Find the nearest object
|
||||
attachment = 0
|
||||
|
||||
nearest_object, attachment = self.FindShape(x, y)
|
||||
if nearest_object: # Object event
|
||||
if evt.LeftDown():
|
||||
self._draggedShape = nearest_object
|
||||
self._draggedAttachment = attachment
|
||||
self._dragState = StartDraggingLeft
|
||||
self._firstDragX = x
|
||||
self._firstDragY = y
|
||||
|
||||
elif evt.LeftUp():
|
||||
# N.B. Only register a click if the same object was
|
||||
# identified for down *and* up.
|
||||
if nearest_object == self._draggedShape:
|
||||
nearest_object.GetEventHandler().OnLeftClick(x, y, keys, attachment)
|
||||
self._draggedShape = None
|
||||
self._dragState = NoDragging
|
||||
|
||||
elif evt.LeftDClick():
|
||||
nearest_object.GetEventHandler().OnLeftDoubleClick(x, y, keys, attachment)
|
||||
self._draggedShape = None
|
||||
self._dragState = NoDragging
|
||||
|
||||
elif evt.RightDown():
|
||||
self._draggedShape = nearest_object
|
||||
self._draggedAttachment = attachment
|
||||
self._dragState = StartDraggingRight
|
||||
self._firstDragX = x
|
||||
self._firstDragY = y
|
||||
|
||||
elif evt.RightUp():
|
||||
if nearest_object == self._draggedShape:
|
||||
nearest_object.GetEventHandler().OnRightClick(x, y, keys, attachment)
|
||||
self._draggedShape = None
|
||||
self._dragState = NoDragging
|
||||
|
||||
else: # Canvas event
|
||||
if evt.LeftDown():
|
||||
self._draggedShape = None
|
||||
self._dragState = StartDraggingLeft
|
||||
self._firstDragX = x
|
||||
self._firstDragY = y
|
||||
|
||||
elif evt.LeftUp():
|
||||
self.OnLeftClick(x, y, keys)
|
||||
self._draggedShape = None
|
||||
self._dragState = NoDragging
|
||||
|
||||
elif evt.RightDown():
|
||||
self._draggedShape = None
|
||||
self._dragState = StartDraggingRight
|
||||
self._firstDragX = x
|
||||
self._firstDragY = y
|
||||
|
||||
elif evt.RightUp():
|
||||
self.OnRightClick(x, y, keys)
|
||||
self._draggedShape = None
|
||||
self._dragState = NoDragging
|
||||
|
||||
def FindShape(self, x, y, info = None, notObject = None):
|
||||
nearest = 100000.0
|
||||
nearest_attachment = 0
|
||||
nearest_object = None
|
||||
|
||||
# Go backward through the object list, since we want:
|
||||
# (a) to have the control points drawn LAST to overlay
|
||||
# the other objects
|
||||
# (b) to find the control points FIRST if they exist
|
||||
|
||||
for object in self.GetDiagram().GetShapeList()[::-1]:
|
||||
# First pass for lines, which might be inside a container, so we
|
||||
# want lines to take priority over containers. This first loop
|
||||
# could fail if we clickout side a line, so then we'll
|
||||
# try other shapes.
|
||||
if object.IsShown() and \
|
||||
isinstance(object, LineShape) and \
|
||||
object.HitTest(x, y) and \
|
||||
((info == None) or isinstance(object, info)) and \
|
||||
(not notObject or not notObject.HasDescendant(object)):
|
||||
temp_attachment, dist = object.HitTest(x, y)
|
||||
# A line is trickier to spot than a normal object.
|
||||
# For a line, since it's the diagonal of the box
|
||||
# we use for the hit test, we may have several
|
||||
# lines in the box and therefore we need to be able
|
||||
# to specify the nearest point to the centre of the line
|
||||
# as our hit criterion, to give the user some room for
|
||||
# manouevre.
|
||||
if dist<nearest:
|
||||
nearest = dist
|
||||
nearest_object = object
|
||||
nearest_attachment = temp_attachment
|
||||
|
||||
for object in self.GetDiagram().GetShapeList()[::-1]:
|
||||
# On second pass, only ever consider non-composites or
|
||||
# divisions. If children want to pass up control to
|
||||
# the composite, that's up to them.
|
||||
if (object.IsShown() and
|
||||
(isinstance(object, DivisionShape) or
|
||||
not isinstance(object, CompositeShape)) and
|
||||
object.HitTest(x, y) and
|
||||
(info == None or isinstance(object, info)) and
|
||||
(not notObject or not notObject.HasDescendant(object))):
|
||||
temp_attachment, dist = object.HitTest(x, y)
|
||||
if not isinstance(object, LineShape):
|
||||
# If we've hit a container, and we have already
|
||||
# found a line in the first pass, then ignore
|
||||
# the container in case the line is in the container.
|
||||
# Check for division in case line straddles divisions
|
||||
# (i.e. is not wholly contained).
|
||||
if not nearest_object or not (isinstance(object, DivisionShape) or WhollyContains(object, nearest_object)):
|
||||
nearest_object = object
|
||||
nearest_attachment = temp_attachment
|
||||
break
|
||||
|
||||
return nearest_object, nearest_attachment
|
||||
|
||||
def AddShape(self, object, addAfter = None):
|
||||
self.GetDiagram().AddShape(object, addAfter)
|
||||
|
||||
def InsertShape(self, object):
|
||||
self.GetDiagram().InsertShape(object)
|
||||
|
||||
def RemoveShape(self, object):
|
||||
self.GetDiagram().RemoveShape(object)
|
||||
|
||||
def GetQuickEditMode(self):
|
||||
return self.GetDiagram().GetQuickEditMode()
|
||||
|
||||
def Redraw(self, dc):
|
||||
self.GetDiagram().Redraw(dc)
|
||||
|
||||
def Snap(self, x, y):
|
||||
return self.GetDiagram().Snap(x, y)
|
||||
|
||||
def OnLeftClick(self, x, y, keys = 0):
|
||||
pass
|
||||
|
||||
def OnRightClick(self, x, y, keys = 0):
|
||||
pass
|
||||
|
||||
def OnDragLeft(self, draw, x, y, keys = 0):
|
||||
pass
|
||||
|
||||
def OnBeginDragLeft(self, x, y, keys = 0):
|
||||
pass
|
||||
|
||||
def OnEndDragLeft(self, x, y, keys = 0):
|
||||
pass
|
||||
|
||||
def OnDragRight(self, draw, x, y, keys = 0):
|
||||
pass
|
||||
|
||||
def OnBeginDragRight(self, x, y, keys = 0):
|
||||
pass
|
||||
|
||||
def OnEndDragRight(self, x, y, keys = 0):
|
||||
pass
|
1410
wxPython/wx/lib/ogl/composit.py
Normal file
1410
wxPython/wx/lib/ogl/composit.py
Normal file
File diff suppressed because it is too large
Load Diff
160
wxPython/wx/lib/ogl/diagram.py
Normal file
160
wxPython/wx/lib/ogl/diagram.py
Normal file
@@ -0,0 +1,160 @@
|
||||
# -*- coding: iso-8859-1 -*-
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: diagram.py
|
||||
# Purpose: Diagram class
|
||||
#
|
||||
# Author: Pierre Hj<48>lm (from C++ original by Julian Smart)
|
||||
#
|
||||
# Created: 20040508
|
||||
# RCS-ID:
|
||||
# Copyright: (c) 2004 Pierre Hj<48>lm - 1998 Julian Smart
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
from __future__ import division
|
||||
|
||||
import wx
|
||||
|
||||
DEFAULT_MOUSE_TOLERANCE = 3
|
||||
|
||||
|
||||
|
||||
class Diagram(object):
|
||||
"""Encapsulates an entire diagram, with methods for drawing. A diagram has
|
||||
an associated ShapeCanvas.
|
||||
|
||||
Derived from:
|
||||
Object
|
||||
"""
|
||||
def __init__(self):
|
||||
self._diagramCanvas = None
|
||||
self._quickEditMode = False
|
||||
self._snapToGrid = True
|
||||
self._gridSpacing = 5.0
|
||||
self._shapeList = []
|
||||
self._mouseTolerance = DEFAULT_MOUSE_TOLERANCE
|
||||
|
||||
def Redraw(self, dc):
|
||||
"""Draw the shapes in the diagram on the specified device context."""
|
||||
if self._shapeList:
|
||||
if self.GetCanvas():
|
||||
self.GetCanvas().SetCursor(wx.HOURGLASS_CURSOR)
|
||||
for object in self._shapeList:
|
||||
object.Draw(dc)
|
||||
if self.GetCanvas():
|
||||
self.GetCanvas().SetCursor(wx.STANDARD_CURSOR)
|
||||
|
||||
def Clear(self, dc):
|
||||
"""Clear the specified device context."""
|
||||
dc.Clear()
|
||||
|
||||
def AddShape(self, object, addAfter = None):
|
||||
"""Adds a shape to the diagram. If addAfter is not None, the shape
|
||||
will be added after addAfter.
|
||||
"""
|
||||
if not object in self._shapeList:
|
||||
if addAfter:
|
||||
self._shapeList.insert(self._shapeList.index(addAfter) + 1, object)
|
||||
else:
|
||||
self._shapeList.append(object)
|
||||
|
||||
object.SetCanvas(self.GetCanvas())
|
||||
|
||||
def InsertShape(self, object):
|
||||
"""Insert a shape at the front of the shape list."""
|
||||
self._shapeList.insert(0, object)
|
||||
|
||||
def RemoveShape(self, object):
|
||||
"""Remove the shape from the diagram (non-recursively) but do not
|
||||
delete it.
|
||||
"""
|
||||
if object in self._shapeList:
|
||||
self._shapeList.remove(object)
|
||||
|
||||
def RemoveAllShapes(self):
|
||||
"""Remove all shapes from the diagram but do not delete the shapes."""
|
||||
self._shapeList = []
|
||||
|
||||
def DeleteAllShapes(self):
|
||||
"""Remove and delete all shapes in the diagram."""
|
||||
for shape in self._shapeList[:]:
|
||||
if not shape.GetParent():
|
||||
self.RemoveShape(shape)
|
||||
|
||||
def ShowAll(self, show):
|
||||
"""Call Show for each shape in the diagram."""
|
||||
for shape in self._shapeList:
|
||||
shape.Show()
|
||||
|
||||
def DrawOutLine(self, dc, x1, y1, x2, y2):
|
||||
"""Draw an outline rectangle on the current device context."""
|
||||
dc.SetPen(wx.Pen(wx.Color(0, 0, 0), 1, wx.DOT))
|
||||
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
||||
|
||||
dc.DrawLines([[x1, y1], [x2, y1], [x2, y2], [x1, y2], [x1, y1]])
|
||||
|
||||
def RecentreAll(self, dc):
|
||||
"""Make sure all text that should be centred, is centred."""
|
||||
for shape in self._shapeList:
|
||||
shape.Recentre(dc)
|
||||
|
||||
def SetCanvas(self, canvas):
|
||||
"""Set the canvas associated with this diagram."""
|
||||
self._diagramCanvas = canvas
|
||||
|
||||
def GetCanvas(self):
|
||||
"""Return the shape canvas associated with this diagram."""
|
||||
return self._diagramCanvas
|
||||
|
||||
def FindShape(self, id):
|
||||
"""Return the shape for the given identifier."""
|
||||
for shape in self._shapeList:
|
||||
if shape.GetId() == id:
|
||||
return shape
|
||||
return None
|
||||
|
||||
def Snap(self, x, y):
|
||||
"""'Snaps' the coordinate to the nearest grid position, if
|
||||
snap-to-grid is on."""
|
||||
if self._snapToGrid:
|
||||
return self._gridSpacing * int(x / self._gridSpacing + 0.5), self._gridSpacing * int(y / self._gridSpacing + 0.5)
|
||||
return x, y
|
||||
|
||||
def GetGridSpacing(self):
|
||||
"""Return the grid spacing."""
|
||||
return self._gridSpacing
|
||||
|
||||
def GetSnapToGrid(self):
|
||||
"""Return snap-to-grid mode."""
|
||||
return self._snapToGrid
|
||||
|
||||
def SetQuickEditMode(self, mode):
|
||||
"""Set quick-edit-mode on of off.
|
||||
|
||||
In this mode, refreshes are minimized, but the diagram may need
|
||||
manual refreshing occasionally.
|
||||
"""
|
||||
self._quickEditMode = mode
|
||||
|
||||
def GetQuickEditMode(self):
|
||||
"""Return quick edit mode."""
|
||||
return self._quickEditMode
|
||||
|
||||
def SetMouseTolerance(self, tolerance):
|
||||
"""Set the tolerance within which a mouse move is ignored.
|
||||
|
||||
The default is 3 pixels.
|
||||
"""
|
||||
self._mouseTolerance = tolerance
|
||||
|
||||
def GetMouseTolerance(self):
|
||||
"""Return the tolerance within which a mouse move is ignored."""
|
||||
return self._mouseTolerance
|
||||
|
||||
def GetShapeList(self):
|
||||
"""Return the internal shape list."""
|
||||
return self._shapeList
|
||||
|
||||
def GetCount(self):
|
||||
"""Return the number of shapes in the diagram."""
|
||||
return len(self._shapeList)
|
404
wxPython/wx/lib/ogl/divided.py
Normal file
404
wxPython/wx/lib/ogl/divided.py
Normal file
@@ -0,0 +1,404 @@
|
||||
# -*- coding: iso-8859-1 -*-
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: divided.py
|
||||
# Purpose: DividedShape class
|
||||
#
|
||||
# Author: Pierre Hj<48>lm (from C++ original by Julian Smart)
|
||||
#
|
||||
# Created: 20040508
|
||||
# RCS-ID:
|
||||
# Copyright: (c) 2004 Pierre Hj<48>lm - 1998 Julian Smart
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
from __future__ import division
|
||||
|
||||
import sys
|
||||
import wx
|
||||
|
||||
from basic import ControlPoint, RectangleShape, Shape
|
||||
from oglmisc import *
|
||||
|
||||
|
||||
|
||||
class DividedShapeControlPoint(ControlPoint):
|
||||
def __init__(self, the_canvas, object, region, size, the_m_xoffset, the_m_yoffset, the_type):
|
||||
ControlPoint.__init__(self, the_canvas, object, size, the_m_xoffset, the_m_yoffset, the_type)
|
||||
self.regionId = region
|
||||
|
||||
# Implement resizing of divided object division
|
||||
def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
|
||||
dc = wx.ClientDC(self.GetCanvas())
|
||||
self.GetCanvas().PrepareDC(dc)
|
||||
|
||||
dc.SetLogicalFunction(OGLRBLF)
|
||||
dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
|
||||
dc.SetPen(dottedPen)
|
||||
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
||||
|
||||
dividedObject = self._shape
|
||||
x1 = dividedObject.GetX()-dividedObject.GetWidth() / 2
|
||||
y1 = y
|
||||
x2 = dividedObject.GetX() + dividedObject.GetWidth() / 2
|
||||
y2 = y
|
||||
|
||||
dc.DrawLine(x1, y1, x2, y2)
|
||||
|
||||
def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
|
||||
dc = wx.ClientDC(self.GetCanvas())
|
||||
self.GetCanvas().PrepareDC(dc)
|
||||
|
||||
dc.SetLogicalFunction(OGLRBLF)
|
||||
dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
|
||||
dc.SetPen(dottedPen)
|
||||
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
||||
|
||||
dividedObject = self._shape
|
||||
|
||||
x1 = dividedObject.GetX()-dividedObject.GetWidth() / 2
|
||||
y1 = y
|
||||
x2 = dividedObject.GetX() + dividedObject.GetWidth() / 2
|
||||
y2 = y
|
||||
|
||||
dc.DrawLine(x1, y1, x2, y2)
|
||||
self._canvas.CaptureMouse()
|
||||
|
||||
def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
|
||||
dc = wx.ClientDC(self.GetCanvas())
|
||||
self.GetCanvas().PrepareDC(dc)
|
||||
|
||||
dividedObject = self._shape
|
||||
if not dividedObject.GetRegions()[self.regionId]:
|
||||
return
|
||||
|
||||
thisRegion = dividedObject.GetRegions()[self.regionId]
|
||||
nextRegion = None
|
||||
|
||||
dc.SetLogicalFunction(wx.COPY)
|
||||
|
||||
if self._canvas.HasCapture():
|
||||
self._canvas.ReleaseMouse()
|
||||
|
||||
# Find the old top and bottom of this region,
|
||||
# and calculate the new proportion for this region
|
||||
# if legal.
|
||||
currentY = dividedObject.GetY()-dividedObject.GetHeight() / 2
|
||||
maxY = dividedObject.GetY() + dividedObject.GetHeight() / 2
|
||||
|
||||
# Save values
|
||||
theRegionTop = 0
|
||||
nextRegionBottom = 0
|
||||
|
||||
for i in range(len(dividedObject.GetRegions())):
|
||||
region = dividedObject.GetRegions()[i]
|
||||
proportion = region._regionProportionY
|
||||
yy = currentY + dividedObject.GetHeight() * proportion
|
||||
actualY = min(maxY, yy)
|
||||
|
||||
if region == thisRegion:
|
||||
thisRegionTop = currentY
|
||||
|
||||
if i + 1<len(dividedObject.GetRegions()):
|
||||
nextRegion = dividedObject.GetRegions()[i + 1]
|
||||
if region == nextRegion:
|
||||
nextRegionBottom = actualY
|
||||
|
||||
currentY = actualY
|
||||
|
||||
if not nextRegion:
|
||||
return
|
||||
|
||||
# Check that we haven't gone above this region or below
|
||||
# next region.
|
||||
if y <= thisRegionTop or y >= nextRegionBottom:
|
||||
return
|
||||
|
||||
dividedObject.EraseLinks(dc)
|
||||
|
||||
# Now calculate the new proportions of this region and the next region
|
||||
thisProportion = (y-thisRegionTop) / dividedObject.GetHeight()
|
||||
nextProportion = (nextRegionBottom-y) / dividedObject.GetHeight()
|
||||
|
||||
thisRegion.SetProportions(0, thisProportion)
|
||||
nextRegion.SetProportions(0, nextProportion)
|
||||
self._yoffset = y-dividedObject.GetY()
|
||||
|
||||
# Now reformat text
|
||||
for i, region in enumerate(dividedObject.GetRegions()):
|
||||
if region.GetText():
|
||||
s = region.GetText()
|
||||
dividedObject.FormatText(dc, s, i)
|
||||
|
||||
dividedObject.SetRegionSizes()
|
||||
dividedObject.Draw(dc)
|
||||
dividedObject.GetEventHandler().OnMoveLinks(dc)
|
||||
|
||||
|
||||
|
||||
class DividedShape(RectangleShape):
|
||||
"""A DividedShape is a rectangle with a number of vertical divisions.
|
||||
Each division may have its text formatted with independent characteristics,
|
||||
and the size of each division relative to the whole image may be specified.
|
||||
|
||||
Derived from:
|
||||
RectangleShape
|
||||
"""
|
||||
def __init__(self, w, h):
|
||||
RectangleShape.__init__(self, w, h)
|
||||
self.ClearRegions()
|
||||
|
||||
def OnDraw(self, dc):
|
||||
RectangleShape.OnDraw(self, dc)
|
||||
|
||||
def OnDrawContents(self, dc):
|
||||
if self.GetRegions():
|
||||
defaultProportion = 1 / len(self.GetRegions())
|
||||
else:
|
||||
defaultProportion = 0
|
||||
currentY = self._ypos-self._height / 2
|
||||
maxY = self._ypos + self._height / 2
|
||||
|
||||
leftX = self._xpos-self._width / 2
|
||||
rightX = self._xpos + self._width / 2
|
||||
|
||||
if self._pen:
|
||||
dc.SetPen(self._pen)
|
||||
|
||||
dc.SetTextForeground(self._textColour)
|
||||
|
||||
# For efficiency, don't do this under X - doesn't make
|
||||
# any visible difference for our purposes.
|
||||
if sys.platform[:3]=="win":
|
||||
dc.SetTextBackground(self._brush.GetColour())
|
||||
|
||||
if self.GetDisableLabel():
|
||||
return
|
||||
|
||||
xMargin = 2
|
||||
yMargin = 2
|
||||
|
||||
dc.SetBackgroundMode(wx.TRANSPARENT)
|
||||
|
||||
for region in self.GetRegions():
|
||||
dc.SetFont(region.GetFont())
|
||||
dc.SetTextForeground(region.GetActualColourObject())
|
||||
|
||||
if region._regionProportionY<0:
|
||||
proportion = defaultProportion
|
||||
else:
|
||||
proportion = region._regionProportionY
|
||||
|
||||
y = currentY + self._height * proportion
|
||||
actualY = min(maxY, y)
|
||||
|
||||
centreX = self._xpos
|
||||
centreY = currentY + (actualY-currentY) / 2
|
||||
|
||||
DrawFormattedText(dc, region._formattedText, centreX, centreY, self._width-2 * xMargin, actualY-currentY-2 * yMargin, region._formatMode)
|
||||
|
||||
if y <= maxY and region != self.GetRegions()[-1]:
|
||||
regionPen = region.GetActualPen()
|
||||
if regionPen:
|
||||
dc.SetPen(regionPen)
|
||||
dc.DrawLine(leftX, y, rightX, y)
|
||||
|
||||
currentY = actualY
|
||||
|
||||
def SetSize(self, w, h, recursive = True):
|
||||
self.SetAttachmentSize(w, h)
|
||||
self._width = w
|
||||
self._height = h
|
||||
self.SetRegionSizes()
|
||||
|
||||
def SetRegionSizes(self):
|
||||
"""Set all region sizes according to proportions and this object
|
||||
total size.
|
||||
"""
|
||||
if not self.GetRegions():
|
||||
return
|
||||
|
||||
if self.GetRegions():
|
||||
defaultProportion = 1 / len(self.GetRegions())
|
||||
else:
|
||||
defaultProportion = 0
|
||||
currentY = self._ypos-self._height / 2
|
||||
maxY = self._ypos + self._height / 2
|
||||
|
||||
for region in self.GetRegions():
|
||||
if region._regionProportionY <= 0:
|
||||
proportion = defaultProportion
|
||||
else:
|
||||
proportion = region._regionProportionY
|
||||
|
||||
sizeY = proportion * self._height
|
||||
y = currentY + sizeY
|
||||
actualY = min(maxY, y)
|
||||
|
||||
centreY = currentY + (actualY-currentY) / 2
|
||||
|
||||
region.SetSize(self._width, sizeY)
|
||||
region.SetPosition(0, centreY-self._ypos)
|
||||
|
||||
currentY = actualY
|
||||
|
||||
# Attachment points correspond to regions in the divided box
|
||||
def GetAttachmentPosition(self, attachment, nth = 0, no_arcs = 1, line = None):
|
||||
totalNumberAttachments = len(self.GetRegions()) * 2 + 2
|
||||
if self.GetAttachmentMode() == ATTACHMENT_MODE_NONE or attachment >= totalNumberAttachments:
|
||||
return Shape.GetAttachmentPosition(self, attachment, nth, no_arcs)
|
||||
|
||||
n = len(self.GetRegions())
|
||||
isEnd = line and line.IsEnd(self)
|
||||
|
||||
left = self._xpos-self._width / 2
|
||||
right = self._xpos + self._width / 2
|
||||
top = self._ypos-self._height / 2
|
||||
bottom = self._ypos + self._height / 2
|
||||
|
||||
# Zero is top, n + 1 is bottom
|
||||
if attachment == 0:
|
||||
y = top
|
||||
if self._spaceAttachments:
|
||||
if line and line.GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE:
|
||||
# Align line according to the next handle along
|
||||
point = line.GetNextControlPoint(self)
|
||||
if point.x<left:
|
||||
x = left
|
||||
elif point.x>right:
|
||||
x = right
|
||||
else:
|
||||
x = point.x
|
||||
else:
|
||||
x = left + (nth + 1) * self._width / (no_arcs + 1)
|
||||
else:
|
||||
x = self._xpos
|
||||
elif attachment == n + 1:
|
||||
y = bottom
|
||||
if self._spaceAttachments:
|
||||
if line and line.GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE:
|
||||
# Align line according to the next handle along
|
||||
point = line.GetNextControlPoint(self)
|
||||
if point.x<left:
|
||||
x = left
|
||||
elif point.x>right:
|
||||
x = right
|
||||
else:
|
||||
x = point.x
|
||||
else:
|
||||
x = left + (nth + 1) * self._width / (no_arcs + 1)
|
||||
else:
|
||||
x = self._xpos
|
||||
else: # Left or right
|
||||
isLeft = not attachment<(n + 1)
|
||||
if isLeft:
|
||||
i = totalNumberAttachments-attachment-1
|
||||
else:
|
||||
i = attachment-1
|
||||
|
||||
region = self.GetRegions()[i]
|
||||
if region:
|
||||
if isLeft:
|
||||
x = left
|
||||
else:
|
||||
x = right
|
||||
|
||||
# Calculate top and bottom of region
|
||||
top = self._ypos + region._y-region._height / 2
|
||||
bottom = self._ypos + region._y + region._height / 2
|
||||
|
||||
# Assuming we can trust the absolute size and
|
||||
# position of these regions
|
||||
if self._spaceAttachments:
|
||||
if line and line.GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE:
|
||||
# Align line according to the next handle along
|
||||
point = line.GetNextControlPoint(self)
|
||||
if point.y<bottom:
|
||||
y = bottom
|
||||
elif point.y>top:
|
||||
y = top
|
||||
else:
|
||||
y = point.y
|
||||
else:
|
||||
y = top + (nth + 1) * region._height / (no_arcs + 1)
|
||||
else:
|
||||
y = self._ypos + region._y
|
||||
else:
|
||||
return False
|
||||
return x, y
|
||||
|
||||
def GetNumberOfAttachments(self):
|
||||
# There are two attachments for each region (left and right),
|
||||
# plus one on the top and one on the bottom.
|
||||
n = len(self.GetRegions()) * 2 + 2
|
||||
|
||||
maxN = n-1
|
||||
for point in self._attachmentPoints:
|
||||
if point._id>maxN:
|
||||
maxN = point._id
|
||||
|
||||
return maxN + 1
|
||||
|
||||
def AttachmentIsValid(self, attachment):
|
||||
totalNumberAttachments = len(self.GetRegions()) * 2 + 2
|
||||
if attachment >= totalNumberAttachments:
|
||||
return Shape.AttachmentIsValid(self, attachment)
|
||||
else:
|
||||
return attachment >= 0
|
||||
|
||||
def MakeControlPoints(self):
|
||||
RectangleShape.MakeControlPoints(self)
|
||||
self.MakeMandatoryControlPoints()
|
||||
|
||||
def MakeMandatoryControlPoints(self):
|
||||
currentY = self.GetY()-self._height / 2
|
||||
maxY = self.GetY() + self._height / 2
|
||||
|
||||
for i, region in enumerate(self.GetRegions()):
|
||||
proportion = region._regionProportionY
|
||||
|
||||
y = currentY + self._height * proportion
|
||||
actualY = min(maxY, y)
|
||||
|
||||
if region != self.GetRegions()[-1]:
|
||||
controlPoint = DividedShapeControlPoint(self._canvas, self, i, CONTROL_POINT_SIZE, 0, actualY-self.GetY(), 0)
|
||||
self._canvas.AddShape(controlPoint)
|
||||
self._controlPoints.append(controlPoint)
|
||||
|
||||
currentY = actualY
|
||||
|
||||
def ResetControlPoints(self):
|
||||
# May only have the region handles, (n - 1) of them
|
||||
if len(self._controlPoints)>len(self.GetRegions())-1:
|
||||
RectangleShape.ResetControlPoints(self)
|
||||
|
||||
self.ResetMandatoryControlPoints()
|
||||
|
||||
def ResetMandatoryControlPoints(self):
|
||||
currentY = self.GetY()-self._height / 2
|
||||
maxY = self.GetY() + self._height / 2
|
||||
|
||||
i = 0
|
||||
for controlPoint in self._controlPoints:
|
||||
if isinstance(controlPoint, DividedShapeControlPoint):
|
||||
region = self.GetRegions()[i]
|
||||
proportion = region._regionProportionY
|
||||
|
||||
y = currentY + self._height * proportion
|
||||
actualY = min(maxY, y)
|
||||
|
||||
controlPoint._xoffset = 0
|
||||
controlPoint._yoffset = actualY-self.GetY()
|
||||
|
||||
currentY = actualY
|
||||
|
||||
i += 1
|
||||
|
||||
def EditRegions(self):
|
||||
"""Edit the region colours and styles. Not implemented."""
|
||||
print "EditRegions() is unimplemented"
|
||||
|
||||
def OnRightClick(self, x, y, keys = 0, attachment = 0):
|
||||
if keys & KEY_CTRL:
|
||||
self.EditRegions()
|
||||
else:
|
||||
RectangleShape.OnRightClick(self, x, y, keys, attachment)
|
1533
wxPython/wx/lib/ogl/lines.py
Normal file
1533
wxPython/wx/lib/ogl/lines.py
Normal file
File diff suppressed because it is too large
Load Diff
416
wxPython/wx/lib/ogl/oglmisc.py
Normal file
416
wxPython/wx/lib/ogl/oglmisc.py
Normal file
@@ -0,0 +1,416 @@
|
||||
# -*- coding: iso-8859-1 -*-
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: oglmisc.py
|
||||
# Purpose: Miscellaneous OGL support functions
|
||||
#
|
||||
# Author: Pierre Hj<48>lm (from C++ original by Julian Smart)
|
||||
#
|
||||
# Created: 20040508
|
||||
# RCS-ID:
|
||||
# Copyright: (c) 2004 Pierre Hj<48>lm - 1998 Julian Smart
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
from __future__ import division
|
||||
from math import *
|
||||
|
||||
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 = 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 = 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 - sqrt((b1 * b1 - (((x2 - x1) * (x2 - x1)) * (b1 * b1) / (a1 * a1))))
|
||||
else:
|
||||
y4 = y1 + 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) / (x32 - 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) + sqrt(K)) / (2 * E)
|
||||
ellipse1_y = ((H * (ellipse1_x - x2)) + y2)
|
||||
else:
|
||||
ellipse1_x = (((F*-1) - 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 = 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
|
Reference in New Issue
Block a user