def getConnectorLine(self, otherWidget): """ Get the line that would be drawn between this widget and another. """ start = self.parent.toPixels(self.getCenter()) end = self.parent.toPixels(otherWidget.getCenter()) # Additional tweak to make overlapping arrows more visible length = min( math.sqrt((start[0] - end[0])**2 + (start[1] - end[1])**2) / 32, 16) if start[1] != end[1]: start[0] += length * math.copysign(1, start[1] - end[1]) end[0] += length * math.copysign(1, start[1] - end[1]) if start[0] != end[0]: start[1] += length * math.copysign(1, start[0] - end[0]) end[1] += length * math.copysign(1, start[0] - end[0]) # Clip the end of the arrow start, end = geometry.clipLineByRects([start, end], otherWidget.getPixelRect()) return (start, end)
def paintConnectorTo(self, otherWidget, arrowheads, gc, updateRect=None): """ Paints a connecting line between this widget and another, with optional arrowheads. You may pass either a wx.GraphicsContext (anti-aliased drawing) or a wx.PaintDC. """ start = self.parent.toPixels(self.getCenter()) end = self.parent.toPixels(otherWidget.getCenter()) start, end = geometry.clipLineByRects([start, end], otherWidget.getPixelRect()) # does it actually need to be drawn? if updateRect and not geometry.lineRectIntersection([start, end], updateRect): return if otherWidget == self: return # ok, really draw the line lineWidth = max( self.parent.toPixels((PassageWidget.CONNECTOR_WIDTH, 0), scaleOnly=True)[0], 1) gc.SetPen(wx.Pen(PassageWidget.CONNECTOR_COLOR, lineWidth)) if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(start[0], start[1], end[0], end[1]) else: gc.DrawLine(start[0], start[1], end[0], end[1]) # arrowheads at end if not arrowheads: return arrowheadLength = max( self.parent.toPixels((PassageWidget.ARROWHEAD_LENGTH, 0), scaleOnly=True)[0], 1) arrowhead = geometry.endPointProjectedFrom((start, end), angle = PassageWidget.ARROWHEAD_ANGLE, \ distance = arrowheadLength) if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(end[0], end[1], arrowhead[0], arrowhead[1]) else: gc.DrawLine(end[0], end[1], arrowhead[0], arrowhead[1]) arrowhead = geometry.endPointProjectedFrom((start, end), angle = 0 - PassageWidget.ARROWHEAD_ANGLE, \ distance = arrowheadLength) if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(end[0], end[1], arrowhead[0], arrowhead[1]) else: gc.DrawLine(end[0], end[1], arrowhead[0], arrowhead[1])
def getConnectorLine(self, otherWidget, clipped=True): """ Get the line that would be drawn between this widget and another. """ start = self.getCenter() end = otherWidget.getCenter() #Tweak to make overlapping lines easier to see by shifting the end point #Devision by a large constant to so the behavior is not overly noticeable while dragging lengthSquared = ((start[0]-end[0])**2+(start[1]-end[1])**2)/1024**2 end[0] += (0.5 - math.sin(lengthSquared))*PassageWidget.SIZE/8.0 end[1] += (0.5 - math.cos(lengthSquared))*PassageWidget.SIZE/8.0 if clipped: [start, end] = geometry.clipLineByRects([start, end], otherWidget.getLogicalRect()) return self.parent.toPixels(start), self.parent.toPixels(end)
def paintConnectorTo (self, otherWidget, arrowheads, color, gc, updateRect = None): """ Paints a connecting line between this widget and another, with optional arrowheads. You may pass either a wx.GraphicsContext (anti-aliased drawing) or a wx.PaintDC. """ start = self.parent.toPixels(self.getCenter()) end = self.parent.toPixels(otherWidget.getCenter()) start, end = geometry.clipLineByRects([start, end], otherWidget.getPixelRect()) # does it actually need to be drawn? if updateRect and not geometry.lineRectIntersection([start, end], updateRect): return if otherWidget == self: return # ok, really draw the line lineWidth = max(self.parent.toPixels((PassageWidget.CONNECTOR_WIDTH, 0), scaleOnly = True)[0], 1) gc.SetPen(wx.Pen(color, lineWidth)) if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(start[0], start[1], end[0], end[1]) else: gc.DrawLine(start[0], start[1], end[0], end[1]) # arrowheads at end if not arrowheads: return arrowheadLength = max(self.parent.toPixels((PassageWidget.ARROWHEAD_LENGTH, 0), scaleOnly = True)[0], 1) arrowhead = geometry.endPointProjectedFrom((start, end), angle = PassageWidget.ARROWHEAD_ANGLE, \ distance = arrowheadLength) if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(end[0], end[1], arrowhead[0], arrowhead[1]) else: gc.DrawLine(end[0], end[1], arrowhead[0], arrowhead[1]) arrowhead = geometry.endPointProjectedFrom((start, end), angle = 0 - PassageWidget.ARROWHEAD_ANGLE, \ distance = arrowheadLength) if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(end[0], end[1], arrowhead[0], arrowhead[1]) else: gc.DrawLine(end[0], end[1], arrowhead[0], arrowhead[1])
def getConnectorLine(self, otherWidget, clipped=True): """ Get the line that would be drawn between this widget and another. """ start = self.getCenter() end = otherWidget.getCenter() #Tweak to make overlapping lines easier to see by shifting the end point #Devision by a large constant to so the behavior is not overly noticeable while dragging lengthSquared = ((start[0] - end[0])**2 + (start[1] - end[1])**2) / 1024**2 end[0] += (0.5 - math.sin(lengthSquared)) * PassageWidget.SIZE / 8.0 end[1] += (0.5 - math.cos(lengthSquared)) * PassageWidget.SIZE / 8.0 if clipped: [start, end] = geometry.clipLineByRects([start, end], otherWidget.getLogicalRect()) return self.parent.toPixels(start), self.parent.toPixels(end)
def getConnectorLine(self, otherWidget): """ Get the line that would be drawn between this widget and another. """ start = self.parent.toPixels(self.getCenter()) end = self.parent.toPixels(otherWidget.getCenter()) # Additional tweak to make overlapping arrows more visible length = min(math.sqrt((start[0]-end[0])**2 + (start[1]-end[1])**2)/32, 16) if start[1] != end[1]: start[0] += length * math.copysign(1, start[1] - end[1]); end[0] += length * math.copysign(1, start[1] - end[1]); if start[0] != end[0]: start[1] += length * math.copysign(1, start[0] - end[0]); end[1] += length * math.copysign(1, start[0] - end[0]); # Clip the end of the arrow start, end = geometry.clipLineByRects([start, end], otherWidget.getPixelRect()) return (start, end)
def paintConnectorTo(self, otherWidget, arrowheads, color, width, gc, updateRect = None): """ Paints a connecting line between this widget and another, with optional arrowheads. You may pass either a wx.GraphicsContext (anti-aliased drawing) or a wx.PaintDC. """ start = self.parent.toPixels(self.getCenter()) end = self.parent.toPixels(otherWidget.getCenter()) # Additional tweak to make overlapping arrows more visible length = min(math.sqrt((start[0]-end[0])**2 + (start[1]-end[1])**2)/32, 16) if start[1] != end[1]: start[0] += length * math.copysign(1, start[1] - end[1]); end[0] += length * math.copysign(1, start[1] - end[1]); if start[0] != end[0]: start[1] += length * math.copysign(1, start[0] - end[0]); end[1] += length * math.copysign(1, start[0] - end[0]); # Clip the end of the arrow start, end = geometry.clipLineByRects([start, end], otherWidget.getPixelRect()) # does it actually need to be drawn? if otherWidget == self: return if updateRect and not geometry.lineRectIntersection([start, end], updateRect): return # ok, really draw the line lineWidth = max(self.parent.toPixels((width, 0), scaleOnly = True)[0], 1) gc.SetPen(wx.Pen(color, lineWidth)) if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(start[0], start[1], end[0], end[1]) else: gc.DrawLine(start[0], start[1], end[0], end[1]) # arrowheads at end if not arrowheads: return arrowheadLength = max(self.parent.toPixels((PassageWidget.ARROWHEAD_LENGTH, 0), scaleOnly = True)[0], 1) arrowhead = geometry.endPointProjectedFrom((start, end), angle = PassageWidget.ARROWHEAD_ANGLE, \ distance = arrowheadLength) if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(end[0], end[1], arrowhead[0], arrowhead[1]) else: gc.DrawLine(end[0], end[1], arrowhead[0], arrowhead[1]) arrowhead = geometry.endPointProjectedFrom((start, end), angle = 0 - PassageWidget.ARROWHEAD_ANGLE, \ distance = arrowheadLength) if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(end[0], end[1], arrowhead[0], arrowhead[1]) else: gc.DrawLine(end[0], end[1], arrowhead[0], arrowhead[1])
def paintConnectorTo(self, otherWidget, arrowheads, color, width, gc, updateRect=None): """ Paints a connecting line between this widget and another, with optional arrowheads. You may pass either a wx.GraphicsContext (anti-aliased drawing) or a wx.PaintDC. """ start = self.parent.toPixels(self.getCenter()) end = self.parent.toPixels(otherWidget.getCenter()) # Additional tweak to make overlapping arrows more visible length = min( math.sqrt((start[0] - end[0])**2 + (start[1] - end[1])**2) / 32, 16) if start[1] != end[1]: start[0] += length * math.copysign(1, start[1] - end[1]) end[0] += length * math.copysign(1, start[1] - end[1]) if start[0] != end[0]: start[1] += length * math.copysign(1, start[0] - end[0]) end[1] += length * math.copysign(1, start[0] - end[0]) # Clip the end of the arrow start, end = geometry.clipLineByRects([start, end], otherWidget.getPixelRect()) # does it actually need to be drawn? if otherWidget == self: return if updateRect and not geometry.lineRectIntersection([start, end], updateRect): return # ok, really draw the line lineWidth = max(self.parent.toPixels((width, 0), scaleOnly=True)[0], 1) gc.SetPen(wx.Pen(color, lineWidth)) if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(start[0], start[1], end[0], end[1]) else: gc.DrawLine(start[0], start[1], end[0], end[1]) # arrowheads at end if not arrowheads: return arrowheadLength = max( self.parent.toPixels((PassageWidget.ARROWHEAD_LENGTH, 0), scaleOnly=True)[0], 1) arrowhead = geometry.endPointProjectedFrom((start, end), angle = PassageWidget.ARROWHEAD_ANGLE, \ distance = arrowheadLength) if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(end[0], end[1], arrowhead[0], arrowhead[1]) else: gc.DrawLine(end[0], end[1], arrowhead[0], arrowhead[1]) arrowhead = geometry.endPointProjectedFrom((start, end), angle = 0 - PassageWidget.ARROWHEAD_ANGLE, \ distance = arrowheadLength) if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(end[0], end[1], arrowhead[0], arrowhead[1]) else: gc.DrawLine(end[0], end[1], arrowhead[0], arrowhead[1])
def paint (self, event): """Paints marquee selection, widget connectors, and widgets onscreen.""" # do NOT call self.DoPrepareDC() no matter what the docs may say # we already take into account our scroll origin in our # toPixels() method # OS X already double buffers drawing for us; if we try to do it # ourselves, performance is horrendous if (sys.platform == 'darwin'): gc = wx.GraphicsContext.Create(wx.PaintDC(self)) else: gc = wx.GraphicsContext.Create(wx.BufferedPaintDC(self)) # background updateRect = self.GetUpdateRegion().GetBox() gc.SetBrush(wx.Brush(StoryPanel.BACKGROUND_COLOR)) gc.DrawRectangle(updateRect.x, updateRect.y, updateRect.width, updateRect.height) # connectors arrowheadLength = max(self.toPixels((StoryPanel.ARROWHEAD_LENGTH, 0), scaleOnly = True)[0], \ StoryPanel.MIN_ARROWHEAD_LENGTH) gc.SetPen(wx.Pen(StoryPanel.CONNECTOR_COLOR, max(self.toPixels((StoryPanel.CONNECTOR_WIDTH, 0), \ scaleOnly = True)[0], 1))) for widget in self.widgets: if widget.dimmed: continue start = self.toPixels(widget.getCenter()) for link in widget.passage.links(): otherWidget = self.findWidget(link) if otherWidget and not otherWidget.dimmed: # connector line end = self.toPixels(otherWidget.getCenter()) start, end = geometry.clipLineByRects([start, end], otherWidget.getPixelRect()) gc.StrokeLine(start[0], start[1], end[0], end[1]) # arrowheads at end arrowhead = geometry.endPointProjectedFrom((start, end), angle = StoryPanel.ARROWHEAD_ANGLE, \ distance = arrowheadLength) gc.StrokeLine(end[0], end[1], arrowhead[0], arrowhead[1]) arrowhead = geometry.endPointProjectedFrom((start, end), angle = 0 - StoryPanel.ARROWHEAD_ANGLE, \ distance = arrowheadLength) gc.StrokeLine(end[0], end[1], arrowhead[0], arrowhead[1]) # widgets for widget in self.widgets: if updateRect.Intersects(widget.getPixelRect()): widget.paint(gc) # marquee selection # use alpha blending for interior if self.draggingMarquee: marqueeColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT) gc.SetPen(wx.Pen(marqueeColor)) r, g, b = marqueeColor.Get() marqueeColor = wx.Color(r, g, b, StoryPanel.MARQUEE_ALPHA) gc.SetBrush(wx.Brush(marqueeColor)) gc.DrawRectangle(self.dragRect.x, self.dragRect.y, self.dragRect.width, self.dragRect.height)
def paint (self, event): """Paints marquee selection, widget connectors, and widgets onscreen.""" # do NOT call self.DoPrepareDC() no matter what the docs may say # we already take into account our scroll origin in our # toPixels() method # in fast drawing, we ask for a standard paint context # in slow drawing, we ask for a anti-aliased one # # OS X already double buffers drawing for us; if we try to do it # ourselves, performance is horrendous if (sys.platform == 'darwin'): gc = wx.PaintDC(self) else: gc = wx.BufferedPaintDC(self) if not self.app.config.ReadBool('fastStoryPanel'): gc = wx.GraphicsContext.Create(gc) # background updateRect = self.GetUpdateRegion().GetBox() gc.SetBrush(wx.Brush(StoryPanel.BACKGROUND_COLOR)) gc.DrawRectangle(updateRect.x - 1, updateRect.y - 1, updateRect.width + 2, updateRect.height + 2) # connectors arrowheadLength = max(self.toPixels((StoryPanel.ARROWHEAD_LENGTH, 0), scaleOnly = True)[0], \ StoryPanel.MIN_ARROWHEAD_LENGTH) gc.SetPen(wx.Pen(StoryPanel.CONNECTOR_COLOR, max(self.toPixels((StoryPanel.CONNECTOR_WIDTH, 0), \ scaleOnly = True)[0], 1))) # cache bad links so we don't have to keep doing worst-case lookups badLinks = [] for widget in self.widgets: if widget.dimmed: continue start = self.toPixels(widget.getCenter()) for link in widget.passage.links(): if link in badLinks: continue otherWidget = self.findWidget(link) if not otherWidget: badLinks.append(link) if otherWidget and not otherWidget.dimmed: # connector line end = self.toPixels(otherWidget.getCenter()) # does it actually need to be drawn? if not geometry.lineRectIntersection([start, end], updateRect): continue # ok, really draw the line if self.scale > StoryPanel.ARROWHEAD_THRESHOLD: start, end = geometry.clipLineByRects([start, end], otherWidget.getPixelRect()) if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(start[0], start[1], end[0], end[1]) else: gc.DrawLine(start[0], start[1], end[0], end[1]) # arrowheads at end if self.scale < StoryPanel.ARROWHEAD_THRESHOLD: continue arrowhead = geometry.endPointProjectedFrom((start, end), angle = StoryPanel.ARROWHEAD_ANGLE, \ distance = arrowheadLength) if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(end[0], end[1], arrowhead[0], arrowhead[1]) else: gc.DrawLine(end[0], end[1], arrowhead[0], arrowhead[1]) arrowhead = geometry.endPointProjectedFrom((start, end), angle = 0 - StoryPanel.ARROWHEAD_ANGLE, \ distance = arrowheadLength) if isinstance(gc, wx.GraphicsContext): gc.StrokeLine(end[0], end[1], arrowhead[0], arrowhead[1]) else: gc.DrawLine(end[0], end[1], arrowhead[0], arrowhead[1]) # widgets for widget in self.widgets: if updateRect.Intersects(widget.getPixelRect()): widget.paint(gc) # marquee selection # with slow drawing, use alpha blending for interior if self.draggingMarquee: if self.app.config.ReadBool('fastStoryPanel'): gc.SetPen(wx.Pen('#ffffff', 1, wx.DOT)) else: marqueeColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT) gc.SetPen(wx.Pen(marqueeColor)) r, g, b = marqueeColor.Get() marqueeColor = wx.Color(r, g, b, StoryPanel.MARQUEE_ALPHA) gc.SetBrush(wx.Brush(marqueeColor)) gc.DrawRectangle(self.dragRect.x, self.dragRect.y, self.dragRect.width, self.dragRect.height)