def dist_point_to_segment(p, s0, s1): """ get the distance of a point to a segment. p, s0, s1 are xy sequences This algorithm from http://softsurfer.com/Archive/algorithm_0102/algorithm_0102.htm#Distance%20to%20Ray%20or%20Segment """ p = asarray(p, Float) s0 = asarray(s0, Float) s1 = asarray(s1, Float) v = s1 - s0 w = p - s0 c1 = dot(w,v); if ( c1 <= 0 ): return dist(p, s0); c2 = dot(v,v) if ( c2 <= c1 ): return dist(p, s1); b = c1 / c2 pb = s0 + b * v; return dist(p, pb)
def get_rotation_matrix(self, x0, y0): theta = pi/180.0*self.get_rotation() # translate x0,y0 to origin Torigin = array([ [1, 0, -x0], [0, 1, -y0], [0, 0, 1 ]]) # rotate by theta R = array([ [cos(theta), -sin(theta), 0], [sin(theta), cos(theta), 0], [0, 0, 1]]) # translate origin back to x0,y0 Tback = array([ [1, 0, x0], [0, 1, y0], [0, 0, 1 ]]) return dot(dot(Tback,R), Torigin)
def plot_surface(self, X, Y, Z, *args, **kwargs): had_data = self.has_data() rows, cols = Z.shape tX,tY,tZ = nx.transpose(X), nx.transpose(Y), nx.transpose(Z) rstride = cbook.popd(kwargs, 'rstride', 10) cstride = cbook.popd(kwargs, 'cstride', 10) # polys = [] boxes = [] for rs in nx.arange(0,rows,rstride): for cs in nx.arange(0,cols,cstride): ps = [] corners = [] for a,ta in [(X,tX),(Y,tY),(Z,tZ)]: ztop = a[rs][cs:min(cols-1,cs+cstride)] zleft = ta[min(cols-1,cs+cstride)][rs:min(rows,rs+rstride+1)] zbase = a[min(rows-1,rs+rstride)][cs:min(cols,cs+cstride+1):] zbase = zbase[::-1] zright = ta[cs][rs:min(rows-1,rs+rstride):] zright = zright[::-1] corners.append([ztop[0],ztop[-1],zbase[0],zbase[-1]]) z = nx.concatenate((ztop,zleft,zbase,zright)) ps.append(z) boxes.append(map(nx.array,zip(*corners))) polys.append(zip(*ps)) # lines = [] shade = [] for box in boxes: n = proj3d.cross(box[0]-box[1], box[0]-box[2]) n = n/proj3d.mod(n)*5 shade.append(nx.dot(n,[-1,-1,0.5])) lines.append((box[0],n+box[0])) # color = nx.array([0,0,1,1]) norm = normalize(min(shade),max(shade)) colors = [color * (0.5+norm(v)*0.5) for v in shade] for c in colors: c[3] = 1 polyc = art3d.Poly3DCollection(polys, facecolors=colors, *args, **kwargs) polyc._zsort = 1 self.add_collection(polyc) # self.auto_scale_xyz(X,Y,Z, had_data) return polyc
def _get_layout(self, renderer): # layout the xylocs in display coords as if angle = zero and # then rotate them around self._x, self._y key = self.get_prop_tup() if self.cached.has_key(key): return self.cached[key] horizLayout = [] pad =2 thisx, thisy = self._transform.xy_tup( (self._x, self._y) ) width = 0 height = 0 xmin, ymin = thisx, thisy if self.is_math_text(): lines = [self._text] else: lines = self._text.split('\n') whs = [] for line in lines: w,h = renderer.get_text_width_height( line, self._fontproperties, ismath=self.is_math_text()) if not len(line) and not self.is_math_text(): # approx the height of empty line with tall char tmp, h = renderer.get_text_width_height( 'T', self._fontproperties, ismath=False) whs.append( (w,h) ) offsety = h+pad horizLayout.append((line, thisx, thisy, w, h)) thisy -= offsety # now translate down by text height, window coords width = max(width, w) ymin = horizLayout[-1][2] ymax = horizLayout[0][2] + horizLayout[0][-1] height = ymax-ymin xmax = xmin + width # get the rotation matrix M = self.get_rotation_matrix(xmin, ymin) # the corners of the unrotated bounding box cornersHoriz = ( (xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin) ) offsetLayout = [] # now offset the individual text lines within the box if len(lines)>1: # do the multiline aligment malign = self._get_multialignment() for line, thisx, thisy, w, h in horizLayout: if malign=='center': offsetx = width/2.0-w/2.0 elif malign=='right': offsetx = width-w else: offsetx = 0 thisx += offsetx offsetLayout.append( (thisx, thisy )) else: # no additional layout needed offsetLayout = [ (thisx, thisy) for line, thisx, thisy, w, h in horizLayout] # now rotate the bbox cornersRotated = [dot(M,array([[thisx],[thisy],[1]])) for thisx, thisy in cornersHoriz] txs = [float(v[0][0]) for v in cornersRotated] tys = [float(v[1][0]) for v in cornersRotated] # compute the bounds of the rotated box xmin, xmax = min(txs), max(txs) ymin, ymax = min(tys), max(tys) width = xmax - xmin height = ymax - ymin # Now move the box to the targe position offset the display bbox by alignment halign = self._horizontalalignment valign = self._verticalalignment # compute the text location in display coords and the offsets # necessary to align the bbox with that location tx, ty = self._transform.xy_tup( (self._x, self._y) ) if halign=='center': offsetx = tx - (xmin + width/2.0) elif halign=='right': offsetx = tx - (xmin + width) else: offsetx = tx - xmin if valign=='center': offsety = ty - (ymin + height/2.0) elif valign=='top': offsety = ty - (ymin + height) else: offsety = ty - ymin xmin += offsetx xmax += offsetx ymin += offsety ymax += offsety bbox = lbwh_to_bbox(xmin, ymin, width, height) # now rotate the positions around the first x,y position xys = [dot(M,array([[thisx],[thisy],[1]])) for thisx, thisy in offsetLayout] tx = [float(v[0][0])+offsetx for v in xys] ty = [float(v[1][0])+offsety for v in xys] # now inverse transform back to data coords xys = [self._transform.inverse_xy_tup( xy ) for xy in zip(tx, ty)] xs, ys = zip(*xys) ret = bbox, zip(lines, whs, xs, ys) self.cached[key] = ret return ret
def _norm(x): "return sqrt(x dot x)" return numerix.mlab.sqrt(dot(x,x))
def dist(x,y): 'return the distance between two points' d = x-y return numerix.mlab.sqrt(dot(d,d))
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 _norm(x): "return sqrt(x dot x)" return numerix.mlab.sqrt(dot(x, x))
def dist(x, y): 'return the distance between two points' d = x - y return numerix.mlab.sqrt(dot(d, d))