def partition(tex_coord1, ver_coord1): tex_coord2, ver_coord2 = Pointset(3), Pointset(3) for iQuad in range(len(tex_coord1)/4): io = iQuad * 4 for i1 in range(4): for i2 in range(4): i3 = (i1 + i2)%4 tex_coord2.append( 0.5*(tex_coord1[io+i1] + tex_coord1[io+i3]) ) ver_coord2.append( 0.5*(ver_coord1[io+i1] + ver_coord1[io+i3]) ) #print('partition from %i to %i vertices' % (len(tex_coord1), len(tex_coord2))) return tex_coord2, ver_coord2
def partition(tex_coord1, ver_coord1): tex_coord2, ver_coord2 = Pointset(3), Pointset(3) for iQuad in range(len(tex_coord1) / 4): io = iQuad * 4 for i1 in range(4): for i2 in range(4): i3 = (i1 + i2) % 4 tex_coord2.append( 0.5 * (tex_coord1[io + i1] + tex_coord1[io + i3])) ver_coord2.append( 0.5 * (ver_coord1[io + i1] + ver_coord1[io + i3])) #print('partition from %i to %i vertices' % (len(tex_coord1), len(tex_coord2))) return tex_coord2, ver_coord2
def _AddLineAndLabel(self, text, yspacing=1.0, twoPoints=True): """ Add a line and label to our pool. """ # get y position index = len(self._wobjects) y = self._yoffset + yspacing * (index) # create label label = Label(self, text) label.bgcolor = '' label.position = self._xoffset * 2 + twoPoints * self._linelen, y label._Compile() label._PositionText() #y2 = label.position.h / 2 y2 = (label._deltay[1] - label._deltay[0]) / 2 # create 2-element pointset pp = Pointset(2) pp.append(self._xoffset, y + y2) if twoPoints: pp.append(self._xoffset + self._linelen, y + y2) # create line line = Line(self, pp) # line has no parent # return return line, label
def _NodesToLine(self, nodes, line, update=False): """ Convert nodes to a full 256 element line. """ # sort nodes nn = [n for n in nodes] nn.sort(key=lambda n:n.x) nodes = Pointset(2) for n in nn: nodes.append(n) # interpolate xx = np.linspace(0,1,256) if nodes: yy = np.interp(xx, nodes[:,0], nodes[:,1]) if np.isnan(yy[-1]): # happens when last two nodes on same pos yy[-1] = yy[-2] line[:,1] = yy else: line[:,1] = np.zeros((256,),dtype=np.float32) # no nodes if update: # Create colormap map = {} for i in range(4): nn = self._allNodes[i] tmp = [] for ii in range(len(nn)): tmp.append((nn[ii,0], 1-nn[ii,1])) if tmp: key = 'rgba'[i] map[key] = sorted(tmp, key=lambda x:x[0]) # Apply colormap to all registered objects if self.parent: for ob in self.parent.GetMapables(): ob._SetColormap(map) ob.Draw()
def _SliderCalcDots(self, event=None): # Init dots dots1, dots2 = Pointset(2), Pointset(2) # Get width height w,h = self.position.size # Fill pointsets if w > h: i = 5 while i < h-5: dots1.append(2,i); dots1.append(5,i) dots2.append(-2,i); dots2.append(-5,i) i += 3 else: i = 5 while i < w-5: dots1.append(i,2); dots1.append(i,5) dots2.append(i,-2); dots2.append(i,-5) i += 3 self._dots1, self._dots2 = dots1, dots2
def OnDraw(self): # Draw bg color and edges Box.OnDraw(self) # Margin d1 = 2 d2 = d1+1 # Get normalize limits t1, t2 = self._getNormalizedSliderLimits() # Get widget shape w, h = self.position.size # Calculate real dimensions of patch if w > h: x1, x2 = max(d2, t1*w), min(w-d1, t2*w) y1, y2 = d1, h-d2 # dots1 = self._dots1 + Point(x1, 0) dots2 = self._dots2 + Point(x2, 0) # diff = abs(x1-x2) # self._label.textAngle = 0 else: x1, x2 = d2, w-d1 y1, y2 = max(d1, t1*h), min(h-d2, t2*h) # dots1 = self._dots1 + Point(0, y1) dots2 = self._dots2 + Point(0, y2) # diff = abs(y1-y2) # self._label.textAngle = -90 # Draw slider bit clr = self._frontColor gl.glColor(clr[0], clr[1], clr[2], 1.0) # gl.glBegin(gl.GL_POLYGON) gl.glVertex2f(x1,y1) gl.glVertex2f(x1,y2) gl.glVertex2f(x2,y2) gl.glVertex2f(x2,y1) gl.glEnd() # Draw dots if True: # Prepare gl.glColor(0,0,0,1) gl.glPointSize(1) gl.glDisable(gl.GL_POINT_SMOOTH) # Draw gl.glEnableClientState(gl.GL_VERTEX_ARRAY) if isinstance(self, RangeSlider) and diff>5: gl.glVertexPointerf(dots1.data) gl.glDrawArrays(gl.GL_POINTS, 0, len(dots1)) if diff>5: gl.glVertexPointerf(dots2.data) gl.glDrawArrays(gl.GL_POINTS, 0, len(dots2)) gl.glDisableClientState(gl.GL_VERTEX_ARRAY) if self._showTicks: # Reset color to black gl.glColor(0,0,0,1) # Draw ticks if w>h: p0 = Point(0, h) p1 = Point(w, h) delta = Point(0,3) halign, valign = 0, 0 xoffset, yoffset = -8, -2 else: p0 = Point(w, h) p1 = Point(w, 0) delta = Point(3,0) halign, valign = -1, 0 xoffset, yoffset = 5, -8 # Get tickmarks ticks, ticksPos, ticksText = GetTicks(p0, p1, self._fullRange) newLabelPool = {} linePieces = Pointset(2) for tick, pos, text in zip(ticks, ticksPos, ticksText): pos2 = pos + delta # Add line piece linePieces.append(pos); linePieces.append(pos2) # Create or reuse label if tick in self._labelPool: label = self._labelPool.pop(tick) else: label = Label(self, ' '+text+' ') label.bgcolor = '' # Position label and set text alignment newLabelPool[tick] = label label.halign, label.valign = halign, valign label.position.x = pos2.x + xoffset label.position.w = 16 label.position.y = pos2.y + yoffset # Clean up label pool for label in self._labelPool.values(): label.Destroy() self._labelPool = newLabelPool # Draw line pieces gl.glLineWidth(1) gl.glEnableClientState(gl.GL_VERTEX_ARRAY) gl.glVertexPointerf(linePieces.data) gl.glDrawArrays(gl.GL_LINES, 0, len(linePieces)) gl.glDisableClientState(gl.GL_VERTEX_ARRAY)
def solidBox(translation=None, scaling=None, direction=None, rotation=None, axesAdjust=True, axes=None): """ solidBox(translation=None, scaling=None, direction=None, rotation=None, axesAdjust=True, axes=None) Creates a solid cube (or box if you scale it) centered at the origin. Returns an OrientableMesh. Parameters ---------- Note that translation, scaling, and direction can also be given using a Point instance. translation : (dx, dy, dz), optional The translation in world units of the created world object. scaling: (sx, sy, sz), optional The scaling in world units of the created world object. direction: (nx, ny, nz), optional Normal vector that indicates the direction of the created world object. rotation: scalar, optional The anle (in degrees) to rotate the created world object around its direction vector. axesAdjust : bool If True, this function will call axes.SetLimits(), and set the camera type to 3D. If daspectAuto has not been set yet, it is set to False. axes : Axes instance Display the bars in the given axes, or the current axes if not given. """ # Create vertices of a cube pp = Pointset(3) # Bottom pp.append(-0.5, -0.5, -0.5) pp.append(+0.5, -0.5, -0.5) pp.append(+0.5, +0.5, -0.5) pp.append(-0.5, +0.5, -0.5) # Top pp.append(-0.5, -0.5, +0.5) pp.append(-0.5, +0.5, +0.5) pp.append(+0.5, +0.5, +0.5) pp.append(+0.5, -0.5, +0.5) # Init vertices and normals vertices = Pointset(3) normals = Pointset(3) # Create vertices for i in [3, 2, 1, 0]: # Top vertices.append(pp[i]) normals.append(0, 0, -1) for i in [7, 6, 5, 4]: # Bottom vertices.append(pp[i]) normals.append(0, 0, +1) for i in [5, 6, 2, 3]: # Front vertices.append(pp[i]) normals.append(0, +1, 0) for i in [1, 7, 4, 0]: # Back vertices.append(pp[i]) normals.append(0, -1, 0) for i in [4, 5, 3, 0]: # Left vertices.append(pp[i]) normals.append(-1, 0, 0) for i in [2, 6, 7, 1]: # Right vertices.append(pp[i]) normals.append(+1, 0, 0) ## Visualize # Create axes if axes is None: axes = vv.gca() # Create mesh and set orientation m = vv.OrientableMesh(axes, vertices, None, normals, verticesPerFace=4) # if translation is not None: m.translation = translation if scaling is not None: m.scaling = scaling if direction is not None: m.direction = direction if rotation is not None: m.rotation = rotation # Adjust axes if axesAdjust: if axes.daspectAuto is None: axes.daspectAuto = False axes.cameraType = '3d' axes.SetLimits() # Done axes.Draw() return m
plain color, colormaps and texture. On the website, this example also demonstrates the fly camera to fly through the mesh objects. """ import numpy as np import visvis as vv from visvis.pypoints import Point, Pointset vv.figure() a = vv.gca() # Define points for the line pp = Pointset(3) pp.append(0, 0, 0) pp.append(0, 1, 0) pp.append(1, 2, 0) pp.append(0, 2, 1) # Create all solids box = vv.solidBox((0, 0, 0)) sphere = vv.solidSphere((3, 0, 0)) cone = vv.solidCone((6, 0, 0)) pyramid = vv.solidCone((9, 0, 0), N=4) # a cone with 4 faces is a pyramid cylinder = vv.solidCylinder((0, 3, 0), (1, 1, 2)) ring = vv.solidRing((3, 3, 0)) teapot = vv.solidTeapot((6, 3, 0)) line = vv.solidLine(pp + Point(9, 3, 0), radius=0.2) # Let's put a face on that cylinder
def _DragCalcDots(self, event=None): w,h = self.position.size dots = Pointset(2) # dots.append(3,3); dots.append(3,6); dots.append(3,9) dots.append(6,3); dots.append(6,6); dots.append(6,9) dots.append(9,3); dots.append(9,6); dots.append(9,9) # dots.append(w-3, h-3); dots.append(w-3, h-6); dots.append(w-3, h-9) dots.append(w-6, h-3); dots.append(w-6, h-6); dots.append(w-9, h-3); self._dots = dots
def solidRing(translation=None, scaling=None, direction=None, rotation=None, thickness=0.25, N=16, M=16, axesAdjust=True, axes=None): """ solidRing(translation=None, scaling=None, direction=None, rotation=None, thickness=0.25, N=16, M=16, axesAdjust=True, axes=None) Creates a solid ring with quad faces oriented at the origin. Returns an OrientableMesh instance. Parameters ---------- Note that translation, scaling, and direction can also be given using a Point instance. translation : (dx, dy, dz), optional The translation in world units of the created world object. scaling: (sx, sy, sz), optional The scaling in world units of the created world object. direction: (nx, ny, nz), optional Normal vector that indicates the direction of the created world object. rotation: scalar, optional The anle (in degrees) to rotate the created world object around its direction vector. thickness : scalar The tickness of the ring, represented as a fraction of the radius. N : int The number of subdivisions around its axis. If smaller than 8, flat shading is used instead of smooth shading. M : int The number of subdivisions along its axis. If smaller than 8, flat shading is used instead of smooth shading. axesAdjust : bool If True, this function will call axes.SetLimits(), and set the camera type to 3D. If daspectAuto has not been set yet, it is set to False. axes : Axes instance Display the bars in the given axes, or the current axes if not given. """ # Note that the number of vertices around the axis is N+1. This # would not be necessary per see, but it helps create a nice closed # texture when it is mapped. There are N number of faces though. # Similarly, to obtain M faces along the axis, we need M+1 # vertices. # Quick access pi2 = np.pi * 2 cos = np.cos sin = np.sin sl = M + 1 # Determine where the stitch is, depending on M if M <= 8: rotOffset = 0.5 / M else: rotOffset = 0.0 # Calculate vertices, normals and texcords vertices = Pointset(3) normals = Pointset(3) texcords = Pointset(2) # Cone for n in range(N + 1): v = float(n) / N a = pi2 * v # Obtain outer and center position of "tube" po = Point(sin(a), cos(a), 0) pc = po * (1.0 - 0.5 * thickness) # Create two vectors that span the the circle orthogonal to the tube p1 = (pc - po) p2 = Point(0, 0, 0.5 * thickness) # Sample around tube for m in range(M + 1): u = float(m) / (M) b = pi2 * (u + rotOffset) dp = cos(b) * p1 + sin(b) * p2 vertices.append(pc + dp) normals.append(dp.normalize()) texcords.append(v, u) # Calculate indices indices = [] for j in range(N): for i in range(M): #indices.extend([j*sl+i, j*sl+i+1, (j+1)*sl+i+1, (j+1)*sl+i]) indices.extend([(j + 1) * sl + i, (j + 1) * sl + i + 1, j * sl + i + 1, j * sl + i]) # Make indices a numpy array indices = np.array(indices, dtype=np.uint32) ## Visualize # Create axes if axes is None: axes = vv.gca() # Create mesh m = vv.OrientableMesh(axes, vertices, indices, normals, values=texcords, verticesPerFace=4) # if translation is not None: m.translation = translation if scaling is not None: m.scaling = scaling if direction is not None: m.direction = direction if rotation is not None: m.rotation = rotation # If necessary, use flat shading if N < 8 or M < 8: m.faceShading = 'flat' # Adjust axes if axesAdjust: if axes.daspectAuto is None: axes.daspectAuto = False axes.cameraType = '3d' axes.SetLimits() # Done axes.Draw() return m
def lineToMesh(pp, radius, vertex_num, values=None): """ lineToMesh(pp, radius, vertex_num, values=None) From a line, create a mesh that represents the line as a tube with given diameter. Returns a BaseMesh instance. Parameters ---------- pp : 3D Pointset The points along the line. If the first and last point are the same, the mesh-line is closed. radius : scalar The radius of the tube that is created. Radius can also be a sequence of values (containing a radius for each point). vertex_num : int The number of vertices to create along the circumference of the tube. values : list or numpy array (optional) A value per point. Can be Nx1, Nx2, Nx3 or Nx4. A list of scalars can also be given. The values are propagated to the mesh vertices and supplied as input to the Mesh constructor. This allows for example to define the color for the tube. """ # we need this quite a bit pi = np.pi # process radius if hasattr(radius, '__len__'): if len(radius) != len(pp): raise ValueError('Len of radii much match len of points.') else: radius = np.array(radius, dtype=np.float32) else: radius = radius*np.ones((len(pp),), dtype=np.float32) # calculate vertex points for 2D circle angles = np.arange(0, pi*2-0.0001, pi*2/vertex_num) angle_cos = np.cos(angles) angle_sin = np.sin(angles) vertex_num2 = len(angles) # just to be sure # calculate distance between two line pieces (for smooth cylinders) dists = pp[1:].distance(pp[:-1]) bufdist = min( radius.max(), dists.min()/2.2) # check if line is closed lclosed = (pp[0]==pp[-1]) # calculate normal vectors on each line point normals = pp[:-1].subtract( pp[1:] ) if lclosed: normals.append( pp[0]-pp[1] ) else: normals.append( pp[-2]-pp[-1] ) normals = -1 * normals.normalize() # create list to store vertices vertices = Pointset(3) surfaceNormals = Pointset(3) # And a list for the values if values is None: vvalues = None nvalues = 0 elif isinstance(values, list): if len(values) != len(pp): raise ValueError('There must be as many values as points.') vvalues = Pointset(1) elif isinstance(values, np.ndarray): if values.ndim != 2: raise ValueError('Values must be Nx1, Nx2, Nx3 or Nx4.') if values.shape[0] != len(pp): raise ValueError('There must be as many values as points.') vvalues = Pointset(values.shape[1]) elif vv.pypoints.is_Pointset(values): if values.ndim > 4: raise ValueError('Can specify one to four values per point.') if len(values) != len(pp): raise ValueError('There must be as many values as points.') vvalues = Pointset(values.ndim) else: raise ValueError('Invalid value for values.') # Number of triangelized cylinder elements added to plot the 3D line n_cylinders = 0 # Init a and b a, b = Point(0,0,1), Point(0,1,0) # Calculate the 3D circle coordinates of the first circle/cylinder a,b = getSpanVectors(normals[0], a, b) circm = getCircle(angle_cos, angle_sin, a, b); # If not a closed line, add half sphere made with 5 cylinders at line start if not lclosed: for j in range(5,0,-1): # Translate the circle on it's position on the line r = (1-(j/5.0)**2)**0.5 circmp = float(r*radius[0])*circm + (pp[0]-(j/5.0)*bufdist*normals[0]) # Calc normals circmn = ( pp[0].subtract(circmp) ).normalize() # Store the vertex list vertices.extend( circmp ) surfaceNormals.extend( -1*circmn ) if vvalues is not None: for iv in range(vertex_num2): vvalues.append(values[0]) n_cylinders += 1 # Loop through all line pieces for i in range(len(pp)-1): ## Create main cylinder between two line points # which consists of two connected circles. # get normal and point normal1 = normals[i] point1 = pp[i] # calculate the 3D circle coordinates a,b = getSpanVectors(normal1, a, b) circm = getCircle(angle_cos, angle_sin, a, b) # Translate the circle, and store circmp = float(radius[i])*circm + (point1+bufdist*normal1) vertices.extend( circmp ) surfaceNormals.extend( circm ) if vvalues is not None: for iv in range(vertex_num2): vvalues.append(values[i]) n_cylinders += 1 # calc second normal and line normal2 = normals[i+1] point2 = pp[i+1] # Translate the circle, and store circmp = float(radius[i+1])*circm + (point2-bufdist*normal1) vertices.extend( circmp ) surfaceNormals.extend( circm ) if vvalues is not None: for iv in range(vertex_num2): vvalues.append(values[i+1]) n_cylinders += 1 ## Create in between circle to smoothly connect line pieces if not lclosed and i == len(pp)-2: break # get normal and point normal12 = (normal1 + normal2).normalize() tmp = (point2+bufdist*normal2) + (point2-bufdist*normal1) point12 = 0.5858*point2 + 0.4142*(0.5*tmp) # Calculate the 3D circle coordinates a,b = getSpanVectors(normal12, a, b) circm = getCircle(angle_cos, angle_sin, a, b); # Translate the circle, and store circmp = float(radius[i+1])*circm + point12 vertices.extend( circmp ) surfaceNormals.extend( circm ) if vvalues is not None: for iv in range(vertex_num2): vvalues.append( 0.5*(values[i]+values[i+1]) ) n_cylinders += 1 # If not a closed line, add half sphere made with 5 cylinders at line start # Otherwise add the starting circle to the line end. if not lclosed: for j in range(0,6): # Translate the circle on it's position on the line r = (1-(j/5.0)**2)**0.5 circmp = float(r*radius[-1])*circm + (pp[-1]+(j/5.0)*bufdist*normals[-1]) # Calc normals circmn = ( pp[-1].subtract(circmp) ).normalize() # Store the vertex list vertices.extend( circmp ) surfaceNormals.extend( -1*circmn ) if vvalues is not None: for iv in range(vertex_num2): vvalues.append(values[-1]) n_cylinders += 1 else: # get normal and point normal1 = normals[-1] point1 = pp[-1] # calculate the 3D circle coordinates a,b = getSpanVectors(normal1, a, b) circm = getCircle(angle_cos, angle_sin, a, b) # Translate the circle, and store circmp = float(radius[0])*circm + (point1+bufdist*normal1) vertices.extend( circmp ) surfaceNormals.extend( circm ) if vvalues is not None: for iv in range(vertex_num2): vvalues.append(values[-1]) n_cylinders += 1 # almost done, determine quad faces ... # define single faces #firstFace = [0, 1, vertex_num+1, vertex_num] #lastFace = [vertex_num-1, 0, vertex_num, 2*vertex_num-1] firstFace = [vertex_num, vertex_num+1, 1, 0] lastFace = [2*vertex_num-1, vertex_num, 0, vertex_num-1] # define single round oneRound = [] for i in range(vertex_num-1): oneRound.extend( [val+i for val in firstFace] ) oneRound.extend(lastFace) oneRound = np.array(oneRound, dtype=np.uint32) # calculate face data parts = [] for i in range(n_cylinders-1): parts.append(oneRound+i*vertex_num) faces = np.concatenate(parts) faces.shape = faces.shape[0]/4, 4 # Done! return BaseMesh(vertices, faces, surfaceNormals, vvalues)
def _DragCalcDots(self, event=None): w, h = self.position.size dots = Pointset(2) # dots.append(3, 3) dots.append(3, 6) dots.append(3, 9) dots.append(6, 3) dots.append(6, 6) dots.append(6, 9) dots.append(9, 3) dots.append(9, 6) dots.append(9, 9) # dots.append(w - 3, h - 3) dots.append(w - 3, h - 6) dots.append(w - 3, h - 9) dots.append(w - 6, h - 3) dots.append(w - 6, h - 6) dots.append(w - 9, h - 3) self._dots = dots
def plot(data1, data2=None, data3=None, lw=1, lc='b', ls="-", mw=7, mc='b', ms='', mew=1, mec='k', alpha=1, axesAdjust=True, axes=None, **kwargs): """ plot(*args, lw=1, lc='b', ls="-", mw=7, mc='b', ms='', mew=1, mec='k', alpha=1, axesAdjust=True, axes=None): Plot 1, 2 or 3 dimensional data and return the Line object. Usage ----- * plot(Y, ...) plots a 1D signal, with the values plotted along the y-axis * plot(X, Y, ...) also supplies x coordinates * plot(X, Y, Z, ...) also supplies z coordinates * plot(P, ...) plots using a Point or Pointset instance Keyword arguments ----------------- (The longer names for the line properties can also be used) lw : scalar lineWidth. The width of the line. If zero, no line is drawn. mw : scalar markerWidth. The width of the marker. If zero, no marker is drawn. mew : scalar markerEdgeWidth. The width of the edge of the marker. lc : 3-element tuple or char lineColor. The color of the line. A tuple should represent the RGB values between 0 and 1. If a char is given it must be one of 'rgbmcywk', for reg, green, blue, magenta, cyan, yellow, white, black, respectively. mc : 3-element tuple or char markerColor. The color of the marker. See lineColor. mec : 3-element tuple or char markerEdgeColor. The color of the edge of the marker. ls : string lineStyle. The style of the line. (See below) ms : string markerStyle. The style of the marker. (See below) axesAdjust : bool If axesAdjust==True, this function will call axes.SetLimits(), set the camera type to 2D when plotting 2D data and to 3D when plotting 3D data. If daspectAuto has not been set yet, it is set to True. axes : Axes instance Display the image in this axes, or the current axes if not given. Line styles ----------- * Solid line: '-' * Dotted line: ':' * Dashed line: '--' * Dash-dot line: '-.' or '.-' * A line that is drawn between each pair of points: '+' * No line: '' or None. Marker styles ------------- * Plus: '+' * Cross: 'x' * Square: 's' * Diamond: 'd' * Triangle (pointing up, down, left, right): '^', 'v', '<', '>' * Pentagram star: 'p' or '*' * Hexgram: 'h' * Point/cirle: 'o' or '.' * No marker: '' or None """ # create a dict from the properties and combine with kwargs tmp = { 'lineWidth': lw, 'lineColor': lc, 'lineStyle': ls, 'markerWidth': mw, 'markerColor': mc, 'markerStyle': ms, 'markerEdgeWidth': mew, 'markerEdgeColor': mec } for i in tmp: if not i in kwargs: kwargs[i] = tmp[i] # init dimension variable camDim = 0 ## create the data if is_Pointset(data1): pp = data1 elif is_Point(data1): pp = Pointset(data1.ndim) pp.append(data1) else: if data1 is None: raise Exception("The first argument cannot be None!") data1 = makeArray(data1) d3 = data3 if data3 is None: data3 = 0.1 * np.ones(data1.shape) camDim = 2 else: camDim = 3 data3 = makeArray(data3) if data2 is None: if d3 is not None: tmp = "third argument in plot() ignored, as second not given." print "Warning: " + tmp # y data is given, xdata must be a range starting from 1 data2 = data1 data1 = np.arange(1, data2.shape[0] + 1) data3 = 0.1 * np.ones(data2.shape) else: data2 = makeArray(data2) # check dimensions L = data1.size if L != data2.size or L != data3.size: raise Exception("Array dimensions do not match! %i vs %i vs %i" % (data1.size, data2.size, data3.size)) # build points data1 = data1.reshape((data1.size, 1)) data2 = data2.reshape((data2.size, 1)) data3 = data3.reshape((data3.size, 1)) tmp = data1, data2, data3 pp = Pointset(np.concatenate(tmp, 1)) #pp.points = np.empty((L,3)) #pp.points[:,0] = data1.ravel() #pp.points[:,1] = data2.ravel() #pp.points[:,2] = data3.ravel() # Process camdim for given points or pointsets if not camDim: camDim = pp.ndim ## create the line if axes is None: axes = vv.gca() l = vv.Line(axes, pp) l.lw = kwargs['lineWidth'] l.lc = kwargs['lineColor'] l.ls = kwargs['lineStyle'] l.mw = kwargs['markerWidth'] l.mc = kwargs['markerColor'] l.ms = kwargs['markerStyle'] l.mew = kwargs['markerEdgeWidth'] l.mec = kwargs['markerEdgeColor'] l.alpha = alpha ## done... if axesAdjust: if axes.daspectAuto is None: axes.daspectAuto = True axes.cameraType = str(camDim) + 'd' axes.SetLimits() axes.Draw() return l
def _GetLimits(self, *args): """ _GetLimits(self, x1, x2, y1, y2, z1, z2) Get the limits in world coordinates between which the object exists. This is used by the Axes class to set the camera correctly. If None is returned, the limits are undefined. Inheriting Wobject classes should overload this method. However, they can use this method to take all transformations into account by giving the cornerpoints of the untransformed object. Returns a 3 element tuple of vv.Range instances: xlim, ylim, zlim. """ # Examine args if not args: minx, maxx, miny, maxy, minz, maxz = [], [], [], [], [], [] elif len(args) == 6: minx, maxx, miny, maxy, minz, maxz = tuple([[arg] for arg in args]) else: raise ValueError("_Getlimits expects 0 or 6 arguments.") # Get limits of children for ob in self.children: tmp = ob._GetLimits() if tmp is not None: limx, limy, limz = tmp minx.append(limx.min); maxx.append(limx.max) miny.append(limy.min); maxy.append(limy.max) minz.append(limz.min); maxz.append(limz.max) # Do we have limits? if not (minx and maxx and miny and maxy and minz and maxz): return None # Take min and max x1, y1, z1 = tuple([min(val) for val in [minx, miny, minz]]) x2, y2, z2 = tuple([max(val) for val in [maxx, maxy, maxz]]) # Make pointset of eight cornerpoints pp = Pointset(3) for x in [x1, x2]: for y in [y1, y2]: for z in [z1, z2]: pp.append(x,y,z) # Transform these points for i in range(len(pp)): p = pp[i] for t in reversed(self._transformations): if isinstance(t, Transform_Translate): p.x += t.dx p.y += t.dy p.z += t.dz elif isinstance(t, Transform_Scale): p.x *= t.sx p.y *= t.sy p.z *= t.sz elif isinstance(t, Transform_Rotate): angle = float(t.angle * np.pi / 180.0) q = Quaternion.create_from_axis_angle(angle, t.ax, t.ay, t.az) p = q.rotate_point(p) # Update pp[i] = p # Return limits xlim = misc.Range( pp[:,0].min(), pp[:,0].max() ) ylim = misc.Range( pp[:,1].min(), pp[:,1].max() ) zlim = misc.Range( pp[:,2].min(), pp[:,2].max() ) return xlim, ylim, zlim
def solidRing(translation=None, scaling=None, direction=None, rotation=None, thickness=0.25, N=16, M=16, axesAdjust=True, axes=None): """ solidRing(translation=None, scaling=None, direction=None, rotation=None, thickness=0.25, N=16, M=16, axesAdjust=True, axes=None) Creates a solid ring with quad faces oriented at the origin. Returns an OrientableMesh instance. Parameters ---------- Note that translation, scaling, and direction can also be given using a Point instance. translation : (dx, dy, dz), optional The translation in world units of the created world object. scaling: (sx, sy, sz), optional The scaling in world units of the created world object. direction: (nx, ny, nz), optional Normal vector that indicates the direction of the created world object. rotation: scalar, optional The anle (in degrees) to rotate the created world object around its direction vector. thickness : scalar The tickness of the ring, represented as a fraction of the radius. N : int The number of subdivisions around its axis. If smaller than 8, flat shading is used instead of smooth shading. M : int The number of subdivisions along its axis. If smaller than 8, flat shading is used instead of smooth shading. axesAdjust : bool If True, this function will call axes.SetLimits(), and set the camera type to 3D. If daspectAuto has not been set yet, it is set to False. axes : Axes instance Display the bars in the given axes, or the current axes if not given. """ # Note that the number of vertices around the axis is N+1. This # would not be necessary per see, but it helps create a nice closed # texture when it is mapped. There are N number of faces though. # Similarly, to obtain M faces along the axis, we need M+1 # vertices. # Quick access pi2 = np.pi*2 cos = np.cos sin = np.sin sl = M+1 # Determine where the stitch is, depending on M if M<=8: rotOffset = 0.5/M else: rotOffset = 0.0 # Calculate vertices, normals and texcords vertices = Pointset(3) normals = Pointset(3) texcords = Pointset(2) # Cone for n in range(N+1): v = float(n)/N a = pi2 * v # Obtain outer and center position of "tube" po = Point(sin(a), cos(a), 0) pc = po * (1.0-0.5*thickness) # Create two vectors that span the the circle orthogonal to the tube p1 = (pc-po) p2 = Point(0, 0, 0.5*thickness) # Sample around tube for m in range(M+1): u = float(m) / (M) b = pi2 * (u+rotOffset) dp = cos(b) * p1 + sin(b) * p2 vertices.append(pc+dp) normals.append(dp.normalize()) texcords.append(v,u) # Calculate indices indices = [] for j in range(N): for i in range(M): #indices.extend([j*sl+i, j*sl+i+1, (j+1)*sl+i+1, (j+1)*sl+i]) indices.extend([(j+1)*sl+i, (j+1)*sl+i+1, j*sl+i+1, j*sl+i]) # Make indices a numpy array indices = np.array(indices, dtype=np.uint32) ## Visualize # Create axes if axes is None: axes = vv.gca() # Create mesh m = vv.OrientableMesh(axes, vertices, indices, normals, values=texcords, verticesPerFace=4) # if translation is not None: m.translation = translation if scaling is not None: m.scaling = scaling if direction is not None: m.direction = direction if rotation is not None: m.rotation = rotation # If necessary, use flat shading if N<8 or M<8: m.faceShading = 'flat' # Adjust axes if axesAdjust: if axes.daspectAuto is None: axes.daspectAuto = False axes.cameraType = '3d' axes.SetLimits() # Done axes.Draw() return m
def polarplot(data1, data2=None, inRadians=False, lw=1, lc='b', ls="-", mw=7, mc='b', ms='', mew=1, mec='k', alpha=1, axesAdjust=True, axes=None, **kwargs): """ polarplot(*args, inRadians=False, lw=1, lc='b', ls="-", mw=7, mc='b', ms='', mew=1, mec='k', alpha=1, axesAdjust=True, axes=None): Plot 2D polar data, using a polar axis to draw a polar grid. Usage ----- * plot(Y, ...) plots a 1D polar signal. * plot(X, Y, ...) also supplies angular coordinates * plot(P, ...) plots using a Point or Pointset instance Keyword arguments ----------------- (The longer names for the line properties can also be used) lw : scalar lineWidth. The width of the line. If zero, no line is drawn. mw : scalar markerWidth. The width of the marker. If zero, no marker is drawn. mew : scalar markerEdgeWidth. The width of the edge of the marker. lc : 3-element tuple or char lineColor. The color of the line. A tuple should represent the RGB values between 0 and 1. If a char is given it must be one of 'rgbmcywk', for reg, green, blue, magenta, cyan, yellow, white, black, respectively. mc : 3-element tuple or char markerColor. The color of the marker. See lineColor. mec : 3-element tuple or char markerEdgeColor. The color of the edge of the marker. ls : string lineStyle. The style of the line. (See below) ms : string markerStyle. The style of the marker. (See below) axesAdjust : bool If axesAdjust==True, this function will call axes.SetLimits(), and set the camera type to 2D. axes : Axes instance Display the image in this axes, or the current axes if not given. Line styles ----------- * Solid line: '-' * Dotted line: ':' * Dashed line: '--' * Dash-dot line: '-.' or '.-' * A line that is drawn between each pair of points: '+' * No line: '' or None. Marker styles ------------- * Plus: '+' * Cross: 'x' * Square: 's' * Diamond: 'd' * Triangle (pointing up, down, left, right): '^', 'v', '<', '>' * Pentagram star: 'p' or '*' * Hexgram: 'h' * Point/cirle: 'o' or '.' * No marker: '' or None Polar axis ---------- This polar axis has a few specialized methods for adjusting the polar plot. Access these via vv.gca().axis. * SetLimits(thetaRange, radialRange) * thetaRange, radialRange = GetLimits() * angularRefPos: Get and Set methods for the relative screen angle of the 0 degree polar reference. Default is 0 degs which corresponds to the positive x-axis (y =0) * isCW: Get and Set methods for the sense of rotation CCW or CW. This method takes/returns a bool (True if the default CW). Interaction ----------- * Drag mouse up/down to translate radial axis. * Drag mouse left/right to rotate angular ref position. * Drag mouse + shift key up/down to rescale radial axis (min R fixed). """ # create a dict from the properties and combine with kwargs tmp = {'lineWidth': lw, 'lineColor': lc, 'lineStyle': ls, 'markerWidth': mw, 'markerColor': mc, 'markerStyle': ms, 'markerEdgeWidth': mew, 'markerEdgeColor': mec} for i in tmp: if not i in kwargs: kwargs[i] = tmp[i] ## create the data if is_Pointset(data1): pp = data1 elif is_Point(data1): pp = Pointset(data1.ndim) pp.append(data1) else: if data1 is None: raise ValueError("The first argument cannot be None!") data1 = makeArray(data1) if data2 is None: # R data is given, thetadata must be # a range starting from 0 degrees data2 = data1 data1 = np.arange(0, data2.shape[0]) else: data2 = makeArray(data2) # check dimensions L = data1.size if L != data2.size: raise ValueError("Array dimensions do not match! %i vs %i " % (data1.size, data2.size)) # build points data1 = data1.reshape((data1.size, 1)) data2 = data2.reshape((data2.size, 1)) if not inRadians: data1 = np.pi * data1 / 180.0 ## create the line if axes is None: axes = vv.gca() axes.axisType = 'polar' fig = axes.GetFigure() l = PolarLine(axes, data1, data2) l.lw = kwargs['lineWidth'] l.lc = kwargs['lineColor'] l.ls = kwargs['lineStyle'] l.mw = kwargs['markerWidth'] l.mc = kwargs['markerColor'] l.ms = kwargs['markerStyle'] l.mew = kwargs['markerEdgeWidth'] l.mec = kwargs['markerEdgeColor'] l.alpha = alpha ## almost done... # Init axis # axes.axis.SetLimits() if axesAdjust: if axes.daspectAuto is None: axes.daspectAuto = True axes.cameraType = '2d' axes.SetLimits() # Subsribe after-draw event handler # (unsubscribe first in case we do multiple plots) fig.eventAfterDraw.Unbind(_SetLimitsAfterDraw) fig.eventAfterDraw.Bind(_SetLimitsAfterDraw) # Return axes.Draw() return l
def _Compile(self): """ Create a series of glyphs from the given text. From these Glyphs the textureCords in the font texture can be calculated. Also the relative vertices are calculated, which are then corrected for angle and alignment in _PositionText(). -> Produces _vertices1 (and is called when that is None) """ # make invalid first self._Invalidate() # get font instance from figure f = self.GetFigure() if not f: return font = f._fontManager.GetFont(self._fontname) # clear glyphs glyphs = [] self._xglyph = Glyph(font, 'X', self._size) tt = self._text # transform greek characters that were given without double backslash tt = tt.replace('\alpha', unichr(escapes['alpha'])) tt = tt.replace('\beta', unichr(escapes['beta'])) tt = tt.replace('\rho', unichr(escapes['rho'])) tt = tt.replace('\theta', unichr(escapes['theta'])) # transform other chars tt = tt.replace(r'\\', '\t') # double backslashes do not escape for c in escapesKeys: tt = tt.replace('\\' + c, unichr(escapes[c])) tt = tt.replace('\t', r'\\') # get italic and bold modifiers tt = tt.replace('\i', '\x06') # just use some char that is no string tt = tt.replace('\b', '\x07') # build list of glyphs, take sub/super scripting into account. escape = False styles = [] style = None # Style to set for i in range(len(tt)): c = tt[i] if escape: g = Glyph(font, c, self._size, styles) glyphs.append(g) escape = False elif c == '{': # Append style to the list if style: styles.append(style) style = None elif c == '}': # Remove style if styles: styles.pop() elif c == '^': style = MiniStyle(2) elif c == '_': style = MiniStyle(1) elif c == '\x06': style = MiniStyle(0, False, True) elif c == '\x07': style = MiniStyle(0, True, False) elif c == '\\' and i + 1 < len(tt) and tt[i + 1] in ['_^\x06\x07']: escape = True else: # create glyph (with new style (or not)) g = Glyph(font, c, self._size, styles + [style]) glyphs.append(g) style = None # build arrays with vertices and coordinates x1, y1, z = 0, 0, 0 vertices = Pointset(3) texCords = Pointset(2) for g in glyphs: x2 = x1 + g.sizex y2 = g.sizey #y2 = y1 - g.sizey dy = g.dy # append texture coordinates texCords.append(g.s1, g.t1) texCords.append(g.s2, g.t1) texCords.append(g.s2, g.t2) texCords.append(g.s1, g.t2) # set skewing for position skew = self._size * g.skewFactor # append vertices vertices.append(x1 + skew, y1 + dy, z) vertices.append(x2 + skew, y1 + dy, z) vertices.append(x2, y2 + dy, z) vertices.append(x1, y2 + dy, z) # prepare for next glyph x1 = x1 + g.width + self._charSpacing # store self._texCords = texCords self._vertices1 = vertices
def _Compile(self): """ Create a series of glyphs from the given text. From these Glyphs the textureCords in the font texture can be calculated. Also the relative vertices are calculated, which are then corrected for angle and alignment in _PositionText(). -> Produces _vertices1 (and is called when that is None) """ # make invalid first self._Invalidate() # get font instance from figure f = self.GetFigure() if not f: return font = f._fontManager.GetFont(self._fontname) # clear glyphs glyphs = [] self._xglyph = Glyph(font, 'X', self._size) tt = self._text # transform greek characters that were given without double backslash tt = tt.replace('\alpha', unichr(escapes['alpha'])) tt = tt.replace('\beta', unichr(escapes['beta'])) tt = tt.replace('\rho', unichr(escapes['rho'])) tt = tt.replace('\theta', unichr(escapes['theta'])) # transform other chars tt = tt.replace(r'\\', '\t') # double backslashes do not escape for c in escapesKeys: tt = tt.replace('\\'+c, unichr(escapes[c])) tt = tt.replace('\t', r'\\') # get italic and bold modifiers tt = tt.replace('\i', '\x06') # just use some char that is no string tt = tt.replace('\b', '\x07') # build list of glyphs, take sub/super scripting into account. escape = False styles = [] style = None # Style to set for i in range(len(tt)): c = tt[i] if escape: g = Glyph(font, c, self._size, styles) glyphs.append( g ) escape = False elif c=='{': # Append style to the list if style: styles.append(style) style = None elif c=='}': # Remove style if styles: styles.pop() elif c=='^': style = MiniStyle(2) elif c=='_': style = MiniStyle(1) elif c=='\x06': style = MiniStyle(0,False,True) elif c=='\x07': style = MiniStyle(0,True,False) elif c=='\\' and i+1<len(tt) and tt[i+1] in ['_^\x06\x07']: escape = True else: # create glyph (with new style (or not)) g = Glyph(font, c, self._size, styles+[style]) glyphs.append( g ) style = None # build arrays with vertices and coordinates x1, y1, z = 0, 0, 0 vertices = Pointset(3) texCords = Pointset(2) for g in glyphs: x2 = x1 + g.sizex y2 = g.sizey #y2 = y1 - g.sizey dy = g.dy # append texture coordinates texCords.append(g.s1, g.t1) texCords.append(g.s2, g.t1) texCords.append(g.s2, g.t2) texCords.append(g.s1, g.t2) # set skewing for position skew = self._size * g.skewFactor # append vertices vertices.append(x1+skew, y1+dy, z) vertices.append(x2+skew, y1+dy, z) vertices.append(x2, y2+dy, z) vertices.append(x1, y2+dy, z) # prepare for next glyph x1 = x1 + g.width + self._charSpacing # store self._texCords = texCords self._vertices1 = vertices
def _CreateQuads(self): axes = self.GetAxes() if not axes: return # Store daspect so we can detect it changing self._daspectStored = axes.daspect self._qcountStored = self._quadPartitionCount(axes.camera.fov) # Note that we could determine the world coordinates and use # them directly here. However, the way that we do it now (using # the transformations) is to be preferred, because that way the # transformations are applied via the ModelView matrix stack, # and can easily be made undone in the raycaster. # The -0.5 offset is to center pixels/voxels. This works correctly # for anisotropic data. #shape = self._texture1._shape shape = self._texture1._dataRef.shape x0, x1 = -0.5, shape[2] - 0.5 y0, y1 = -0.5, shape[1] - 0.5 z0, z1 = -0.5, shape[0] - 0.5 # prepare texture coordinates t0, t1 = 0, 1 # I previously swapped coordinates to make sure the right faces # were frontfacing. Now I apply culling to achieve the same # result in a better way. # using glTexCoord* is the same as glMultiTexCoord*(GL_TEXTURE0) # Therefore we need to bind the base texture to 0. # So we draw the six planes of the cube (well not a cube, # a 3d rectangle thingy). The inside is only rendered if the # vertex is facing front, so only 3 planes are rendered at a # time... # Define the 8 corners of the cube. tex_coord0, ver_coord0 = Pointset(3), Pointset(3) # bottom tex_coord0.append((t0, t0, t0)) ver_coord0.append((x0, y0, z0)) # 0 tex_coord0.append((t1, t0, t0)) ver_coord0.append((x1, y0, z0)) # 1 tex_coord0.append((t1, t1, t0)) ver_coord0.append((x1, y1, z0)) # 2 tex_coord0.append((t0, t1, t0)) ver_coord0.append((x0, y1, z0)) # 3 # top tex_coord0.append((t0, t0, t1)) ver_coord0.append((x0, y0, z1)) # 4 tex_coord0.append((t0, t1, t1)) ver_coord0.append((x0, y1, z1)) # 5 tex_coord0.append((t1, t1, t1)) ver_coord0.append((x1, y1, z1)) # 6 tex_coord0.append((t1, t0, t1)) ver_coord0.append((x1, y0, z1)) # 7 # Unwrap the vertices. 4 vertices per side = 24 vertices # Warning: dont mess up the list with indices; theyre carefully # chosen to be front facing. tex_coord, ver_coord = Pointset(3), Pointset(3) for i in [ 0, 1, 2, 3, 4, 5, 6, 7, 3, 2, 6, 5, 0, 4, 7, 1, 0, 3, 5, 4, 1, 7, 6, 2 ]: tex_coord.append(tex_coord0[i]) ver_coord.append(ver_coord0[i]) # Function to partition each quad in four smaller quads def partition(tex_coord1, ver_coord1): tex_coord2, ver_coord2 = Pointset(3), Pointset(3) for iQuad in range(len(tex_coord1) / 4): io = iQuad * 4 for i1 in range(4): for i2 in range(4): i3 = (i1 + i2) % 4 tex_coord2.append( 0.5 * (tex_coord1[io + i1] + tex_coord1[io + i3])) ver_coord2.append( 0.5 * (ver_coord1[io + i1] + ver_coord1[io + i3])) #print('partition from %i to %i vertices' % (len(tex_coord1), len(tex_coord2))) return tex_coord2, ver_coord2 # Partition quads in smaller quads? for iter in range(self._qcountStored): tex_coord, ver_coord = partition(tex_coord, ver_coord) # Store quads data self._quads = tex_coord, ver_coord
# Get axes if axes is None: axes = vv.gca() # Create mesh object m = vv.Mesh(axes, baseMesh) # Adjust axes if axesAdjust: if axes.daspectAuto is None: axes.daspectAuto = False axes.cameraType = '3d' axes.SetLimits() # Return axes.Draw() return m if __name__ == '__main__': pp = Pointset(3) pp.append(0,1,0) pp.append(3,2,1) pp.append(4,5,2) pp.append(2,3,1) pp.append(0,4,0) # pp.append(0,1,0) vv.figure() m = solidLine(pp, [0.1, 0.2, 0.3, 0.03, 0.2], 8)
def OnDraw(self): # Get colormaps that apply par = self.parent if par is None: return elif isinstance(par, (BaseFigure, Axes)): mapables = par.FindObjects(Colormapable) elif isinstance(par, ColormapEditor): mapables = par.GetMapables() elif isinstance(par, ClimEditor): mapables = par.GetMapables() else: mapables = [] # Get the last one mapable = None if mapables: mapable = mapables[-1] # get dimensions w,h = self.position.size # Calc map direction if w > h: texCords = [0,0,1,1] else: texCords = [1,0,0,1] # draw plane if mapable: # Use it's colormap texture mapable._EnableColormap() # Disable alpha channel (by not blending) gl.glDisable(gl.GL_BLEND) gl.glColor(1.0, 1.0, 1.0, 1.0) # Draw quads gl.glBegin(gl.GL_QUADS) gl.glTexCoord1f(texCords[0]); gl.glVertex2f(0,0) gl.glTexCoord1f(texCords[1]); gl.glVertex2f(0,h) gl.glTexCoord1f(texCords[2]); gl.glVertex2f(w,h) gl.glTexCoord1f(texCords[3]); gl.glVertex2f(w,0) gl.glEnd() # Clean up gl.glEnable(gl.GL_BLEND) gl.glFlush() mapable._DisableColormap() # prepare gl.glDisable(gl.GL_LINE_SMOOTH) # draw edges if self.edgeWidth and self.edgeColor: clr = self.edgeColor gl.glColor(clr[0], clr[1], clr[2], 1.0) gl.glLineWidth(self.edgeWidth) # gl.glBegin(gl.GL_LINE_LOOP) gl.glVertex2f(0,0) gl.glVertex2f(0,h) gl.glVertex2f(w,h) gl.glVertex2f(w,0) gl.glEnd() if hasattr(mapable, 'clim'): # Draw ticks if w>h: p0 = Point(0, h) p1 = Point(w, h) delta = Point(0,3) halign, valign = 0, 0 xoffset, yoffset = -8, -2 else: p0 = Point(w, h) p1 = Point(w, 0) delta = Point(3,0) halign, valign = -1, 0 xoffset, yoffset = 5, -8 # Get tickmarks ticks, ticksPos, ticksText = GetTicks(p0, p1, mapable.clim) newLabelPool = {} linePieces = Pointset(2) for tick, pos, text in zip(ticks, ticksPos, ticksText): pos2 = pos + delta # Add line piece linePieces.append(pos); linePieces.append(pos2) # Create or reuse label if tick in self._labelPool: label = self._labelPool.pop(tick) else: label = Label(self, ' '+text+' ') label.bgcolor = '' # Position label and set text alignment newLabelPool[tick] = label label.halign, label.valign = halign, valign label.position.x = pos2.x + xoffset label.position.w = 16 label.position.y = pos2.y + yoffset # Clean up label pool for label in self._labelPool.values(): label.Destroy() self._labelPool = newLabelPool # Draw line pieces # prepare gl.glLineWidth(1) gl.glEnableClientState(gl.GL_VERTEX_ARRAY) gl.glVertexPointerf(linePieces.data) gl.glDrawArrays(gl.GL_LINES, 0, len(linePieces)) gl.glDisableClientState(gl.GL_VERTEX_ARRAY) # clean up gl.glEnable(gl.GL_LINE_SMOOTH)
def _CreateQuads(self): axes = self.GetAxes() if not axes: return # Store daspect so we can detect it changing self._daspectStored = axes.daspect self._qcountStored = self._quadPartitionCount(axes.camera.fov) # Note that we could determine the world coordinates and use # them directly here. However, the way that we do it now (using # the transformations) is to be preferred, because that way the # transformations are applied via the ModelView matrix stack, # and can easily be made undone in the raycaster. # The -0.5 offset is to center pixels/voxels. This works correctly # for anisotropic data. #shape = self._texture1._shape shape = self._texture1._dataRef.shape x0, x1 = -0.5, shape[2]-0.5 y0, y1 = -0.5, shape[1]-0.5 z0, z1 = -0.5, shape[0]-0.5 # prepare texture coordinates t0, t1 = 0, 1 # I previously swapped coordinates to make sure the right faces # were frontfacing. Now I apply culling to achieve the same # result in a better way. # using glTexCoord* is the same as glMultiTexCoord*(GL_TEXTURE0) # Therefore we need to bind the base texture to 0. # So we draw the six planes of the cube (well not a cube, # a 3d rectangle thingy). The inside is only rendered if the # vertex is facing front, so only 3 planes are rendered at a # time... # Define the 8 corners of the cube. tex_coord0, ver_coord0 = Pointset(3), Pointset(3) # bottom tex_coord0.append((t0,t0,t0)); ver_coord0.append((x0, y0, z0)) # 0 tex_coord0.append((t1,t0,t0)); ver_coord0.append((x1, y0, z0)) # 1 tex_coord0.append((t1,t1,t0)); ver_coord0.append((x1, y1, z0)) # 2 tex_coord0.append((t0,t1,t0)); ver_coord0.append((x0, y1, z0)) # 3 # top tex_coord0.append((t0,t0,t1)); ver_coord0.append((x0, y0, z1)) # 4 tex_coord0.append((t0,t1,t1)); ver_coord0.append((x0, y1, z1)) # 5 tex_coord0.append((t1,t1,t1)); ver_coord0.append((x1, y1, z1)) # 6 tex_coord0.append((t1,t0,t1)); ver_coord0.append((x1, y0, z1)) # 7 # Unwrap the vertices. 4 vertices per side = 24 vertices # Warning: dont mess up the list with indices; theyre carefully # chosen to be front facing. tex_coord, ver_coord = Pointset(3), Pointset(3) for i in [0,1,2,3, 4,5,6,7, 3,2,6,5, 0,4,7,1, 0,3,5,4, 1,7,6,2]: tex_coord.append(tex_coord0[i]) ver_coord.append(ver_coord0[i]) # Function to partition each quad in four smaller quads def partition(tex_coord1, ver_coord1): tex_coord2, ver_coord2 = Pointset(3), Pointset(3) for iQuad in range(len(tex_coord1)/4): io = iQuad * 4 for i1 in range(4): for i2 in range(4): i3 = (i1 + i2)%4 tex_coord2.append( 0.5*(tex_coord1[io+i1] + tex_coord1[io+i3]) ) ver_coord2.append( 0.5*(ver_coord1[io+i1] + ver_coord1[io+i3]) ) #print('partition from %i to %i vertices' % (len(tex_coord1), len(tex_coord2))) return tex_coord2, ver_coord2 # Partition quads in smaller quads? for iter in range(self._qcountStored): tex_coord, ver_coord = partition(tex_coord, ver_coord) # Store quads data self._quads = tex_coord, ver_coord
def solidBox(translation=None, scaling=None, direction=None, rotation=None, axesAdjust=True, axes=None): """ solidBox(translation=None, scaling=None, direction=None, rotation=None, axesAdjust=True, axes=None) Creates a solid cube (or box if you scale it) centered at the origin. Returns an OrientableMesh. Parameters ---------- Note that translation, scaling, and direction can also be given using a Point instance. translation : (dx, dy, dz), optional The translation in world units of the created world object. scaling: (sx, sy, sz), optional The scaling in world units of the created world object. direction: (nx, ny, nz), optional Normal vector that indicates the direction of the created world object. rotation: scalar, optional The anle (in degrees) to rotate the created world object around its direction vector. axesAdjust : bool If True, this function will call axes.SetLimits(), and set the camera type to 3D. If daspectAuto has not been set yet, it is set to False. axes : Axes instance Display the bars in the given axes, or the current axes if not given. """ # Create vertices of a cube pp = Pointset(3) # Bottom pp.append(-0.5,-0.5,-0.5) pp.append(+0.5,-0.5,-0.5) pp.append(+0.5,+0.5,-0.5) pp.append(-0.5,+0.5,-0.5) # Top pp.append(-0.5,-0.5,+0.5) pp.append(-0.5,+0.5,+0.5) pp.append(+0.5,+0.5,+0.5) pp.append(+0.5,-0.5,+0.5) # Init vertices and normals vertices = Pointset(3) normals = Pointset(3) # Create vertices for i in [3,2,1,0]: # Top vertices.append(pp[i]); normals.append(0,0,-1) for i in [7,6,5,4]: # Bottom vertices.append(pp[i]); normals.append(0,0,+1) for i in [5,6,2,3]: # Front vertices.append(pp[i]); normals.append(0,+1,0) for i in [1,7,4,0]: # Back vertices.append(pp[i]); normals.append(0,-1,0) for i in [4,5,3,0]: # Left vertices.append(pp[i]); normals.append(-1,0,0) for i in [2,6,7,1]: # Right vertices.append(pp[i]); normals.append(+1,0,0) ## Visualize # Create axes if axes is None: axes = vv.gca() # Create mesh and set orientation m = vv.OrientableMesh(axes, vertices, None, normals, verticesPerFace=4) # if translation is not None: m.translation = translation if scaling is not None: m.scaling = scaling if direction is not None: m.direction = direction if rotation is not None: m.rotation = rotation # Adjust axes if axesAdjust: if axes.daspectAuto is None: axes.daspectAuto = False axes.cameraType = '3d' axes.SetLimits() # Done axes.Draw() return m
def solidCylinder(translation=None, scaling=None, direction=None, rotation=None, N=16, M=16, axesAdjust=True, axes=None): """ solidCylinder( translation=None, scaling=None, direction=None, rotation=None, N=16, M=16, axesAdjust=True, axes=None) Creates a cylinder object with quad faces and its base at the origin. Returns an OrientableMesh instance. Parameters ---------- Note that translation, scaling, and direction can also be given using a Point instance. translation : (dx, dy, dz), optional The translation in world units of the created world object. scaling: (sx, sy, sz), optional The scaling in world units of the created world object. direction: (nx, ny, nz), optional Normal vector that indicates the direction of the created world object. rotation: scalar, optional The anle (in degrees) to rotate the created world object around its direction vector. N : int The number of subdivisions around its axis. If smaller than 8, flat shading is used instead of smooth shading. M : int The number of subdivisions along its axis. If smaller than 8, flat shading is used instead of smooth shading. axesAdjust : bool If True, this function will call axes.SetLimits(), and set the camera type to 3D. If daspectAuto has not been set yet, it is set to False. axes : Axes instance Display the bars in the given axes, or the current axes if not given. """ # Note that the number of vertices around the axis is N+1. This # would not be necessary per see, but it helps create a nice closed # texture when it is mapped. There are N number of faces though. # Similarly, to obtain M faces along the axis, we need M+1 # vertices. # Quick access pi2 = np.pi*2 cos = np.cos sin = np.sin sl = N+1 # Calculate vertices, normals and texcords vertices = Pointset(3) normals = Pointset(3) texcords = Pointset(2) # Round part for m in range(M+1): z = 1.0 - float(m)/M # between 0 and 1 v = float(m)/M # for n in range(N+1): b = pi2 * float(n) / N u = float(n) / (N) x = cos(b) y = sin(b) vertices.append(x,y,z) normals.append(x,y,0) texcords.append(u,v) # Top for m in range(2): for n in range(N+1): b = pi2 * float(n) / N u = float(n) / (N) x = cos(b) * m # todo: check which ones are frontfacing! y = sin(b) * m vertices.append(x,y,1) normals.append(0,0,1) texcords.append(u,0) # Bottom for m in range(2): for n in range(N+1): b = pi2 * float(n) / N u = float(n) / (N) x = cos(b) * (1-m) y = sin(b) * (1-m) vertices.append(x,y,0) normals.append(0,0,-1) texcords.append(u,1) # Normalize normals normals = normals.normalize() # Calculate indices indices = [] for j in range(M): for i in range(N): #indices.extend([j*sl+i, j*sl+i+1, (j+1)*sl+i+1, (j+1)*sl+i]) indices.extend([(j+1)*sl+i, (j+1)*sl+i+1, j*sl+i+1, j*sl+i]) j = M+1 for i in range(N): indices.extend([(j+1)*sl+i, (j+1)*sl+i+1, j*sl+i+1, j*sl+i]) j = M+3 for i in range(N): indices.extend([(j+1)*sl+i, (j+1)*sl+i+1, j*sl+i+1, j*sl+i]) # Make indices a numpy array indices = np.array(indices, dtype=np.uint32) ## Visualization # Create axes if axes is None: axes = vv.gca() # Create mesh m = vv.OrientableMesh(axes, vertices, indices, normals, values=texcords, verticesPerFace=4) # if translation is not None: m.translation = translation if scaling is not None: m.scaling = scaling if direction is not None: m.direction = direction if rotation is not None: m.rotation = rotation # Set flat shading? if N<8 or M<8: m.faceShading = 'flat' # Adjust axes if axesAdjust: if axes.daspectAuto is None: axes.daspectAuto = False axes.cameraType = '3d' axes.SetLimits() # Done axes.Draw() return m
# Get axes if axes is None: axes = vv.gca() # Create mesh object m = vv.Mesh(axes, baseMesh) # Adjust axes if axesAdjust: if axes.daspectAuto is None: axes.daspectAuto = False axes.cameraType = '3d' axes.SetLimits() # Return axes.Draw() return m if __name__ == '__main__': pp = Pointset(3) pp.append(0, 1, 0) pp.append(3, 2, 1) pp.append(4, 5, 2) pp.append(2, 3, 1) pp.append(0, 4, 0) # pp.append(0,1,0) vv.figure() m = solidLine(pp, [0.1, 0.2, 0.3, 0.03, 0.2], 8)
def _GetLimits(self, *args): """ _GetLimits(self, x1, x2, y1, y2, z1, z2) Get the limits in world coordinates between which the object exists. This is used by the Axes class to set the camera correctly. If None is returned, the limits are undefined. Inheriting Wobject classes should overload this method. However, they can use this method to take all transformations into account by giving the cornerpoints of the untransformed object. Returns a 3 element tuple of vv.Range instances: xlim, ylim, zlim. """ # Examine args if not args: minx, maxx, miny, maxy, minz, maxz = [], [], [], [], [], [] elif len(args) == 6: minx, maxx, miny, maxy, minz, maxz = tuple([[arg] for arg in args]) else: raise ValueError("_Getlimits expects 0 or 6 arguments.") # Get limits of children for ob in self.children: tmp = ob._GetLimits() if tmp is not None: limx, limy, limz = tmp minx.append(limx.min) maxx.append(limx.max) miny.append(limy.min) maxy.append(limy.max) minz.append(limz.min) maxz.append(limz.max) # Do we have limits? if not (minx and maxx and miny and maxy and minz and maxz): return None # Take min and max x1, y1, z1 = tuple([min(val) for val in [minx, miny, minz]]) x2, y2, z2 = tuple([max(val) for val in [maxx, maxy, maxz]]) # Make pointset of eight cornerpoints pp = Pointset(3) for x in [x1, x2]: for y in [y1, y2]: for z in [z1, z2]: pp.append(x, y, z) # Transform these points for i in range(len(pp)): p = pp[i] for t in reversed(self._transformations): if isinstance(t, Transform_Translate): p.x += t.dx p.y += t.dy p.z += t.dz elif isinstance(t, Transform_Scale): p.x *= t.sx p.y *= t.sy p.z *= t.sz elif isinstance(t, Transform_Rotate): angle = float(t.angle * np.pi / 180.0) q = Quaternion.create_from_axis_angle( angle, t.ax, t.ay, t.az) p = q.rotate_point(p) # Update pp[i] = p # Return limits xlim = misc.Range(pp[:, 0].min(), pp[:, 0].max()) ylim = misc.Range(pp[:, 1].min(), pp[:, 1].max()) zlim = misc.Range(pp[:, 2].min(), pp[:, 2].max()) return xlim, ylim, zlim
def lineToMesh(pp, radius, vertex_num, values=None): """ lineToMesh(pp, radius, vertex_num, values=None) From a line, create a mesh that represents the line as a tube with given diameter. Returns a BaseMesh instance. Parameters ---------- pp : 3D Pointset The points along the line. If the first and last point are the same, the mesh-line is closed. radius : scalar The radius of the tube that is created. Radius can also be a sequence of values (containing a radius for each point). vertex_num : int The number of vertices to create along the circumference of the tube. values : list or numpy array (optional) A value per point. Can be Nx1, Nx2, Nx3 or Nx4. A list of scalars can also be given. The values are propagated to the mesh vertices and supplied as input to the Mesh constructor. This allows for example to define the color for the tube. """ # we need this quite a bit pi = np.pi # process radius if hasattr(radius, '__len__'): if len(radius) != len(pp): raise ValueError('Len of radii much match len of points.') else: radius = np.array(radius, dtype=np.float32) else: radius = radius * np.ones((len(pp), ), dtype=np.float32) # calculate vertex points for 2D circle angles = np.arange(0, pi * 2 - 0.0001, pi * 2 / vertex_num) angle_cos = np.cos(angles) angle_sin = np.sin(angles) vertex_num2 = len(angles) # just to be sure # calculate distance between two line pieces (for smooth cylinders) dists = pp[1:].distance(pp[:-1]) bufdist = min(radius.max(), dists.min() / 2.2) # check if line is closed lclosed = (pp[0] == pp[-1]) # calculate normal vectors on each line point normals = pp[:-1].subtract(pp[1:]) if lclosed: normals.append(pp[0] - pp[1]) else: normals.append(pp[-2] - pp[-1]) normals = -1 * normals.normalize() # create list to store vertices vertices = Pointset(3) surfaceNormals = Pointset(3) # And a list for the values if values is None: vvalues = None nvalues = 0 elif isinstance(values, list): if len(values) != len(pp): raise ValueError('There must be as many values as points.') vvalues = Pointset(1) elif isinstance(values, np.ndarray): if values.ndim != 2: raise ValueError('Values must be Nx1, Nx2, Nx3 or Nx4.') if values.shape[0] != len(pp): raise ValueError('There must be as many values as points.') vvalues = Pointset(values.shape[1]) elif vv.pypoints.is_Pointset(values): if values.ndim > 4: raise ValueError('Can specify one to four values per point.') if len(values) != len(pp): raise ValueError('There must be as many values as points.') vvalues = Pointset(values.ndim) else: raise ValueError('Invalid value for values.') # Number of triangelized cylinder elements added to plot the 3D line n_cylinders = 0 # Init a and b a, b = Point(0, 0, 1), Point(0, 1, 0) # Calculate the 3D circle coordinates of the first circle/cylinder a, b = getSpanVectors(normals[0], a, b) circm = getCircle(angle_cos, angle_sin, a, b) # If not a closed line, add half sphere made with 5 cylinders at line start if not lclosed: for j in range(5, 0, -1): # Translate the circle on it's position on the line r = (1 - (j / 5.0)**2)**0.5 circmp = float(r * radius[0]) * circm + ( pp[0] - (j / 5.0) * bufdist * normals[0]) # Calc normals circmn = (pp[0].subtract(circmp)).normalize() # Store the vertex list vertices.extend(circmp) surfaceNormals.extend(-1 * circmn) if vvalues is not None: for iv in range(vertex_num2): vvalues.append(values[0]) n_cylinders += 1 # Loop through all line pieces for i in range(len(pp) - 1): ## Create main cylinder between two line points # which consists of two connected circles. # get normal and point normal1 = normals[i] point1 = pp[i] # calculate the 3D circle coordinates a, b = getSpanVectors(normal1, a, b) circm = getCircle(angle_cos, angle_sin, a, b) # Translate the circle, and store circmp = float(radius[i]) * circm + (point1 + bufdist * normal1) vertices.extend(circmp) surfaceNormals.extend(circm) if vvalues is not None: for iv in range(vertex_num2): vvalues.append(values[i]) n_cylinders += 1 # calc second normal and line normal2 = normals[i + 1] point2 = pp[i + 1] # Translate the circle, and store circmp = float(radius[i + 1]) * circm + (point2 - bufdist * normal1) vertices.extend(circmp) surfaceNormals.extend(circm) if vvalues is not None: for iv in range(vertex_num2): vvalues.append(values[i + 1]) n_cylinders += 1 ## Create in between circle to smoothly connect line pieces if not lclosed and i == len(pp) - 2: break # get normal and point normal12 = (normal1 + normal2).normalize() tmp = (point2 + bufdist * normal2) + (point2 - bufdist * normal1) point12 = 0.5858 * point2 + 0.4142 * (0.5 * tmp) # Calculate the 3D circle coordinates a, b = getSpanVectors(normal12, a, b) circm = getCircle(angle_cos, angle_sin, a, b) # Translate the circle, and store circmp = float(radius[i + 1]) * circm + point12 vertices.extend(circmp) surfaceNormals.extend(circm) if vvalues is not None: for iv in range(vertex_num2): vvalues.append(0.5 * (values[i] + values[i + 1])) n_cylinders += 1 # If not a closed line, add half sphere made with 5 cylinders at line start # Otherwise add the starting circle to the line end. if not lclosed: for j in range(0, 6): # Translate the circle on it's position on the line r = (1 - (j / 5.0)**2)**0.5 circmp = float(r * radius[-1]) * circm + ( pp[-1] + (j / 5.0) * bufdist * normals[-1]) # Calc normals circmn = (pp[-1].subtract(circmp)).normalize() # Store the vertex list vertices.extend(circmp) surfaceNormals.extend(-1 * circmn) if vvalues is not None: for iv in range(vertex_num2): vvalues.append(values[-1]) n_cylinders += 1 else: # get normal and point normal1 = normals[-1] point1 = pp[-1] # calculate the 3D circle coordinates a, b = getSpanVectors(normal1, a, b) circm = getCircle(angle_cos, angle_sin, a, b) # Translate the circle, and store circmp = float(radius[0]) * circm + (point1 + bufdist * normal1) vertices.extend(circmp) surfaceNormals.extend(circm) if vvalues is not None: for iv in range(vertex_num2): vvalues.append(values[-1]) n_cylinders += 1 # almost done, determine quad faces ... # define single faces #firstFace = [0, 1, vertex_num+1, vertex_num] #lastFace = [vertex_num-1, 0, vertex_num, 2*vertex_num-1] firstFace = [vertex_num, vertex_num + 1, 1, 0] lastFace = [2 * vertex_num - 1, vertex_num, 0, vertex_num - 1] # define single round oneRound = [] for i in range(vertex_num - 1): oneRound.extend([val + i for val in firstFace]) oneRound.extend(lastFace) oneRound = np.array(oneRound, dtype=np.uint32) # calculate face data parts = [] for i in range(n_cylinders - 1): parts.append(oneRound + i * vertex_num) faces = np.concatenate(parts) faces.shape = faces.shape[0] / 4, 4 # Done! return BaseMesh(vertices, faces, surfaceNormals, vvalues)