+++ /dev/null
-# -*- coding: iso-8859-1 -*-
-#----------------------------------------------------------------------------
-# Name: lines.py
-# Purpose: LineShape class
-#
-# Author: Pierre Hjälm (from C++ original by Julian Smart)
-#
-# Created: 2004-05-08
-# RCS-ID: $Id$
-# Copyright: (c) 2004 Pierre Hjälm - 1998 Julian Smart
-# Licence: wxWindows license
-#----------------------------------------------------------------------------
-
-import sys
-import math
-
-from _basic import Shape, ShapeRegion, ShapeTextLine, ControlPoint, RectangleShape
-from _oglmisc import *
-
-# Line alignment flags
-# Vertical by default
-LINE_ALIGNMENT_HORIZ = 1
-LINE_ALIGNMENT_VERT = 0
-LINE_ALIGNMENT_TO_NEXT_HANDLE = 2
-LINE_ALIGNMENT_NONE = 0
-
-
-
-class LineControlPoint(ControlPoint):
- def __init__(self, theCanvas = None, object = None, size = 0.0, x = 0.0, y = 0.0, the_type = 0):
- ControlPoint.__init__(self, theCanvas, object, size, x, y, the_type)
- self._xpos = x
- self._ypos = y
- self._type = the_type
- self._point = None
- self._originalPos = None
-
- def OnDraw(self, dc):
- RectangleShape.OnDraw(self, dc)
-
- # Implement movement of Line point
- def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
- self._shape.GetEventHandler().OnSizingDragLeft(self, draw, x, y, keys, attachment)
-
- def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
- self._shape.GetEventHandler().OnSizingBeginDragLeft(self, x, y, keys, attachment)
-
- def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
- self._shape.GetEventHandler().OnSizingEndDragLeft(self, x, y, keys, attachment)
-
-
-
-class ArrowHead(object):
- def __init__(self, type = 0, end = 0, size = 0.0, dist = 0.0, name = "", mf = None, arrowId = -1):
- if isinstance(type, ArrowHead):
- pass
- else:
- self._arrowType = type
- self._arrowEnd = end
- self._arrowSize = size
- self._xOffset = dist
- self._yOffset = 0.0
- self._spacing = 5.0
-
- self._arrowName = name
- self._metaFile = mf
- self._id = arrowId
- if self._id == -1:
- self._id = wx.NewId()
-
- def _GetType(self):
- return self._arrowType
-
- def GetPosition(self):
- return self._arrowEnd
-
- def SetPosition(self, pos):
- self._arrowEnd = pos
-
- def GetXOffset(self):
- return self._xOffset
-
- def GetYOffset(self):
- return self._yOffset
-
- def GetSpacing(self):
- return self._spacing
-
- def GetSize(self):
- return self._arrowSize
-
- def SetSize(self, size):
- self._arrowSize = size
- if self._arrowType == ARROW_METAFILE and self._metaFile:
- oldWidth = self._metaFile._width
- if oldWidth == 0:
- return
-
- scale = float(size) / oldWidth
- if scale != 1:
- self._metaFile.Scale(scale, scale)
-
- def GetName(self):
- return self._arrowName
-
- def SetXOffset(self, x):
- self._xOffset = x
-
- def SetYOffset(self, y):
- self._yOffset = y
-
- def GetMetaFile(self):
- return self._metaFile
-
- def GetId(self):
- return self._id
-
- def GetArrowEnd(self):
- return self._arrowEnd
-
- def GetArrowSize(self):
- return self._arrowSize
-
- def SetSpacing(self, sp):
- self._spacing = sp
-
-
-
-class LabelShape(RectangleShape):
- def __init__(self, parent, region, w, h):
- RectangleShape.__init__(self, w, h)
- self._lineShape = parent
- self._shapeRegion = region
- self.SetPen(wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT))
-
- def OnDraw(self, dc):
- if self._lineShape and not self._lineShape.GetDrawHandles():
- return
-
- x1 = self._xpos - self._width / 2.0
- y1 = self._ypos - self._height / 2.0
-
- if self._pen:
- if self._pen.GetWidth() == 0:
- dc.SetPen(wx.Pen(wx.WHITE, 1, wx.TRANSPARENT))
- else:
- dc.SetPen(self._pen)
- dc.SetBrush(wx.TRANSPARENT_BRUSH)
-
- if self._cornerRadius > 0:
- dc.DrawRoundedRectangle(x1, y1, self._width, self._height, self._cornerRadius)
- else:
- dc.DrawRectangle(x1, y1, self._width, self._height)
-
- def OnDrawContents(self, dc):
- pass
-
- def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
- RectangleShape.OnDragLeft(self, draw, x, y, keys, attachment)
-
- def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
- RectangleShape.OnBeginDragLeft(self, x, y, keys, attachment)
-
- def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
- RectangleShape.OnEndDragLeft(self, x, y, keys, attachment)
-
- def OnMovePre(self, dc, x, y, old_x, old_y, display):
- return self._lineShape.OnLabelMovePre(dc, self, x, y, old_x, old_y, display)
-
- # Divert left and right clicks to line object
- def OnLeftClick(self, x, y, keys = 0, attachment = 0):
- self._lineShape.GetEventHandler().OnLeftClick(x, y, keys, attachment)
-
- def OnRightClick(self, x, y, keys = 0, attachment = 0):
- self._lineShape.GetEventHandler().OnRightClick(x, y, keys, attachment)
-
-
-
-class LineShape(Shape):
- """LineShape may be attached to two nodes;
- it may be segmented, in which case a control point is drawn for each joint.
-
- A wxLineShape may have arrows at the beginning, end and centre.
-
- Derived from:
- Shape
- """
- def __init__(self):
- Shape.__init__(self)
-
- self._sensitivity = OP_CLICK_LEFT | OP_CLICK_RIGHT
- self._draggable = False
- self._attachmentTo = 0
- self._attachmentFrom = 0
- self._from = None
- self._to = None
- self._erasing = False
- self._arrowSpacing = 5.0
- self._ignoreArrowOffsets = False
- self._isSpline = False
- self._maintainStraightLines = False
- self._alignmentStart = 0
- self._alignmentEnd = 0
-
- self._lineControlPoints = None
-
- # Clear any existing regions (created in an earlier constructor)
- # and make the three line regions.
- self.ClearRegions()
- for name in ["Middle","Start","End"]:
- newRegion = ShapeRegion()
- newRegion.SetName(name)
- newRegion.SetSize(150, 50)
- self._regions.append(newRegion)
-
- self._labelObjects = [None, None, None]
- self._lineOrientations = []
- self._lineControlPoints = []
- self._arcArrows = []
-
- def GetFrom(self):
- """Return the 'from' object."""
- return self._from
-
- def GetTo(self):
- """Return the 'to' object."""
- return self._to
-
- def GetAttachmentFrom(self):
- """Return the attachment point on the 'from' node."""
- return self._attachmentFrom
-
- def GetAttachmentTo(self):
- """Return the attachment point on the 'to' node."""
- return self._attachmentTo
-
- def GetLineControlPoints(self):
- return self._lineControlPoints
-
- def SetSpline(self, spline):
- """Specifies whether a spline is to be drawn through the control points."""
- self._isSpline = spline
-
- def IsSpline(self):
- """TRUE if a spline is drawn through the control points."""
- return self._isSpline
-
- def SetAttachmentFrom(self, attach):
- """Set the 'from' shape attachment."""
- self._attachmentFrom = attach
-
- def SetAttachmentTo(self, attach):
- """Set the 'to' shape attachment."""
- self._attachmentTo = attach
-
- # This is really to distinguish between lines and other images.
- # For lines, want to pass drag to canvas, since lines tend to prevent
- # dragging on a canvas (they get in the way.)
- def Draggable(self):
- return False
-
- def SetIgnoreOffsets(self, ignore):
- """Set whether to ignore offsets from the end of the line when drawing."""
- self._ignoreArrowOffsets = ignore
-
- def GetArrows(self):
- return self._arcArrows
-
- def GetAlignmentStart(self):
- return self._alignmentStart
-
- def GetAlignmentEnd(self):
- return self._alignmentEnd
-
- def IsEnd(self, nodeObject):
- """TRUE if shape is at the end of the line."""
- return self._to == nodeObject
-
- def MakeLineControlPoints(self, n):
- """Make a given number of control points (minimum of two)."""
- self._lineControlPoints = []
-
- for _ in range(n):
- point = wx.RealPoint(-999, -999)
- self._lineControlPoints.append(point)
-
- # pi: added _initialised to keep track of when we have set
- # the middle points to something other than (-999, -999)
- self._initialised = False
-
- def InsertLineControlPoint(self, dc = None, point = None):
- """Insert a control point at an optional given position."""
- if dc:
- self.Erase(dc)
-
- if point:
- line_x, line_y = point
- else:
- last_point = self._lineControlPoints[-1]
- second_last_point = self._lineControlPoints[-2]
-
- line_x = (last_point[0] + second_last_point[0]) / 2.0
- line_y = (last_point[1] + second_last_point[1]) / 2.0
-
- point = wx.RealPoint(line_x, line_y)
- self._lineControlPoints.insert(len(self._lineControlPoints)-1, point)
-
- def DeleteLineControlPoint(self):
- """Delete an arbitary point on the line."""
- if len(self._lineControlPoints) < 3:
- return False
-
- del self._lineControlPoints[-2]
- return True
-
- def Initialise(self):
- """Initialise the line object."""
- if self._lineControlPoints:
- # Just move the first and last control points
- first_point = self._lineControlPoints[0]
- last_point = self._lineControlPoints[-1]
-
- # If any of the line points are at -999, we must
- # initialize them by placing them half way between the first
- # and the last.
-
- for i in range(1,len(self._lineControlPoints)):
- point = self._lineControlPoints[i]
- if point[0] == -999:
- if first_point[0] < last_point[0]:
- x1 = first_point[0]
- x2 = last_point[0]
- else:
- x2 = first_point[0]
- x1 = last_point[0]
- if first_point[1] < last_point[1]:
- y1 = first_point[1]
- y2 = last_point[1]
- else:
- y2 = first_point[1]
- y1 = last_point[1]
- self._lineControlPoints[i] = wx.RealPoint((x2 - x1) / 2.0 + x1, (y2 - y1) / 2.0 + y1)
- self._initialised = True
-
- def FormatText(self, dc, s, i):
- """Format a text string according to the region size, adding
- strings with positions to region text list.
- """
- self.ClearText(i)
-
- if len(self._regions) == 0 or i >= len(self._regions):
- return
-
- region = self._regions[i]
- region.SetText(s)
- dc.SetFont(region.GetFont())
-
- w, h = region.GetSize()
- # Initialize the size if zero
- if (w == 0 or h == 0) and s:
- w, h = 100, 50
- region.SetSize(w, h)
-
- string_list = FormatText(dc, s, w - 5, h - 5, region.GetFormatMode())
- for s in string_list:
- line = ShapeTextLine(0.0, 0.0, s)
- region.GetFormattedText().append(line)
-
- actualW = w
- actualH = h
- if region.GetFormatMode() & FORMAT_SIZE_TO_CONTENTS:
- actualW, actualH = GetCentredTextExtent(dc, region.GetFormattedText(), self._xpos, self._ypos, w, h)
- if actualW != w or actualH != h:
- xx, yy = self.GetLabelPosition(i)
- self.EraseRegion(dc, region, xx, yy)
- if len(self._labelObjects) < i:
- self._labelObjects[i].Select(False, dc)
- self._labelObjects[i].Erase(dc)
- self._labelObjects[i].SetSize(actualW, actualH)
-
- region.SetSize(actualW, actualH)
-
- if len(self._labelObjects) < i:
- self._labelObjects[i].Select(True, dc)
- self._labelObjects[i].Draw(dc)
-
- CentreText(dc, region.GetFormattedText(), self._xpos, self._ypos, actualW, actualH, region.GetFormatMode())
- self._formatted = True
-
- def DrawRegion(self, dc, region, x, y):
- """Format one region at this position."""
- if self.GetDisableLabel():
- return
-
- w, h = region.GetSize()
-
- # Get offset from x, y
- xx, yy = region.GetPosition()
-
- xp = xx + x
- yp = yy + y
-
- # First, clear a rectangle for the text IF there is any
- if len(region.GetFormattedText()):
- dc.SetPen(self.GetBackgroundPen())
- dc.SetBrush(self.GetBackgroundBrush())
-
- # Now draw the text
- if region.GetFont():
- dc.SetFont(region.GetFont())
- dc.DrawRectangle(xp - w / 2.0, yp - h / 2.0, w, h)
-
- if self._pen:
- dc.SetPen(self._pen)
- dc.SetTextForeground(region.GetActualColourObject())
-
- DrawFormattedText(dc, region.GetFormattedText(), xp, yp, w, h, region.GetFormatMode())
-
- def EraseRegion(self, dc, region, x, y):
- """Erase one region at this position."""
- if self.GetDisableLabel():
- return
-
- w, h = region.GetSize()
-
- # Get offset from x, y
- xx, yy = region.GetPosition()
-
- xp = xx + x
- yp = yy + y
-
- if region.GetFormattedText():
- dc.SetPen(self.GetBackgroundPen())
- dc.SetBrush(self.GetBackgroundBrush())
-
- dc.DrawRectangle(xp - w / 2.0, yp - h / 2.0, w, h)
-
- def GetLabelPosition(self, position):
- """Get the reference point for a label.
-
- Region x and y are offsets from this.
- position is 0 (middle), 1 (start), 2 (end).
- """
- if position == 0:
- # Want to take the middle section for the label
- half_way = int(len(self._lineControlPoints) / 2.0)
-
- # Find middle of this line
- point = self._lineControlPoints[half_way - 1]
- next_point = self._lineControlPoints[half_way]
-
- dx = next_point[0] - point[0]
- dy = next_point[1] - point[1]
-
- return point[0] + dx / 2.0, point[1] + dy / 2.0
- elif position == 1:
- return self._lineControlPoints[0][0], self._lineControlPoints[0][1]
- elif position == 2:
- return self._lineControlPoints[-1][0], self._lineControlPoints[-1][1]
-
- def Straighten(self, dc = None):
- """Straighten verticals and horizontals."""
- if len(self._lineControlPoints) < 3:
- return
-
- if dc:
- self.Erase(dc)
-
- GraphicsStraightenLine(self._lineControlPoints[-1], self._lineControlPoints[-2])
-
- for i in range(len(self._lineControlPoints) - 2):
- GraphicsStraightenLine(self._lineControlPoints[i], self._lineControlPoints[i + 1])
-
- if dc:
- self.Draw(dc)
-
- def Unlink(self):
- """Unlink the line from the nodes at either end."""
- if self._to:
- self._to.GetLines().remove(self)
- if self._from:
- self._from.GetLines().remove(self)
- self._to = None
- self._from = None
- for i in range(3):
- if self._labelObjects[i]:
- self._labelObjects[i].Select(False)
- self._labelObjects[i].RemoveFromCanvas(self._canvas)
- self.ClearArrowsAtPosition(-1)
-
- def SetEnds(self, x1, y1, x2, y2):
- """Set the end positions of the line."""
- self._lineControlPoints[0] = wx.RealPoint(x1, y1)
- self._lineControlPoints[-1] = wx.RealPoint(x2, y2)
-
- # Find centre point
- self._xpos = (x1 + x2) / 2.0
- self._ypos = (y1 + y2) / 2.0
-
- # Get absolute positions of ends
- def GetEnds(self):
- """Get the visible endpoints of the lines for drawing between two objects."""
- first_point = self._lineControlPoints[0]
- last_point = self._lineControlPoints[-1]
-
- return first_point[0], first_point[1], last_point[0], last_point[1]
-
- def SetAttachments(self, from_attach, to_attach):
- """Specify which object attachment points should be used at each end
- of the line.
- """
- self._attachmentFrom = from_attach
- self._attachmentTo = to_attach
-
- def HitTest(self, x, y):
- if not self._lineControlPoints:
- return False
-
- # Look at label regions in case mouse is over a label
- inLabelRegion = False
- for i in range(3):
- if self._regions[i]:
- region = self._regions[i]
- if len(region._formattedText):
- xp, yp = self.GetLabelPosition(i)
- # Offset region from default label position
- cx, cy = region.GetPosition()
- cw, ch = region.GetSize()
- cx += xp
- cy += yp
-
- rLeft = cx - cw / 2.0
- rTop = cy - ch / 2.0
- rRight = cx + cw / 2.0
- rBottom = cy + ch / 2.0
- if x > rLeft and x < rRight and y > rTop and y < rBottom:
- inLabelRegion = True
- break
-
- for i in range(len(self._lineControlPoints) - 1):
- point1 = self._lineControlPoints[i]
- point2 = self._lineControlPoints[i + 1]
-
- # For inaccurate mousing allow 8 pixel corridor
- extra = 4
-
- dx = point2[0] - point1[0]
- dy = point2[1] - point1[1]
-
- seg_len = math.sqrt(dx * dx + dy * dy)
- if dy == 0 and dx == 0:
- continue
- distance_from_seg = seg_len * float((x - point1[0]) * dy - (y - point1[1]) * dx) / (dy * dy + dx * dx)
- distance_from_prev = seg_len * float((y - point1[1]) * dy + (x - point1[0]) * dx) / (dy * dy + dx * dx)
-
- if abs(distance_from_seg) < extra and distance_from_prev >= 0 and distance_from_prev <= seg_len or inLabelRegion:
- return 0, distance_from_seg
-
- return False
-
- def DrawArrows(self, dc):
- """Draw all arrows."""
- # Distance along line of each arrow: space them out evenly
- startArrowPos = 0.0
- endArrowPos = 0.0
- middleArrowPos = 0.0
-
- for arrow in self._arcArrows:
- ah = arrow.GetArrowEnd()
- if ah == ARROW_POSITION_START:
- if arrow.GetXOffset() and not self._ignoreArrowOffsets:
- # If specified, x offset is proportional to line length
- self.DrawArrow(dc, arrow, arrow.GetXOffset(), True)
- else:
- self.DrawArrow(dc, arrow, startArrowPos, False)
- startArrowPos += arrow.GetSize() + arrow.GetSpacing()
- elif ah == ARROW_POSITION_END:
- if arrow.GetXOffset() and not self._ignoreArrowOffsets:
- self.DrawArrow(dc, arrow, arrow.GetXOffset(), True)
- else:
- self.DrawArrow(dc, arrow, endArrowPos, False)
- endArrowPos += arrow.GetSize() + arrow.GetSpacing()
- elif ah == ARROW_POSITION_MIDDLE:
- arrow.SetXOffset(middleArrowPos)
- if arrow.GetXOffset() and not self._ignoreArrowOffsets:
- self.DrawArrow(dc, arrow, arrow.GetXOffset(), True)
- else:
- self.DrawArrow(dc, arrow, middleArrowPos, False)
- middleArrowPos += arrow.GetSize() + arrow.GetSpacing()
-
- def DrawArrow(self, dc, arrow, XOffset, proportionalOffset):
- """Draw the given arrowhead (or annotation)."""
- first_line_point = self._lineControlPoints[0]
- second_line_point = self._lineControlPoints[1]
-
- last_line_point = self._lineControlPoints[-1]
- second_last_line_point = self._lineControlPoints[-2]
-
- # Position of start point of line, at the end of which we draw the arrow
- startPositionX, startPositionY = 0.0, 0.0
-
- ap = arrow.GetPosition()
- if ap == ARROW_POSITION_START:
- # If we're using a proportional offset, calculate just where this
- # will be on the line.
- realOffset = XOffset
- if proportionalOffset:
- totalLength = math.sqrt((second_line_point[0] - first_line_point[0]) * (second_line_point[0] - first_line_point[0]) + (second_line_point[1] - first_line_point[1]) * (second_line_point[1] - first_line_point[1]))
- realOffset = XOffset * totalLength
-
- positionOnLineX, positionOnLineY = GetPointOnLine(second_line_point[0], second_line_point[1], first_line_point[0], first_line_point[1], realOffset)
-
- startPositionX = second_line_point[0]
- startPositionY = second_line_point[1]
- elif ap == ARROW_POSITION_END:
- # If we're using a proportional offset, calculate just where this
- # will be on the line.
- realOffset = XOffset
- if proportionalOffset:
- totalLength = math.sqrt((second_last_line_point[0] - last_line_point[0]) * (second_last_line_point[0] - last_line_point[0]) + (second_last_line_point[1] - last_line_point[1]) * (second_last_line_point[1] - last_line_point[1]));
- realOffset = XOffset * totalLength
-
- positionOnLineX, positionOnLineY = GetPointOnLine(second_last_line_point[0], second_last_line_point[1], last_line_point[0], last_line_point[1], realOffset)
-
- startPositionX = second_last_line_point[0]
- startPositionY = second_last_line_point[1]
- elif ap == ARROW_POSITION_MIDDLE:
- # Choose a point half way between the last and penultimate points
- x = (last_line_point[0] + second_last_line_point[0]) / 2.0
- y = (last_line_point[1] + second_last_line_point[1]) / 2.0
-
- # If we're using a proportional offset, calculate just where this
- # will be on the line.
- realOffset = XOffset
- if proportionalOffset:
- totalLength = math.sqrt((second_last_line_point[0] - x) * (second_last_line_point[0] - x) + (second_last_line_point[1] - y) * (second_last_line_point[1] - y));
- realOffset = XOffset * totalLength
-
- positionOnLineX, positionOnLineY = GetPointOnLine(second_last_line_point[0], second_last_line_point[1], x, y, realOffset)
- startPositionX = second_last_line_point[0]
- startPositionY = second_last_line_point[1]
-
- # Add yOffset to arrow, if any
-
- # The translation that the y offset may give
- deltaX = 0.0
- deltaY = 0.0
- if arrow.GetYOffset and not self._ignoreArrowOffsets:
- # |(x4, y4)
- # |d
- # |
- # (x1, y1)--------------(x3, y3)------------------(x2, y2)
- # x4 = x3 - d * math.sin(theta)
- # y4 = y3 + d * math.cos(theta)
- #
- # Where theta = math.tan(-1) of (y3-y1) / (x3-x1)
- x1 = startPositionX
- y1 = startPositionY
- x3 = float(positionOnLineX)
- y3 = float(positionOnLineY)
- d = -arrow.GetYOffset() # Negate so +offset is above line
-
- if x3 == x1:
- theta = math.pi / 2.0
- else:
- theta = math.atan((y3 - y1) / (x3 - x1))
-
- x4 = x3 - d * math.sin(theta)
- y4 = y3 + d * math.cos(theta)
-
- deltaX = x4 - positionOnLineX
- deltaY = y4 - positionOnLineY
-
- at = arrow._GetType()
- if at == ARROW_ARROW:
- arrowLength = arrow.GetSize()
- arrowWidth = arrowLength / 3.0
-
- tip_x, tip_y, side1_x, side1_y, side2_x, side2_y = GetArrowPoints(startPositionX + deltaX, startPositionY + deltaY, positionOnLineX + deltaX, positionOnLineY + deltaY, arrowLength, arrowWidth)
-
- points = [[tip_x, tip_y],
- [side1_x, side1_y],
- [side2_x, side2_y],
- [tip_x, tip_y]]
-
- dc.SetPen(self._pen)
- dc.SetBrush(self._brush)
- dc.DrawPolygon(points)
- elif at in [ARROW_HOLLOW_CIRCLE, ARROW_FILLED_CIRCLE]:
- # Find point on line of centre of circle, which is a radius away
- # from the end position
- diameter = arrow.GetSize()
- x, y = GetPointOnLine(startPositionX + deltaX, startPositionY + deltaY,
- positionOnLineX + deltaX, positionOnLineY + deltaY,
- diameter / 2.0)
- x1 = x - diameter / 2.0
- y1 = y - diameter / 2.0
- dc.SetPen(self._pen)
- if arrow._GetType() == ARROW_HOLLOW_CIRCLE:
- dc.SetBrush(self.GetBackgroundBrush())
- else:
- dc.SetBrush(self._brush)
-
- dc.DrawEllipse(x1, y1, diameter, diameter)
- elif at == ARROW_SINGLE_OBLIQUE:
- pass
- elif at == ARROW_METAFILE:
- if arrow.GetMetaFile():
- # Find point on line of centre of object, which is a half-width away
- # from the end position
- #
- # width
- # <-- start pos <-----><-- positionOnLineX
- # _____
- # --------------| x | <-- e.g. rectangular arrowhead
- # -----
- #
- x, y = GetPointOnLine(startPositionX, startPositionY,
- positionOnLineX, positionOnLineY,
- arrow.GetMetaFile()._width / 2.0)
- # Calculate theta for rotating the metafile.
- #
- # |
- # | o(x2, y2) 'o' represents the arrowhead.
- # | /
- # | /
- # | /theta
- # | /(x1, y1)
- # |______________________
- #
- theta = 0.0
- x1 = startPositionX
- y1 = startPositionY
- x2 = float(positionOnLineX)
- y2 = float(positionOnLineY)
-
- if x1 == x2 and y1 == y2:
- theta = 0.0
- elif x1 == x2 and y1 > y2:
- theta = 3.0 * math.pi / 2.0
- elif x1 == x2 and y2 > y1:
- theta = math.pi / 2.0
- elif x2 > x1 and y2 >= y1:
- theta = math.atan((y2 - y1) / (x2 - x1))
- elif x2 < x1:
- theta = math.pi + math.atan((y2 - y1) / (x2 - x1))
- elif x2 > x1 and y2 < y1:
- theta = 2 * math.pi + math.atan((y2 - y1) / (x2 - x1))
- else:
- raise "Unknown arrowhead rotation case"
-
- # Rotate about the centre of the object, then place
- # the object on the line.
- if arrow.GetMetaFile().GetRotateable():
- arrow.GetMetaFile().Rotate(0.0, 0.0, theta)
-
- if self._erasing:
- # If erasing, just draw a rectangle
- minX, minY, maxX, maxY = arrow.GetMetaFile().GetBounds()
- # Make erasing rectangle slightly bigger or you get droppings
- extraPixels = 4
- dc.DrawRectangle(deltaX + x + minX - extraPixels / 2.0, deltaY + y + minY - extraPixels / 2.0, maxX - minX + extraPixels, maxY - minY + extraPixels)
- else:
- arrow.GetMetaFile().Draw(dc, x + deltaX, y + deltaY)
-
- def OnErase(self, dc):
- old_pen = self._pen
- old_brush = self._brush
-
- bg_pen = self.GetBackgroundPen()
- bg_brush = self.GetBackgroundBrush()
- self.SetPen(bg_pen)
- self.SetBrush(bg_brush)
-
- bound_x, bound_y = self.GetBoundingBoxMax()
- if self._font:
- dc.SetFont(self._font)
-
- # Undraw text regions
- for i in range(3):
- if self._regions[i]:
- x, y = self.GetLabelPosition(i)
- self.EraseRegion(dc, self._regions[i], x, y)
-
- # Undraw line
- dc.SetPen(self.GetBackgroundPen())
- dc.SetBrush(self.GetBackgroundBrush())
-
- # Drawing over the line only seems to work if the line has a thickness
- # of 1.
- if old_pen and old_pen.GetWidth() > 1:
- dc.DrawRectangle(self._xpos - bound_x / 2.0 - 2, self._ypos - bound_y / 2.0 - 2,
- bound_x + 4, bound_y + 4)
- else:
- self._erasing = True
- self.GetEventHandler().OnDraw(dc)
- self.GetEventHandler().OnEraseControlPoints(dc)
- self._erasing = False
-
- if old_pen:
- self.SetPen(old_pen)
- if old_brush:
- self.SetBrush(old_brush)
-
- def GetBoundingBoxMin(self):
- x1, y1 = 10000, 10000
- x2, y2 = -10000, -10000
-
- for point in self._lineControlPoints:
- if point[0] < x1:
- x1 = point[0]
- if point[1] < y1:
- y1 = point[1]
- if point[0] > x2:
- x2 = point[0]
- if point[1] > y2:
- y2 = point[1]
-
- return x2 - x1, y2 - y1
-
- # For a node image of interest, finds the position of this arc
- # amongst all the arcs which are attached to THIS SIDE of the node image,
- # and the number of same.
- def FindNth(self, image, incoming):
- """Find the position of the line on the given object.
-
- Specify whether incoming or outgoing lines are being considered
- with incoming.
- """
- n = -1
- num = 0
-
- if image == self._to:
- this_attachment = self._attachmentTo
- else:
- this_attachment = self._attachmentFrom
-
- # Find number of lines going into / out of this particular attachment point
- for line in image.GetLines():
- if line._from == image:
- # This is the nth line attached to 'image'
- if line == self and not incoming:
- n = num
-
- # Increment num count if this is the same side (attachment number)
- if line._attachmentFrom == this_attachment:
- num += 1
-
- if line._to == image:
- # This is the nth line attached to 'image'
- if line == self and incoming:
- n = num
-
- # Increment num count if this is the same side (attachment number)
- if line._attachmentTo == this_attachment:
- num += 1
-
- return n, num
-
- def OnDrawOutline(self, dc, x, y, w, h):
- old_pen = self._pen
- old_brush = self._brush
-
- dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
- self.SetPen(dottedPen)
- self.SetBrush(wx.TRANSPARENT_BRUSH)
-
- self.GetEventHandler().OnDraw(dc)
-
- if old_pen:
- self.SetPen(old_pen)
- else:
- self.SetPen(None)
- if old_brush:
- self.SetBrush(old_brush)
- else:
- self.SetBrush(None)
-
- def OnMovePre(self, dc, x, y, old_x, old_y, display = True):
- x_offset = x - old_x
- y_offset = y - old_y
-
- if self._lineControlPoints and not (x_offset == 0 and y_offset == 0):
- for point in self._lineControlPoints:
- point[0] += x_offset
- point[1] += y_offset
-
- # Move temporary label rectangles if necessary
- for i in range(3):
- if self._labelObjects[i]:
- self._labelObjects[i].Erase(dc)
- xp, yp = self.GetLabelPosition(i)
- if i < len(self._regions):
- xr, yr = self._regions[i].GetPosition()
- else:
- xr, yr = 0, 0
- self._labelObjects[i].Move(dc, xp + xr, yp + yr)
- return True
-
- def OnMoveLink(self, dc, moveControlPoints = True):
- """Called when a connected object has moved, to move the link to
- correct position
- """
- if not self._from or not self._to:
- return
-
- # Do each end - nothing in the middle. User has to move other points
- # manually if necessary
- end_x, end_y, other_end_x, other_end_y = self.FindLineEndPoints()
-
- oldX, oldY = self._xpos, self._ypos
-
- # pi: The first time we go through FindLineEndPoints we can't
- # use the middle points (since they don't have sane values),
- # so we just do what we do for a normal line. Then we call
- # Initialise to set the middle points, and then FindLineEndPoints
- # again, but this time (and from now on) we use the middle
- # points to calculate the end points.
- # This was buggy in the C++ version too.
-
- self.SetEnds(end_x, end_y, other_end_x, other_end_y)
-
- if len(self._lineControlPoints) > 2:
- self.Initialise()
-
- # Do a second time, because one may depend on the other
- end_x, end_y, other_end_x, other_end_y = self.FindLineEndPoints()
- self.SetEnds(end_x, end_y, other_end_x, other_end_y)
-
- # Try to move control points with the arc
- x_offset = self._xpos - oldX
- y_offset = self._ypos - oldY
-
- # Only move control points if it's a self link. And only works
- # if attachment mode is ON
- if self._from == self._to and self._from.GetAttachmentMode() != ATTACHMENT_MODE_NONE and moveControlPoints and self._lineControlPoints and not (x_offset == 0 and y_offset == 0):
- for point in self._lineControlPoints[1:-1]:
- point[0] += x_offset
- point[1] += y_offset
-
- self.Move(dc, self._xpos, self._ypos)
-
- def FindLineEndPoints(self):
- """Finds the x, y points at the two ends of the line.
-
- This function can be used by e.g. line-routing routines to
- get the actual points on the two node images where the lines will be
- drawn to / from.
- """
- if not self._from or not self._to:
- return
-
- # Do each end - nothing in the middle. User has to move other points
- # manually if necessary.
- second_point = self._lineControlPoints[1]
- second_last_point = self._lineControlPoints[-2]
-
- # pi: If we have a segmented line and this is the first time,
- # do this as a straight line.
- if len(self._lineControlPoints) > 2 and self._initialised:
- if self._from.GetAttachmentMode() != ATTACHMENT_MODE_NONE:
- nth, no_arcs = self.FindNth(self._from, False) # Not incoming
- end_x, end_y = self._from.GetAttachmentPosition(self._attachmentFrom, nth, no_arcs, self)
- else:
- end_x, end_y = self._from.GetPerimeterPoint(self._from.GetX(), self._from.GetY(), second_point[0], second_point[1])
-
- if self._to.GetAttachmentMode() != ATTACHMENT_MODE_NONE:
- nth, no_arch = self.FindNth(self._to, True) # Incoming
- other_end_x, other_end_y = self._to.GetAttachmentPosition(self._attachmentTo, nth, no_arch, self)
- else:
- other_end_x, other_end_y = self._to.GetPerimeterPoint(self._to.GetX(), self._to.GetY(), second_last_point[0], second_last_point[1])
- else:
- fromX = self._from.GetX()
- fromY = self._from.GetY()
- toX = self._to.GetX()
- toY = self._to.GetY()
-
- if self._from.GetAttachmentMode() != ATTACHMENT_MODE_NONE:
- nth, no_arcs = self.FindNth(self._from, False)
- end_x, end_y = self._from.GetAttachmentPosition(self._attachmentFrom, nth, no_arcs, self)
- fromX = end_x
- fromY = end_y
-
- if self._to.GetAttachmentMode() != ATTACHMENT_MODE_NONE:
- nth, no_arcs = self.FindNth(self._to, True)
- other_end_x, other_end_y = self._to.GetAttachmentPosition(self._attachmentTo, nth, no_arcs, self)
- toX = other_end_x
- toY = other_end_y
-
- if self._from.GetAttachmentMode() == ATTACHMENT_MODE_NONE:
- end_x, end_y = self._from.GetPerimeterPoint(self._from.GetX(), self._from.GetY(), toX, toY)
-
- if self._to.GetAttachmentMode() == ATTACHMENT_MODE_NONE:
- other_end_x, other_end_y = self._to.GetPerimeterPoint(self._to.GetX(), self._to.GetY(), fromX, fromY)
-
- return end_x, end_y, other_end_x, other_end_y
-
-
- def OnDraw(self, dc):
- if not self._lineControlPoints:
- return
-
- if self._pen:
- dc.SetPen(self._pen)
- if self._brush:
- dc.SetBrush(self._brush)
-
- points = []
- for point in self._lineControlPoints:
- points.append(wx.Point(point[0], point[1]))
-
- if self._isSpline:
- dc.DrawSpline(points)
- else:
- dc.DrawLines(points)
-
- if sys.platform[:3] == "win":
- # For some reason, last point isn't drawn under Windows
- pt = points[-1]
- dc.DrawPoint(pt[0], pt[1])
-
- # Problem with pen - if not a solid pen, does strange things
- # to the arrowhead. So make (get) a new pen that's solid.
- if self._pen and self._pen.GetStyle() != wx.SOLID:
- solid_pen = wx.Pen(self._pen.GetColour(), 1, wx.SOLID)
- if solid_pen:
- dc.SetPen(solid_pen)
-
- self.DrawArrows(dc)
-
- def OnDrawControlPoints(self, dc):
- if not self._drawHandles:
- return
-
- # Draw temporary label rectangles if necessary
- for i in range(3):
- if self._labelObjects[i]:
- self._labelObjects[i].Draw(dc)
-
- Shape.OnDrawControlPoints(self, dc)
-
- def OnEraseControlPoints(self, dc):
- # Erase temporary label rectangles if necessary
-
- for i in range(3):
- if self._labelObjects[i]:
- self._labelObjects[i].Erase(dc)
-
- Shape.OnEraseControlPoints(self, dc)
-
- def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
- pass
-
- def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
- pass
-
- def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
- pass
-
- def OnDrawContents(self, dc):
- if self.GetDisableLabel():
- return
-
- for i in range(3):
- if self._regions[i]:
- x, y = self.GetLabelPosition(i)
- self.DrawRegion(dc, self._regions[i], x, y)
-
- def SetTo(self, object):
- """Set the 'to' object for the line."""
- self._to = object
-
- def SetFrom(self, object):
- """Set the 'from' object for the line."""
- self._from = object
-
- def MakeControlPoints(self):
- """Make handle control points."""
- if self._canvas and self._lineControlPoints:
- first = self._lineControlPoints[0]
- last = self._lineControlPoints[-1]
-
- control = LineControlPoint(self._canvas, self, CONTROL_POINT_SIZE, first[0], first[1], CONTROL_POINT_ENDPOINT_FROM)
- control._point = first
- self._canvas.AddShape(control)
- self._controlPoints.append(control)
-
- for point in self._lineControlPoints[1:-1]:
- control = LineControlPoint(self._canvas, self, CONTROL_POINT_SIZE, point[0], point[1], CONTROL_POINT_LINE)
- control._point = point
- self._canvas.AddShape(control)
- self._controlPoints.append(control)
-
- control = LineControlPoint(self._canvas, self, CONTROL_POINT_SIZE, last[0], last[1], CONTROL_POINT_ENDPOINT_TO)
- control._point = last
- self._canvas.AddShape(control)
- self._controlPoints.append(control)
-
- def ResetControlPoints(self):
- if self._canvas and self._lineControlPoints and self._controlPoints:
- for i in range(min(len(self._controlPoints), len(self._lineControlPoints))):
- point = self._lineControlPoints[i]
- control = self._controlPoints[i]
- control.SetX(point[0])
- control.SetY(point[1])
-
- # Override select, to create / delete temporary label-moving objects
- def Select(self, select, dc = None):
- Shape.Select(self, select, dc)
- if select:
- for i in range(3):
- if self._regions[i]:
- region = self._regions[i]
- if region._formattedText:
- w, h = region.GetSize()
- x, y = region.GetPosition()
- xx, yy = self.GetLabelPosition(i)
-
- if self._labelObjects[i]:
- self._labelObjects[i].Select(False)
- self._labelObjects[i].RemoveFromCanvas(self._canvas)
-
- self._labelObjects[i] = self.OnCreateLabelShape(self, region, w, h)
- self._labelObjects[i].AddToCanvas(self._canvas)
- self._labelObjects[i].Show(True)
- if dc:
- self._labelObjects[i].Move(dc, x + xx, y + yy)
- self._labelObjects[i].Select(True, dc)
- else:
- for i in range(3):
- if self._labelObjects[i]:
- self._labelObjects[i].Select(False, dc)
- self._labelObjects[i].Erase(dc)
- self._labelObjects[i].RemoveFromCanvas(self._canvas)
- self._labelObjects[i] = None
-
- # Control points ('handles') redirect control to the actual shape, to
- # make it easier to override sizing behaviour.
- def OnSizingDragLeft(self, pt, 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)
-
- if pt._type == CONTROL_POINT_LINE:
- x, y = self._canvas.Snap(x, y)
-
- pt.SetX(x)
- pt.SetY(y)
- pt._point[0] = x
- pt._point[1] = y
-
- old_pen = self.GetPen()
- old_brush = self.GetBrush()
-
- self.SetPen(dottedPen)
- self.SetBrush(wx.TRANSPARENT_BRUSH)
-
- self.GetEventHandler().OnMoveLink(dc, False)
-
- self.SetPen(old_pen)
- self.SetBrush(old_brush)
-
- def OnSizingBeginDragLeft(self, pt, x, y, keys = 0, attachment = 0):
- dc = wx.ClientDC(self.GetCanvas())
- self.GetCanvas().PrepareDC(dc)
-
- if pt._type == CONTROL_POINT_LINE:
- pt._originalPos = pt._point
- x, y = self._canvas.Snap(x, y)
-
- self.Erase(dc)
-
- # Redraw start and end objects because we've left holes
- # when erasing the line
- self.GetFrom().OnDraw(dc)
- self.GetFrom().OnDrawContents(dc)
- self.GetTo().OnDraw(dc)
- self.GetTo().OnDrawContents(dc)
-
- self.SetDisableLabel(True)
- dc.SetLogicalFunction(OGLRBLF)
-
- pt._xpos = x
- pt._ypos = y
- pt._point[0] = x
- pt._point[1] = y
-
- old_pen = self.GetPen()
- old_brush = self.GetBrush()
-
- dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
- self.SetPen(dottedPen)
- self.SetBrush(wx.TRANSPARENT_BRUSH)
-
- self.GetEventHandler().OnMoveLink(dc, False)
-
- self.SetPen(old_pen)
- self.SetBrush(old_brush)
-
- if pt._type == CONTROL_POINT_ENDPOINT_FROM or pt._type == CONTROL_POINT_ENDPOINT_TO:
- self._canvas.SetCursor(wx.StockCursor(wx.CURSOR_BULLSEYE))
- pt._oldCursor = wx.STANDARD_CURSOR
-
- def OnSizingEndDragLeft(self, pt, x, y, keys = 0, attachment = 0):
- dc = wx.ClientDC(self.GetCanvas())
- self.GetCanvas().PrepareDC(dc)
-
- self.SetDisableLabel(False)
-
- if pt._type == CONTROL_POINT_LINE:
- x, y = self._canvas.Snap(x, y)
-
- rpt = wx.RealPoint(x, y)
-
- # Move the control point back to where it was;
- # MoveControlPoint will move it to the new position
- # if it decides it wants. We only moved the position
- # during user feedback so we could redraw the line
- # as it changed shape.
- pt._xpos = pt._originalPos[0]
- pt._ypos = pt._originalPos[1]
- pt._point[0] = pt._originalPos[0]
- pt._point[1] = pt._originalPos[1]
-
- self.OnMoveMiddleControlPoint(dc, pt, rpt)
-
- if pt._type == CONTROL_POINT_ENDPOINT_FROM:
- if pt._oldCursor:
- self._canvas.SetCursor(pt._oldCursor)
-
- if self.GetFrom():
- self.GetFrom().MoveLineToNewAttachment(dc, self, x, y)
-
- if pt._type == CONTROL_POINT_ENDPOINT_TO:
- if pt._oldCursor:
- self._canvas.SetCursor(pt._oldCursor)
-
- if self.GetTo():
- self.GetTo().MoveLineToNewAttachment(dc, self, x, y)
-
- # This is called only when a non-end control point is moved
- def OnMoveMiddleControlPoint(self, dc, lpt, pt):
- lpt._xpos = pt[0]
- lpt._ypos = pt[1]
-
- lpt._point[0] = pt[0]
- lpt._point[1] = pt[1]
-
- self.GetEventHandler().OnMoveLink(dc)
-
- return True
-
- def AddArrow(self, type, end = ARROW_POSITION_END, size = 10.0, xOffset = 0.0, name = "", mf = None, arrowId = -1):
- """Add an arrow (or annotation) to the line.
-
- type may currently be one of:
-
- ARROW_HOLLOW_CIRCLE
- Hollow circle.
- ARROW_FILLED_CIRCLE
- Filled circle.
- ARROW_ARROW
- Conventional arrowhead.
- ARROW_SINGLE_OBLIQUE
- Single oblique stroke.
- ARROW_DOUBLE_OBLIQUE
- Double oblique stroke.
- ARROW_DOUBLE_METAFILE
- Custom arrowhead.
-
- end may currently be one of:
-
- ARROW_POSITION_END
- Arrow appears at the end.
- ARROW_POSITION_START
- Arrow appears at the start.
-
- arrowSize specifies the length of the arrow.
-
- xOffset specifies the offset from the end of the line.
-
- name specifies a name for the arrow.
-
- mf can be a wxPseduoMetaFile, perhaps loaded from a simple Windows
- metafile.
-
- arrowId is the id for the arrow.
- """
- arrow = ArrowHead(type, end, size, xOffset, name, mf, arrowId)
- self._arcArrows.append(arrow)
- return arrow
-
- # Add arrowhead at a particular position in the arrowhead list
- def AddArrowOrdered(self, arrow, referenceList, end):
- """Add an arrowhead in the position indicated by the reference list
- of arrowheads, which contains all legal arrowheads for this line, in
- the correct order. E.g.
-
- Reference list: a b c d e
- Current line list: a d
-
- Add c, then line list is: a c d.
-
- If no legal arrowhead position, return FALSE. Assume reference list
- is for one end only, since it potentially defines the ordering for
- any one of the 3 positions. So we don't check the reference list for
- arrowhead position.
- """
- if not referenceList:
- return False
-
- targetName = arrow.GetName()
-
- # First check whether we need to insert in front of list,
- # because this arrowhead is the first in the reference
- # list and should therefore be first in the current list.
- refArrow = referenceList[0]
- if refArrow.GetName() == targetName:
- self._arcArrows.insert(0, arrow)
- return True
-
- i1 = i2 = 0
- while i1 < len(referenceList) and i2 < len(self._arcArrows):
- refArrow = referenceList[i1]
- currArrow = self._arcArrows[i2]
-
- # Matching: advance current arrow pointer
- if currArrow.GetArrowEnd() == end and currArrow.GetName() == refArrow.GetName():
- i2 += 1
-
- # Check if we're at the correct position in the
- # reference list
- if targetName == refArrow.GetName():
- if i2 < len(self._arcArrows):
- self._arcArrows.insert(i2, arrow)
- else:
- self._arcArrows.append(arrow)
- return True
- i1 += 1
-
- self._arcArrows.append(arrow)
- return True
-
- def ClearArrowsAtPosition(self, end):
- """Delete the arrows at the specified position, or at any position
- if position is -1.
- """
- if end == -1:
- self._arcArrows = []
- return
-
- for arrow in self._arcArrows:
- if arrow.GetArrowEnd() == end:
- self._arcArrows.remove(arrow)
-
- def ClearArrow(self, name):
- """Delete the arrow with the given name."""
- for arrow in self._arcArrows:
- if arrow.GetName() == name:
- self._arcArrows.remove(arrow)
- return True
- return False
-
- def FindArrowHead(self, position, name):
- """Find arrowhead by position and name.
-
- if position is -1, matches any position.
- """
- for arrow in self._arcArrows:
- if (position == -1 or position == arrow.GetArrowEnd()) and arrow.GetName() == name:
- return arrow
-
- return None
-
- def FindArrowHeadId(self, arrowId):
- """Find arrowhead by id."""
- for arrow in self._arcArrows:
- if arrowId == arrow.GetId():
- return arrow
-
- return None
-
- def DeleteArrowHead(self, position, name):
- """Delete arrowhead by position and name.
-
- if position is -1, matches any position.
- """
- for arrow in self._arcArrows:
- if (position == -1 or position == arrow.GetArrowEnd()) and arrow.GetName() == name:
- self._arcArrows.remove(arrow)
- return True
- return False
-
- def DeleteArrowHeadId(self, id):
- """Delete arrowhead by id."""
- for arrow in self._arcArrows:
- if arrowId == arrow.GetId():
- self._arcArrows.remove(arrow)
- return True
- return False
-
- # Calculate the minimum width a line
- # occupies, for the purposes of drawing lines in tools.
- def FindMinimumWidth(self):
- """Find the horizontal width for drawing a line with arrows in
- minimum space. Assume arrows at end only.
- """
- minWidth = 0.0
- for arrowHead in self._arcArrows:
- minWidth += arrowHead.GetSize()
- if arrowHead != self._arcArrows[-1]:
- minWidth += arrowHead + GetSpacing
-
- # We have ABSOLUTE minimum now. So
- # scale it to give it reasonable aesthetics
- # when drawing with line.
- if minWidth > 0:
- minWidth = minWidth * 1.4
- else:
- minWidth = 20.0
-
- self.SetEnds(0.0, 0.0, minWidth, 0.0)
- self.Initialise()
-
- return minWidth
-
- def FindLinePosition(self, x, y):
- """Find which position we're talking about at this x, y.
-
- Returns ARROW_POSITION_START, ARROW_POSITION_MIDDLE, ARROW_POSITION_END.
- """
- startX, startY, endX, endY = self.GetEnds()
-
- # Find distances from centre, start and end. The smallest wins
- centreDistance = math.sqrt((x - self._xpos) * (x - self._xpos) + (y - self._ypos) * (y - self._ypos))
- startDistance = math.sqrt((x - startX) * (x - startX) + (y - startY) * (y - startY))
- endDistance = math.sqrt((x - endX) * (x - endX) + (y - endY) * (y - endY))
-
- if centreDistance < startDistance and centreDistance < endDistance:
- return ARROW_POSITION_MIDDLE
- elif startDistance < endDistance:
- return ARROW_POSITION_START
- else:
- return ARROW_POSITION_END
-
- def SetAlignmentOrientation(self, isEnd, isHoriz):
- if isEnd:
- if isHoriz and self._alignmentEnd & LINE_ALIGNMENT_HORIZ != LINE_ALIGNMENT_HORIZ:
- self._alignmentEnd != LINE_ALIGNMENT_HORIZ
- elif not isHoriz and self._alignmentEnd & LINE_ALIGNMENT_HORIZ == LINE_ALIGNMENT_HORIZ:
- self._alignmentEnd -= LINE_ALIGNMENT_HORIZ
- else:
- if isHoriz and self._alignmentStart & LINE_ALIGNMENT_HORIZ != LINE_ALIGNMENT_HORIZ:
- self._alignmentStart != LINE_ALIGNMENT_HORIZ
- elif not isHoriz and self._alignmentStart & LINE_ALIGNMENT_HORIZ == LINE_ALIGNMENT_HORIZ:
- self._alignmentStart -= LINE_ALIGNMENT_HORIZ
-
- def SetAlignmentType(self, isEnd, alignType):
- if isEnd:
- if alignType == LINE_ALIGNMENT_TO_NEXT_HANDLE:
- if self._alignmentEnd & LINE_ALIGNMENT_TO_NEXT_HANDLE != LINE_ALIGNMENT_TO_NEXT_HANDLE:
- self._alignmentEnd |= LINE_ALIGNMENT_TO_NEXT_HANDLE
- elif self._alignmentEnd & LINE_ALIGNMENT_TO_NEXT_HANDLE == LINE_ALIGNMENT_TO_NEXT_HANDLE:
- self._alignmentEnd -= LINE_ALIGNMENT_TO_NEXT_HANDLE
- else:
- if alignType == LINE_ALIGNMENT_TO_NEXT_HANDLE:
- if self._alignmentStart & LINE_ALIGNMENT_TO_NEXT_HANDLE != LINE_ALIGNMENT_TO_NEXT_HANDLE:
- self._alignmentStart |= LINE_ALIGNMENT_TO_NEXT_HANDLE
- elif self._alignmentStart & LINE_ALIGNMENT_TO_NEXT_HANDLE == LINE_ALIGNMENT_TO_NEXT_HANDLE:
- self._alignmentStart -= LINE_ALIGNMENT_TO_NEXT_HANDLE
-
- def GetAlignmentOrientation(self, isEnd):
- if isEnd:
- return self._alignmentEnd & LINE_ALIGNMENT_HORIZ == LINE_ALIGNMENT_HORIZ
- else:
- return self._alignmentStart & LINE_ALIGNMENT_HORIZ == LINE_ALIGNMENT_HORIZ
-
- def GetAlignmentType(self, isEnd):
- if isEnd:
- return self._alignmentEnd & LINE_ALIGNMENT_TO_NEXT_HANDLE
- else:
- return self._alignmentStart & LINE_ALIGNMENT_TO_NEXT_HANDLE
-
- def GetNextControlPoint(self, shape):
- """Find the next control point in the line after the start / end point,
- depending on whether the shape is at the start or end.
- """
- n = len(self._lineControlPoints)
- if self._to == shape:
- # Must be END of line, so we want (n - 1)th control point.
- # But indexing ends at n-1, so subtract 2.
- nn = n - 2
- else:
- nn = 1
- if nn < len(self._lineControlPoints):
- return self._lineControlPoints[nn]
- return None
-
- def OnCreateLabelShape(self, parent, region, w, h):
- return LabelShape(parent, region, w, h)
-
-
- def OnLabelMovePre(self, dc, labelShape, x, y, old_x, old_y, display):
- labelShape._shapeRegion.SetSize(labelShape.GetWidth(), labelShape.GetHeight())
-
- # Find position in line's region list
- i = self._regions.index(labelShape._shapeRegion)
-
- xx, yy = self.GetLabelPosition(i)
- # Set the region's offset, relative to the default position for
- # each region.
- labelShape._shapeRegion.SetPosition(x - xx, y - yy)
- labelShape.SetX(x)
- labelShape.SetY(y)
-
- # Need to reformat to fit region
- if labelShape._shapeRegion.GetText():
- s = labelShape._shapeRegion.GetText()
- labelShape.FormatText(dc, s, i)
- self.DrawRegion(dc, labelShape._shapeRegion, xx, yy)
- return True
-