def draw(self, renderer): renderer.open_group("regpolycollection") self._transform.freeze() self._transOffset.freeze() self.update_scalarmappable() scales = sqrt(self._sizes * self._dpi.get() / 72.0) if self._edgecolors == "None": self._edgecolors = self._facecolors renderer.draw_regpoly_collection( self.clipbox, self._offsets, self._transOffset, self._verts, scales, self._facecolors, self._edgecolors, self._linewidths, self._antialiaseds, ) self._transform.thaw() self._transOffset.thaw() renderer.close_group("regpolycollection")
def line2d_dist(l, p): """ Distance from line to point line is a tuple of coefficients a,b,c """ a, b, c = l x0, y0 = p return abs((a * x0 + b * y0 + c) / nx.sqrt(a**2 + b**2))
def dist2d(p0, p1): """distance between two points""" try: p = p0 - p1 return nx.sqrt(sum(p**2)) except ValueError: print p0, p1 raise
def colorbars(epsoutfile): x = (arange(50.0)-25)/2.0 y = (arange(50.0)-25)/2.0 r = sqrt(x[:,NewAxis]**2+y**2) z = 5.0*cos(r) colmap = ColMapper.ColorMapper("yellow-red", exponent=0.55, brightness=0.5) lut = colmap.generate_lut() c = pyx.canvas.canvas() g = pyxgraph(xlimits=(min(x), max(x)), ylimits=(min(y), max(y)), width=6, height=6) g.pyxplot("y(x)=sin(x)", style="p") # FIXME: can't do empty plots! g.pyxplotarray(z, colmap=lut) c.insert(g) minz = minvalue=min(ravel(z)) maxz = maxvalue=max(ravel(z)) # --- vertical bars dist = 1.2 for orientation, position in [("vertical", "right"), ("vertical", "middle"), ("vertical2", "middle")]: cb = pyxcolorbar(lut=lut, frame=g, pos=(dist, 0), orientation = orientation, position = position, minvalue = minz, maxvalue=maxz) # add a short note on the style: txt = orientation[0] if "2" in orientation: txt += "2" txt += ", "+position[0] cb.pyxlabel( (0.0, 1.3), txt, style=[pyx.text.halign.left]) c.insert(cb) dist = dist + 0.5 # horizontal ones: dist = -0.3 for orientation, position in [("horizontal", "middle"), ("horizontal2", "middle")]: cb = pyxcolorbar(lut=lut, frame=g, pos=(0.0, dist), orientation = orientation, position = position, minvalue = minz, maxvalue=maxz) # add a short note on the style: txt = orientation[0] if "2" in orientation: txt += "2" txt += ", "+position[0] cb.pyxlabel( (1.3, 0.5), txt, style=[pyx.text.halign.left]) c.insert(cb) dist = dist - 0.3 pyxsave(c, epsoutfile)
def too_close(self, x,y, lw): "if there's a label already nearby, find a better place" if self.cl_xy != []: dist = [sqrt((x-loc[0]) ** 2 + (y-loc[1]) ** 2) for loc in self.cl_xy] for d in dist: if d < 1.2*lw: return 1 else: return 0 else: return 0
def __init__(self, x, y, dx, dy, width=1.0, **kwargs): """Draws an arrow, starting at (x,y), direction and length given by (dx,dy) the width of the arrow is scaled by width """ arrow = array([[0.0, 0.1], [0.0, -0.1], [0.8, -0.1], [0.8, -0.3], [1.0, 0.0], [0.8, 0.3], [0.8, 0.1]]) L = sqrt(dx ** 2 + dy ** 2) or 1 # account for div by zero arrow[:, 0] *= L arrow[:, 1] *= width cx = float(dx) / L sx = float(dy) / L M = array([[cx, sx], [-sx, cx]]) verts = matrixmultiply(arrow, M) + [x, y] Polygon.__init__(self, [tuple(t) for t in verts], **kwargs)
def array_example1(epsoutfile): x = (arange(50.0)-25)/2.0 y = (arange(50.0)-25)/2.0 r = sqrt(x[:,NewAxis]**2+y**2) z = 5.0*sin(r) g=pyxgraph(xlimits=(min(x), max(x)), ylimits=(min(y), max(y)), width=6, height=6, key=False) # WARNING: if key is not specified to be False, one gets a weird # error .... #g.pyxplot("y(x)=sin(x)", style="p") # FIXME: can't do empty plots! g.pyxplotarray(z, colmap=ColMapper.ColorMapper("yellow-red", exponent=0.55, brightness=0.5)) g.pyxsave(epsoutfile)
def get_ind_under_point(self, event): 'get the index of the vertex under point if within epsilon tolerance' x, y = zip(*self.poly.verts) # display coords xt, yt = self.poly.get_transform().numerix_x_y(x, y) d = sqrt((xt - event.x)**2 + (yt - event.y)**2) indseq = nonzero(equal(d, amin(d))) ind = indseq[0] if d[ind] >= self.epsilon: ind = None return ind
def get_ind_under_point(self, event): 'get the index of the vertex under point if within epsilon tolerance' x, y = zip(*self.poly.verts) # display coords xt, yt = self.poly.get_transform().numerix_x_y(x, y) d = sqrt((xt-event.x)**2 + (yt-event.y)**2) indseq = nonzero(equal(d, amin(d))) ind = indseq[0] if d[ind]>=self.epsilon: ind = None return ind
def __init__(self, x, y, dx, dy, width=1.0, **kwargs): """Draws an arrow, starting at (x,y), direction and length given by (dx,dy) the width of the arrow is scaled by width """ arrow = array([[0.0, 0.1], [0.0, -0.1], [0.8, -0.1], [0.8, -0.3], [1.0, 0.0], [0.8, 0.3], [0.8, 0.1]]) L = sqrt(dx**2 + dy**2) or 1 # account for div by zero arrow[:, 0] *= L arrow[:, 1] *= width cx = float(dx) / L sx = float(dy) / L M = array([[cx, sx], [-sx, cx]]) verts = matrixmultiply(arrow, M) + [x, y] Polygon.__init__(self, [tuple(t) for t in verts], **kwargs)
def locate_label(self, linecontour, labelwidth): """find a good place to plot a label (relatively flat part of the contour) and the angle of rotation for the text object """ nsize = len(linecontour) if labelwidth > 1: xsize = int(ceil(nsize / labelwidth)) else: xsize = 1 if xsize == 1: ysize = nsize else: ysize = labelwidth XX = resize(asarray(linecontour)[:, 0], (xsize, ysize)) YY = resize(asarray(linecontour)[:, 1], (xsize, ysize)) yfirst = YY[:, 0] ylast = YY[:, -1] xfirst = XX[:, 0] xlast = XX[:, -1] s = ((reshape(yfirst, (xsize, 1)) - YY) * (reshape(xlast, (xsize, 1)) - reshape(xfirst, (xsize, 1))) - (reshape(xfirst, (xsize, 1)) - XX) * (reshape(ylast, (xsize, 1)) - reshape(yfirst, (xsize, 1)))) L = sqrt((xlast - xfirst)**2 + (ylast - yfirst)**2) dist = add.reduce(([(abs(s)[i] / L[i]) for i in range(xsize)]), -1) x, y, ind = self.get_label_coords(dist, XX, YY, ysize, labelwidth) #print 'ind, x, y', ind, x, y angle = arctan2(ylast - yfirst, xlast - xfirst) rotation = angle[ind] * 180 / pi if rotation > 90: rotation = rotation - 180 if rotation < -90: rotation = 180 + rotation # There must be a more efficient way... lc = [tuple(l) for l in linecontour] dind = lc.index((x, y)) #print 'dind', dind #dind = list(linecontour).index((x,y)) return x, y, rotation, dind
def locate_label(self, linecontour, labelwidth): """find a good place to plot a label (relatively flat part of the contour) and the angle of rotation for the text object """ nsize= len(linecontour) if labelwidth > 1: xsize = int(ceil(nsize/labelwidth)) else: xsize = 1 if xsize == 1: ysize = nsize else: ysize = labelwidth XX = resize(asarray(linecontour)[:,0],(xsize, ysize)) YY = resize(asarray(linecontour)[:,1],(xsize,ysize)) yfirst = YY[:,0] ylast = YY[:,-1] xfirst = XX[:,0] xlast = XX[:,-1] s = ( (reshape(yfirst, (xsize,1))-YY) * (reshape(xlast,(xsize,1)) - reshape(xfirst,(xsize,1))) - (reshape(xfirst,(xsize,1))-XX) * (reshape(ylast,(xsize,1)) - reshape(yfirst,(xsize,1))) ) L=sqrt((xlast-xfirst)**2+(ylast-yfirst)**2) dist = add.reduce(([(abs(s)[i]/L[i]) for i in range(xsize)]),-1) x,y,ind = self.get_label_coords(dist, XX, YY, ysize, labelwidth) #print 'ind, x, y', ind, x, y angle = arctan2(ylast - yfirst, xlast - xfirst) rotation = angle[ind]*180/pi if rotation > 90: rotation = rotation -180 if rotation < -90: rotation = 180 + rotation # There must be a more efficient way... lc = [tuple(l) for l in linecontour] dind = lc.index((x,y)) #print 'dind', dind #dind = list(linecontour).index((x,y)) return x,y, rotation, dind
def draw(self, renderer): renderer.open_group('regpolycollection') self._transform.freeze() self._transOffset.freeze() self.update_scalarmappable() scales = sqrt(self._sizes * self._dpi.get() / 72.0) if self._edgecolors == 'None': self._edgecolors = self._facecolors renderer.draw_regpoly_collection(self.clipbox, self._offsets, self._transOffset, self._verts, scales, self._facecolors, self._edgecolors, self._linewidths, self._antialiaseds) self._transform.thaw() self._transOffset.thaw() renderer.close_group('regpolycollection')
def array_example3(epsoutfile): x = (arange(200.0) - 100) / 10.0 y = (arange(200.0) - 100) / 10.0 r = sqrt(x[:, NewAxis] ** 2 + y ** 2) z = 5.0 * cos(r) colmap1 = ColMapper.ColorMapper("red") colmap1.exponent = 0.9 colmap1.invert = True colmap2 = ColMapper.ColorMapper("green") colmap2.exponent = 0.9 colmap3 = ColMapper.ColorMapper("green") colmap3.invert = True colmap3.exponent = 0.9 colmap4 = ColMapper.ColorMapper("blue") colmap4.exponent = 0.9 colmap = ColMapper.SegmentedColorMapping( [(-5.0, -2.5, colmap1), (-2.5, 0.0, colmap2), (0.0, 2.5, colmap3), (2.5, 5.0, colmap4)], -5.0, 5.0 ) # colmap = ColMapper.example_SegmentedColorMapping(min(ravel(z)),max(ravel(z))) lut = colmap.generate_lut() pilbitmap = ColMapper.Array2PIL(z, lut=lut) c = pyx.canvas.canvas() g = pyxgraph(xlimits=(min(x), max(x)), ylimits=(min(y), max(y)), width=6, height=6, key=False) g.pyxplot("y(x)=sin(x)+20", style="p") # FIXME: can't do empty plots! g.pyxbitmap(pilbitmap) c.insert(g) cb = pyxcolorbar(lut=lut, frame=g, pos=(1.1, 0.0), minvalue=min(ravel(z)), maxvalue=max(ravel(z))) c.insert(cb) pyxsave(c, epsoutfile)
def array_example2(epsoutfile): x = (arange(50.0)-25)/2.0 y = (arange(50.0)-25)/2.0 r = sqrt(x[:,NewAxis]**2+y**2) z = 5.0*cos(r) colmap = ColMapper.ColorMapper("yellow-red", exponent=0.55, brightness=0.5) lut = colmap.generate_lut() c = pyx.canvas.canvas() g = pyxgraph(xlimits=(min(x), max(x)), ylimits=(min(y), max(y)), width=6, height=6) g.pyxplot("y(x)=sin(x)", style="p") # FIXME: can't do empty plots! g.pyxplotarray(z, colmap=lut) c.insert(g) cb = pyxcolorbar(lut=lut, frame=g, pos=(1.1,0.0), minvalue=min(ravel(z)), maxvalue=max(ravel(z))) c.insert(cb) pyxsave(c, epsoutfile)
def draw(self, renderer): if not self.get_visible(): return renderer.open_group('regpolycollection') self._transform.freeze() self._transOffset.freeze() self.update_scalarmappable() self._update_verts() scales = sqrt(asarray(self._sizes) * self._dpi.get() / 72.0) if is_string_like(self._edgecolors) and self._edgecolors[:2] == 'No': #self._edgecolors = self._facecolors self._linewidths = (0, ) # print 'in draw(), self._offsets = %s' % (`self._offsets`) renderer.draw_regpoly_collection(self.clipbox, self._offsets, self._transOffset, self._verts, scales, self._facecolors, self._edgecolors, self._linewidths, self._antialiaseds) self._transform.thaw() self._transOffset.thaw() renderer.close_group('regpolycollection')
def pyxarrow(canvas, xystart, xyend, length=None, arrowstyle=None, graphcoords=False): """ xystart=(,) xyend=(,) are coordinates from [0.0, 1.0] relative to the canvas (or graph) area (values outside of this range are also allowed). If length is not None the direction is specified by xystart,xyend """ if arrowstyle == None: arrowstyle = [pyx.style.linewidth.THIck, pyx.deco.earrow.Large] # print "canvas.xpos",canvas.xpos # print "canvas.ypos",canvas.ypos # print "canvas.width",canvas.width # print "canvas.height",canvas.height if graphcoords == True: # canvas.pos only works after dolayout! xmin = canvas.axes["x"].axis.min xmax = canvas.axes["x"].axis.max ymin = canvas.axes["y"].axis.min ymax = canvas.axes["y"].axis.max if xmin == None or ymin == None or xmax == None or ymax == None: #if not canvas.did(canvas.dolayout): # canvas.dolayout() # print "forced dolayout here" x0, y0 = canvas.pos(xystart[0], xystart[1]) x1, y1 = canvas.pos(xyend[0], xyend[1]) else: def convertxy(position): """Convert from [0.0, 1.0] coordinates to canvas coordinates """ return ( (position[0]-xmin)/(xmax-xmin), (position[1]-ymin)/(ymax-ymin) ) def convertxy_logx(position): """Convert from [0.0, 1.0] coordinates to canvas coordinates on a logarithmic scale in x-direction""" return ( (log(position[0])-log(xmin))/(log(xmax)-log(xmin)), (position[1]-ymin)/(ymax-ymin) ) def convertxy_logy(position): """Convert from [0.0, 1.0] coordinates to canvas coordinates on a logarithmic scale in y-direction""" return ( (position[0]-xmin)/(xmax-xmin), (log(position[1])-log(ymin))/(log(ymax)-log(ymin)) ) def convertxy_loglog(position): """Convert from [0.0, 1.0] coordinates to canvas coordinates on a logarithmic scale in x- and y-direction""" return ( (log(position[0])-log(xmin))/(log(xmax)-log(xmin)), (log(position[1])-log(ymin))/(log(ymax)-log(ymin)) ) if canvas.xaxistype == "linear" and canvas.yaxistype == "linear": xystart_normalized = convertxy(xystart) xyend_normalized = convertxy(xyend) elif canvas.xaxistype == "log" and canvas.yaxistype == "linear": xystart_normalized = convertxy_logx(xystart) xyend_normalized = convertxy_logx(xyend) elif canvas.xaxistype == "linear" and canvas.yaxistype == "log": xystart_normalized = convertxy_logy(xystart) xyend_normalized = convertxy_logy(xyend) elif canvas.xaxistype == "log" and canvas.yaxistype == "log": xystart_normalized = convertxy_loglog(xystart) xyend_normalized = convertxy_loglog(xyend) else: xystart_normalized = convertxy(xystart) xyend_normalized = convertxy(xyend) x0 = canvas.xpos+xystart_normalized[0]*canvas.width x1 = canvas.xpos+xyend_normalized[0]*canvas.width y0 = canvas.ypos+xystart_normalized[1]*canvas.height y1 = canvas.ypos+xyend_normalized[1]*canvas.height else: x0 = canvas.xpos+xystart[0]*canvas.width x1 = canvas.xpos+xyend[0]*canvas.width y0 = canvas.ypos+xystart[1]*canvas.height y1 = canvas.ypos+xyend[1]*canvas.height if length != None: dx = xyend[0]-xystart[0] dy = xyend[1]-xystart[1] norm = sqrt(dx**2+dy**2) dx, dy = length*dx/norm, length*dy/norm x1 = canvas.xpos+xyend[0]*canvas.width+dx*canvas.width y1 = canvas.xpos+xyend[1]*canvas.height+dy*canvas.height path = pyx.path.path(pyx.path.moveto(x0, y0), pyx.path.lineto(x1, y1)) canvas.stroke(path, arrowstyle)
def update_coords(self, renderer): """Computes the actual x,y coordinates for text based on the input x,y and the dashlength. Since the rotation is with respect to the actual canvas's coordinates we need to map back and forth. """ dashx, dashy = self.get_position() dashlength = self.get_dashlength() # Shortcircuit this process if we don't have a dash if dashlength == 0.0: self._x, self._y = dashx, dashy return dashrotation = self.get_dashrotation() dashdirection = self.get_dashdirection() dashpad = self.get_dashpad() dashpush = self.get_dashpush() angle = get_rotation(dashrotation) theta = pi * (angle / 180.0 + dashdirection - 1) cos_theta, sin_theta = cos(theta), sin(theta) transform = self.get_transform() # Compute the dash end points # The 'c' prefix is for canvas coordinates cxy = array(transform.xy_tup((dashx, dashy))) cd = array([cos_theta, sin_theta]) c1 = cxy + dashpush * cd c2 = cxy + (dashpush + dashlength) * cd (x1, y1) = transform.inverse_xy_tup(tuple(c1)) (x2, y2) = transform.inverse_xy_tup(tuple(c2)) self.dashline.set_data((x1, x2), (y1, y2)) # We now need to extend this vector out to # the center of the text area. # The basic problem here is that we're "rotating" # two separate objects but want it to appear as # if they're rotated together. # This is made non-trivial because of the # interaction between text rotation and alignment - # text alignment is based on the bbox after rotation. # We reset/force both alignments to 'center' # so we can do something relatively reasonable. # There's probably a better way to do this by # embedding all this in the object's transformations, # but I don't grok the transformation stuff # well enough yet. we = Text.get_window_extent(self, renderer=renderer) w, h = we.width(), we.height() # Watch for zeros if sin_theta == 0.0: dx = w dy = 0.0 elif cos_theta == 0.0: dx = 0.0 dy = h else: tan_theta = sin_theta / cos_theta dx = w dy = w * tan_theta if dy > h or dy < -h: dy = h dx = h / tan_theta cwd = array([dx, dy]) / 2 cwd *= 1 + dashpad / sqrt(dot(cwd, cwd)) cw = c2 + (dashdirection * 2 - 1) * cwd self._x, self._y = transform.inverse_xy_tup(tuple(cw)) # Now set the window extent # I'm not at all sure this is the right way to do this. we = Text.get_window_extent(self, renderer=renderer) self._twd_window_extent = we.deepcopy() self._twd_window_extent.update(((c1[0], c1[1]), ), False) # Finally, make text align center Text.set_horizontalalignment(self, 'center') Text.set_verticalalignment(self, 'center')
def update_coords(self, renderer): """Computes the actual x,y coordinates for self._mytext based on the input x,y and the dashlength. Since the rotation is with respect to the actual canvas's coordinates we need to map back and forth. """ (x, y) = self.get_position() dashlength = self.get_dashlength() # Shortcircuit this process if we don't have a dash if dashlength == 0.0: self._mytext.set_position((x, y)) return dashrotation = self.get_dashrotation() dashdirection = self.get_dashdirection() dashpad = self.get_dashpad() dashpush = self.get_dashpush() transform = self.get_transform() angle = get_rotation(dashrotation) theta = pi*(angle/180.0+dashdirection-1) cos_theta, sin_theta = cos(theta), sin(theta) # Compute the dash end points # The 'c' prefix is for canvas coordinates cxy = array(transform.xy_tup((x, y))) cd = array([cos_theta, sin_theta]) c1 = cxy+dashpush*cd c2 = cxy+(dashpush+dashlength)*cd (x1, y1) = transform.inverse_xy_tup(tuple(c1)) (x2, y2) = transform.inverse_xy_tup(tuple(c2)) self.dashline.set_data((x1, x2), (y1, y2)) # We now need to extend this vector out to # the center of the text area. # The basic problem here is that we're "rotating" # two separate objects but want it to appear as # if they're rotated together. # This is made non-trivial because of the # interaction between text rotation and alignment - # text alignment is based on the bbox after rotation. # We reset/force both alignments to 'center' # so we can do something relatively reasonable. # There's probably a better way to do this by # embedding all this in the object's transformations, # but I don't grok the transformation stuff # well enough yet. we = self._mytext.get_window_extent(renderer=renderer) w, h = we.width(), we.height() # Watch for zeros if sin_theta == 0.0: dx = w dy = 0.0 elif cos_theta == 0.0: dx = 0.0 dy = h else: tan_theta = sin_theta/cos_theta dx = w dy = w*tan_theta if dy > h or dy < -h: dy = h dx = h/tan_theta cwd = array([dx, dy])/2 cwd *= 1+dashpad/sqrt(dot(cwd,cwd)) cw = c2+(dashdirection*2-1)*cwd self._mytext.set_position(transform.inverse_xy_tup(tuple(cw))) # Now set the window extent # I'm not at all sure this is the right way to do this. we = self._mytext.get_window_extent(renderer=renderer) self._window_extent = we.deepcopy() self._window_extent.update(((c1[0], c1[1]),), False) # Finally, make text align center self._mytext.set_horizontalalignment('center') self._mytext.set_verticalalignment('center')
def __init__( self, x, y, dx, dy, width=0.001, length_includes_head=False, head_width=None, head_length=None, shape="full", overhang=0, head_starts_at_zero=False, **kwargs ): """Returns a new Arrow. length_includes_head: True if head is counted in calculating the length. shape: ['full', 'left', 'right'] overhang: distance that the arrow is swept back (0 overhang means triangular shape). head_starts_at_zero: if True, the head starts being drawn at coordinate 0 instead of ending at coordinate 0. """ if head_width is None: head_width = 3 * width if head_length is None: head_length = 1.5 * head_width distance = sqrt(dx ** 2 + dy ** 2) if length_includes_head: length = distance else: length = distance + head_length if not length: verts = [] # display nothing if empty else: # start by drawing horizontal arrow, point at (0,0) hw, hl, hs, lw = head_width, head_length, overhang, width left_half_arrow = array( [ [0.0, 0.0], # tip [-hl, -hw / 2.0], # leftmost [-hl * (1 - hs), -lw / 2.0], # meets stem [-length, -lw / 2.0], # bottom left [-length, 0], ] ) # if we're not including the head, shift up by head length if not length_includes_head: left_half_arrow += [head_length, 0] # if the head starts at 0, shift up by another head length if head_starts_at_zero: left_half_arrow += [head_length / 2.0, 0] # figure out the shape, and complete accordingly if shape == "left": coords = left_half_arrow else: right_half_arrow = left_half_arrow * [1, -1] if shape == "right": coords = right_half_arrow elif shape == "full": coords = concatenate([left_half_arrow, right_half_arrow[::-1]]) else: raise ValueError, "Got unknown shape: %s" % shape cx = float(dx) / distance sx = float(dy) / distance M = array([[cx, sx], [-sx, cx]]) verts = matrixmultiply(coords, M) + (x + dx, y + dy) Polygon.__init__(self, map(tuple, verts), **kwargs)
def __init__(self, x, y, dx, dy, width=0.001, length_includes_head=False, \ head_width=None, head_length=None, shape='full', overhang=0, \ head_starts_at_zero=False,**kwargs): """Returns a new Arrow. length_includes_head: True if head is counted in calculating the length. shape: ['full', 'left', 'right'] overhang: distance that the arrow is swept back (0 overhang means triangular shape). head_starts_at_zero: if True, the head starts being drawn at coordinate 0 instead of ending at coordinate 0. """ if head_width is None: head_width = 3 * width if head_length is None: head_length = 1.5 * head_width distance = sqrt(dx**2 + dy**2) if length_includes_head: length = distance else: length = distance + head_length if not length: verts = [] #display nothing if empty else: #start by drawing horizontal arrow, point at (0,0) hw, hl, hs, lw = head_width, head_length, overhang, width left_half_arrow = array([ [0.0, 0.0], #tip [-hl, -hw / 2.0], #leftmost [-hl * (1 - hs), -lw / 2.0], #meets stem [-length, -lw / 2.0], #bottom left [-length, 0], ]) #if we're not including the head, shift up by head length if not length_includes_head: left_half_arrow += [head_length, 0] #if the head starts at 0, shift up by another head length if head_starts_at_zero: left_half_arrow += [head_length / 2.0, 0] #figure out the shape, and complete accordingly if shape == 'left': coords = left_half_arrow else: right_half_arrow = left_half_arrow * [1, -1] if shape == 'right': coords = right_half_arrow elif shape == 'full': coords = concatenate( [left_half_arrow, right_half_arrow[::-1]]) else: raise ValueError, "Got unknown shape: %s" % shape cx = float(dx) / distance sx = float(dy) / distance M = array([[cx, sx], [-sx, cx]]) verts = matrixmultiply(coords, M) + (x + dx, y + dy) Polygon.__init__(self, map(tuple, verts), **kwargs)
def mod(v): """3d vector length""" return nx.sqrt(v[0]**2 + v[1]**2 + v[2]**2)