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:
parent
6033bbc1ff
commit
f847103a32
@ -12,7 +12,7 @@
|
||||
|
||||
%define DOCSTRING
|
||||
"The Object Graphics Library provides for simple drawing and manipulation
|
||||
of 2D objects."
|
||||
of 2D objects. (This version is deprecated, please use wx.lib.ogl instead.)"
|
||||
%enddef
|
||||
%module(docstring=DOCSTRING) ogl
|
||||
|
||||
@ -29,6 +29,12 @@ of 2D objects."
|
||||
%pythoncode { wx = _core }
|
||||
%pythoncode { __docfilter__ = wx.__DocFilter(globals()) }
|
||||
|
||||
%pythoncode {
|
||||
import warnings
|
||||
warnings.warn("This module is deprecated. Please use the wx.lib.ogl pacakge instead.",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
}
|
||||
|
||||
|
||||
MAKE_CONST_WXSTRING_NOSWIG(EmptyString);
|
||||
|
||||
|
@ -30,7 +30,7 @@ import images
|
||||
|
||||
_treeList = [
|
||||
# new stuff
|
||||
('Recent Additions', [
|
||||
('Recent Additions and Updates', [
|
||||
'VListBox',
|
||||
'Listbook',
|
||||
'MaskedNumCtrl',
|
||||
|
@ -1,15 +1,19 @@
|
||||
# -*- coding: iso-8859-1 -*-
|
||||
# 11/20/2003 - Jeff Grimmett (grimmtooth@softhome.net)
|
||||
#
|
||||
# o Updated for wx namespace
|
||||
#
|
||||
# 20040508 - Pierre Hjälm
|
||||
#
|
||||
# o Changed to use the python version of OGL
|
||||
# o Added TextShape, CompositeShape and CompositeShape with divisions
|
||||
#
|
||||
|
||||
import wx
|
||||
import wx.ogl as ogl
|
||||
import wx
|
||||
import wx.lib.ogl as ogl
|
||||
|
||||
import images
|
||||
|
||||
##wx.Trap()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class DiamondShape(ogl.PolygonShape):
|
||||
@ -20,13 +24,6 @@ class DiamondShape(ogl.PolygonShape):
|
||||
if h == 0.0:
|
||||
h = 60.0
|
||||
|
||||
# Either ogl.RealPoints or 2-tuples of floats works.
|
||||
|
||||
#points = [ ogl.RealPoint(0.0, -h/2.0),
|
||||
# ogl.RealPoint(w/2.0, 0.0),
|
||||
# ogl.RealPoint(0.0, h/2.0),
|
||||
# ogl.RealPoint(-w/2.0, 0.0),
|
||||
# ]
|
||||
points = [ (0.0, -h/2.0),
|
||||
(w/2.0, 0.0),
|
||||
(0.0, h/2.0),
|
||||
@ -44,6 +41,67 @@ class RoundedRectangleShape(ogl.RectangleShape):
|
||||
self.SetCornerRadius(-0.3)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class CompositeDivisionShape(ogl.CompositeShape):
|
||||
def __init__(self, canvas):
|
||||
ogl.CompositeShape.__init__(self)
|
||||
|
||||
self.SetCanvas(canvas)
|
||||
|
||||
# create a division in the composite
|
||||
self.MakeContainer()
|
||||
|
||||
# add a shape to the original division
|
||||
shape2 = ogl.RectangleShape(40, 60)
|
||||
self.GetDivisions()[0].AddChild(shape2)
|
||||
|
||||
# now divide the division so we get 2
|
||||
self.GetDivisions()[0].Divide(wx.HORIZONTAL)
|
||||
|
||||
# and add a shape to the second division (and move it to the
|
||||
# centre of the division)
|
||||
shape3 = ogl.CircleShape(40)
|
||||
shape3.SetBrush(wx.CYAN_BRUSH)
|
||||
self.GetDivisions()[1].AddChild(shape3)
|
||||
shape3.SetX(self.GetDivisions()[1].GetX())
|
||||
|
||||
for division in self.GetDivisions():
|
||||
division.SetSensitivityFilter(0)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class CompositeShape(ogl.CompositeShape):
|
||||
def __init__(self, canvas):
|
||||
ogl.CompositeShape.__init__(self)
|
||||
|
||||
self.SetCanvas(canvas)
|
||||
|
||||
constraining_shape = ogl.RectangleShape(120, 100)
|
||||
constrained_shape1 = ogl.CircleShape(50)
|
||||
constrained_shape2 = ogl.RectangleShape(80, 20)
|
||||
|
||||
constraining_shape.SetBrush(wx.BLUE_BRUSH)
|
||||
constrained_shape2.SetBrush(wx.RED_BRUSH)
|
||||
|
||||
self.AddChild(constraining_shape)
|
||||
self.AddChild(constrained_shape1)
|
||||
self.AddChild(constrained_shape2)
|
||||
|
||||
constraint = ogl.Constraint(ogl.CONSTRAINT_MIDALIGNED_BOTTOM, constraining_shape, [constrained_shape1, constrained_shape2])
|
||||
self.AddConstraint(constraint)
|
||||
self.Recompute()
|
||||
|
||||
# If we don't do this, the shapes will be able to move on their
|
||||
# own, instead of moving the composite
|
||||
constraining_shape.SetDraggable(False)
|
||||
constrained_shape1.SetDraggable(False)
|
||||
constrained_shape2.SetDraggable(False)
|
||||
|
||||
# If we don't do this the shape will take all left-clicks for itself
|
||||
constraining_shape.SetSensitivityFilter(0)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class DividedShape(ogl.DividedShape):
|
||||
@ -88,7 +146,7 @@ class DividedShape(ogl.DividedShape):
|
||||
|
||||
def OnSizingEndDragLeft(self, pt, x, y, keys, attch):
|
||||
print "***", self
|
||||
self.base_OnSizingEndDragLeft(pt, x, y, keys, attch)
|
||||
ogl.DividedShape.OnSizingEndDragLeft(self, pt, x, y, keys, attch)
|
||||
self.SetRegionSizes()
|
||||
self.ReformatRegions()
|
||||
self.GetCanvas().Refresh()
|
||||
@ -103,15 +161,14 @@ class MyEvtHandler(ogl.ShapeEvtHandler):
|
||||
self.statbarFrame = frame
|
||||
|
||||
def UpdateStatusBar(self, shape):
|
||||
x,y = shape.GetX(), shape.GetY()
|
||||
x, y = shape.GetX(), shape.GetY()
|
||||
width, height = shape.GetBoundingBoxMax()
|
||||
self.statbarFrame.SetStatusText("Pos: (%d,%d) Size: (%d, %d)" %
|
||||
self.statbarFrame.SetStatusText("Pos: (%d, %d) Size: (%d, %d)" %
|
||||
(x, y, width, height))
|
||||
|
||||
|
||||
def OnLeftClick(self, x, y, keys = 0, attachment = 0):
|
||||
def OnLeftClick(self, x, y, keys=0, attachment=0):
|
||||
shape = self.GetShape()
|
||||
print shape.__class__, shape.GetClassName()
|
||||
canvas = shape.GetCanvas()
|
||||
dc = wx.ClientDC(canvas)
|
||||
canvas.PrepareDC(dc)
|
||||
@ -142,9 +199,9 @@ class MyEvtHandler(ogl.ShapeEvtHandler):
|
||||
self.UpdateStatusBar(shape)
|
||||
|
||||
|
||||
def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
|
||||
def OnEndDragLeft(self, x, y, keys=0, attachment=0):
|
||||
shape = self.GetShape()
|
||||
self.base_OnEndDragLeft(x, y, keys, attachment)
|
||||
ogl.ShapeEvtHandler.OnEndDragLeft(self, x, y, keys, attachment)
|
||||
|
||||
if not shape.Selected():
|
||||
self.OnLeftClick(x, y, keys, attachment)
|
||||
@ -153,12 +210,12 @@ class MyEvtHandler(ogl.ShapeEvtHandler):
|
||||
|
||||
|
||||
def OnSizingEndDragLeft(self, pt, x, y, keys, attch):
|
||||
self.base_OnSizingEndDragLeft(pt, x, y, keys, attch)
|
||||
ogl.ShapeEvtHandler.OnSizingEndDragLeft(self, pt, x, y, keys, attch)
|
||||
self.UpdateStatusBar(self.GetShape())
|
||||
|
||||
|
||||
def OnMovePost(self, dc, x, y, oldX, oldY, display):
|
||||
self.base_OnMovePost(dc, x, y, oldX, oldY, display)
|
||||
ogl.ShapeEvtHandler.OnMovePost(self, dc, x, y, oldX, oldY, display)
|
||||
self.UpdateStatusBar(self.GetShape())
|
||||
|
||||
|
||||
@ -188,29 +245,44 @@ class TestWindow(ogl.ShapeCanvas):
|
||||
rRectBrush = wx.Brush("MEDIUM TURQUOISE", wx.SOLID)
|
||||
dsBrush = wx.Brush("WHEAT", wx.SOLID)
|
||||
|
||||
self.MyAddShape(
|
||||
CompositeDivisionShape(self),
|
||||
310, 310, wx.BLACK_PEN, wx.BLUE_BRUSH, "Division"
|
||||
)
|
||||
|
||||
self.MyAddShape(
|
||||
CompositeShape(self),
|
||||
100, 260, wx.BLACK_PEN, wx.RED_BRUSH, "Composite"
|
||||
)
|
||||
|
||||
self.MyAddShape(
|
||||
ogl.CircleShape(80),
|
||||
100, 100, wx.Pen(wx.BLUE, 3), wx.GREEN_BRUSH, "Circle"
|
||||
)
|
||||
|
||||
self.MyAddShape(
|
||||
ogl.TextShape(45, 30),
|
||||
205, 60, wx.GREEN_PEN, wx.LIGHT_GREY_BRUSH, "Text"
|
||||
)
|
||||
|
||||
self.MyAddShape(
|
||||
ogl.RectangleShape(85, 50),
|
||||
305, 60, wx.BLACK_PEN, wx.LIGHT_GREY_BRUSH, "Rectangle"
|
||||
)
|
||||
|
||||
ds = self.MyAddShape(
|
||||
DividedShape(140, 150, self),
|
||||
495, 145, wx.BLACK_PEN, dsBrush, ''
|
||||
)
|
||||
DividedShape(140, 150, self),
|
||||
515, 145, wx.BLACK_PEN, dsBrush, ''
|
||||
)
|
||||
|
||||
self.MyAddShape(
|
||||
DiamondShape(90, 90),
|
||||
345, 235, wx.Pen(wx.BLUE, 3, wx.DOT), wx.RED_BRUSH, "Polygon"
|
||||
445, 305, wx.Pen(wx.BLUE, 3, wx.DOT), wx.RED_BRUSH, "Polygon"
|
||||
)
|
||||
|
||||
self.MyAddShape(
|
||||
RoundedRectangleShape(95,70),
|
||||
140, 255, wx.Pen(wx.RED, 2), rRectBrush, "Rounded Rect"
|
||||
RoundedRectangleShape(95, 70),
|
||||
345, 145, wx.Pen(wx.RED, 2), rRectBrush, "Rounded Rect"
|
||||
)
|
||||
|
||||
bmp = images.getTest2Bitmap()
|
||||
@ -219,7 +291,7 @@ class TestWindow(ogl.ShapeCanvas):
|
||||
|
||||
s = ogl.BitmapShape()
|
||||
s.SetBitmap(bmp)
|
||||
self.MyAddShape(s, 225, 150, None, None, "Bitmap")
|
||||
self.MyAddShape(s, 225, 130, None, None, "Bitmap")
|
||||
|
||||
dc = wx.ClientDC(self)
|
||||
self.PrepareDC(dc)
|
||||
@ -241,14 +313,15 @@ class TestWindow(ogl.ShapeCanvas):
|
||||
self.diagram.AddShape(line)
|
||||
line.Show(True)
|
||||
|
||||
# for some reason, the shapes have to be moved for the line to show up...
|
||||
fromShape.Move(dc, fromShape.GetX(), fromShape.GetY())
|
||||
|
||||
self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
|
||||
|
||||
|
||||
def MyAddShape(self, shape, x, y, pen, brush, text):
|
||||
shape.SetDraggable(True, True)
|
||||
# Composites have to be moved for all children to get in place
|
||||
if isinstance(shape, ogl.CompositeShape):
|
||||
dc = wx.ClientDC(self)
|
||||
self.PrepareDC(dc)
|
||||
shape.Move(dc, x, y)
|
||||
else:
|
||||
shape.SetDraggable(True, True)
|
||||
shape.SetCanvas(self)
|
||||
shape.SetX(x)
|
||||
shape.SetY(y)
|
||||
@ -268,16 +341,6 @@ class TestWindow(ogl.ShapeCanvas):
|
||||
return shape
|
||||
|
||||
|
||||
def OnDestroy(self, evt):
|
||||
# Do some cleanup
|
||||
for shape in self.diagram.GetShapeList():
|
||||
if shape.GetParent() == None:
|
||||
shape.SetCanvas(None)
|
||||
shape.Destroy()
|
||||
|
||||
self.diagram.Destroy()
|
||||
|
||||
|
||||
def OnBeginDragLeft(self, x, y, keys):
|
||||
self.log.write("OnBeginDragLeft: %s, %s, %s\n" % (x, y, keys))
|
||||
|
||||
@ -298,25 +361,32 @@ def runTest(frame, nb, log):
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
# The OGL library holds some resources that need to be freed before
|
||||
# the app shuts down.
|
||||
class __Cleanup:
|
||||
def __del__(self, cleanup=ogl.OGLCleanUp):
|
||||
cleanup()
|
||||
|
||||
# When this module gets cleaned up by Python then __cu will be cleaned
|
||||
# up and it's __dell__ is called, which will then call ogl.OGLCleanUp.
|
||||
__cu = __Cleanup()
|
||||
overview = """<html><body>
|
||||
<h2>Object Graphics Library</h2>
|
||||
|
||||
|
||||
overview = """\
|
||||
The Object Graphics Library is a library supporting the creation and
|
||||
manipulation of simple and complex graphic images on a canvas.
|
||||
|
||||
<p>The OGL library was originally written in C++ and provided to
|
||||
wxPython via an extension module wrapper as is most of the rest of
|
||||
wxPython. The code has now been ported to Python (with many thanks to
|
||||
Pierre Hjälm!) in order to make it be more easily maintainable and
|
||||
less likely to get rusty because nobody cares about the C++ lib any
|
||||
more.
|
||||
|
||||
<p>The Python version should be mostly drop-in compatible with the
|
||||
wrapped C++ version, except for the location of the package
|
||||
(wx.lib.ogl instead of wx.ogl) and that the base class methods are
|
||||
called the normal Python way (superclass.Method(self, ...)) instead of the
|
||||
hacky way that had to be done to support overloaded methods with the
|
||||
old SWIG (self.base_Method(...))
|
||||
|
||||
|
||||
"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys,os
|
||||
import sys, os
|
||||
import run
|
||||
run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])
|
||||
|
||||
|
@ -71,6 +71,7 @@ wxPython/wx/lib/colourchooser
|
||||
wxPython/wx/lib/editor
|
||||
wxPython/wx/lib/masked
|
||||
wxPython/wx/lib/mixins
|
||||
wxPython/wx/lib/ogl
|
||||
wxPython/wx/py
|
||||
wxPython/wx/py/tests
|
||||
wxPython/wxPython
|
||||
|
@ -106,6 +106,8 @@ Source: "wx\lib\colourchooser\*.py"; DestDir: "{app}\wx\lib\colourchoo
|
||||
Source: "wx\lib\editor\*.py"; DestDir: "{app}\wx\lib\editor"; Components: core
|
||||
Source: "wx\lib\editor\*.txt"; DestDir: "{app}\wx\lib\editor"; Components: core
|
||||
Source: "wx\lib\mixins\*.py"; DestDir: "{app}\wx\lib\mixins"; Components: core
|
||||
Source: "wx\lib\masked\*.py"; DestDir: "{app}\wx\lib\masked"; Components: core
|
||||
Source: "wx\lib\ogl\*.py"; DestDir: "{app}\wx\lib\ogl"; Components: core
|
||||
Source: "wx\py\*.py"; DestDir: "{app}\wx\py"; Components: core
|
||||
Source: "wx\py\*.txt"; DestDir: "{app}\wx\py"; Components: core
|
||||
Source: "wx\py\*.ico"; DestDir: "{app}\wx\py"; Components: core
|
||||
|
@ -80,6 +80,17 @@ remaining compatible with "C".
|
||||
Switched gizmos.TreeListCtrl to the newer version of the code from the
|
||||
wxCode project.
|
||||
|
||||
OGL is dead! LONG LIVE OGL! (Oops, sorry. A bit of my dramatic side
|
||||
leaked out there...) The wx.ogl module has been deprecated in favor
|
||||
of the new Python port of the OGL library located at wx.lib.ogl
|
||||
contributed by Pierre Hjälm. This will hopefully greatly extend the
|
||||
life of OGL within wxPython by making it more easily maintainable and
|
||||
less prone to getting rusty as there seems to be less and less
|
||||
interest in maintaining the C++ version. At this point there are just
|
||||
a couple minor known compatibility differences, please see the
|
||||
MigrationGuide_ file for details.
|
||||
|
||||
.. _MigrationGuide: MigrationGuide.html
|
||||
|
||||
|
||||
|
||||
|
@ -3,10 +3,10 @@ wxPython 2.5 Migration Guide
|
||||
============================
|
||||
|
||||
This document will help explain some of the major changes in wxPython
|
||||
2.5 and let you know what you need to do to adapt your programs to
|
||||
those changes. Be sure to also check in the CHANGES_ file like
|
||||
usual to see info about the not so major changes and other things that
|
||||
have been added to wxPython.
|
||||
2.5 since the 2.4 series and let you know what you need to do to adapt
|
||||
your programs to those changes. Be sure to also check in the CHANGES_
|
||||
file like usual to see info about the not so major changes and other
|
||||
things that have been added to wxPython.
|
||||
|
||||
.. _CHANGES: CHANGES.html
|
||||
|
||||
@ -20,8 +20,8 @@ The **wxWindows** project and library is now known as
|
||||
.. _here: http://www.wxwidgets.org/name.htm
|
||||
|
||||
This won't really affect wxPython all that much, other than the fact
|
||||
that the wxwindows.org domain name will be changing to wxwidgets.org,
|
||||
so mail list, CVS, and etc. addresses will be changing. We're going
|
||||
that the wxwindows.org domain name has changed to wxwidgets.org,
|
||||
so mail list, CVS, and etc. addresses have also changed. We're going
|
||||
to try and smooth the transition as much as possible, but I wanted you
|
||||
all to be aware of this change if you run into any issues.
|
||||
|
||||
@ -585,6 +585,34 @@ provided by the makers of the ActiveX control that you are using.
|
||||
|
||||
|
||||
|
||||
OGL is dead! LONG LIVE OGL!
|
||||
---------------------------
|
||||
|
||||
The wx.ogl module has been deprecated in favor of the new Python port
|
||||
of the OGL library located at wx.lib.ogl contributed by Pierre Hjälm.
|
||||
This will hopefully greatly extend the life of OGL within wxPython by
|
||||
making it more easily maintainable and less prone to getting rusty as
|
||||
there seems to be less and less interest in maintaining the C++
|
||||
version.
|
||||
|
||||
There are only a few known compatibility issues at this time. First
|
||||
is the location of OGL. The deprecated version is located in the
|
||||
wx.ogl module, and the new version is in the wx.lib.ogl package. So
|
||||
this just means that to start using the new version you need to adjust
|
||||
your imports. So if your code currently has something like this::
|
||||
|
||||
import wx
|
||||
import wx.ogl as ogl
|
||||
|
||||
Then just change it to this::
|
||||
|
||||
import wx
|
||||
import wx.lib.ogl as ogl
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Obsolete Modules
|
||||
----------------
|
||||
|
||||
|
@ -707,6 +707,7 @@ if __name__ == "__main__":
|
||||
'wx.lib.editor',
|
||||
'wx.lib.masked',
|
||||
'wx.lib.mixins',
|
||||
'wx.lib.ogl',
|
||||
'wx.py',
|
||||
'wx.tools',
|
||||
'wx.tools.XRCed',
|
||||
|
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älm (from C++ original by Julian Smart)
|
||||
#
|
||||
# Created: 20040508
|
||||
# RCS-ID:
|
||||
# Copyright: (c) 2004 Pierre Hjä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älm (from C++ original by Julian Smart)
|
||||
#
|
||||
# Created: 20040508
|
||||
# RCS-ID:
|
||||
# Copyright: (c) 2004 Pierre Hjä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älm (from C++ original by Julian Smart)
|
||||
#
|
||||
# Created: 20040508
|
||||
# RCS-ID:
|
||||
# Copyright: (c) 2004 Pierre Hjä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älm (from C++ original by Julian Smart)
|
||||
#
|
||||
# Created: 20040508
|
||||
# RCS-ID:
|
||||
# Copyright: (c) 2004 Pierre Hjä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älm (from C++ original by Julian Smart)
|
||||
#
|
||||
# Created: 20040508
|
||||
# RCS-ID:
|
||||
# Copyright: (c) 2004 Pierre Hjä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
|
Loading…
Reference in New Issue
Block a user