📄 _lines.py
字号:
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
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:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -