def drawtri(ia, ib, ic, div): a, b, c = vertices[ia], vertices[ib], vertices[ic] if (div <= 0): # Store faces here faces.extend([ia, ib, ic]) else: # Create new points ab = Point(0, 0, 0) ac = Point(0, 0, 0) bc = Point(0, 0, 0) for i in range(3): ab[i] = (a[i] + b[i]) / 2.0 ac[i] = (a[i] + c[i]) / 2.0 bc[i] = (b[i] + c[i]) / 2.0 ab = ab.normalize() ac = ac.normalize() bc = bc.normalize() # Add new points i_offset = len(vertices) vertices.append(ab) vertices.append(ac) vertices.append(bc) iab, iac, ibc = i_offset + 0, i_offset + 1, i_offset + 2 # drawtri(ia, iab, iac, div - 1) drawtri(ib, ibc, iab, div - 1) drawtri(ic, iac, ibc, div - 1) drawtri(iab, ibc, iac, div - 1)
def _OnDoubleClick(self, event): # use OnDown method to test if and which node was clicked self._OnDown(event) if self._selectedNode is None: # create new node? pos = self.position x, y = event.x, event.y if x>-5 and x<pos.width+5 and y>-5 and y<pos.height-5: pos = Point(event.x, event.y) / Point(self.position.size) self._nodes.append( pos ) else: # remove point self._nodes.pop(self._selectedNode) # Update self._UpdateFull()
def __init__(self): # Set current and reference direction (default up) self._refDirection = Point(0, 0, 1) self._direction = Point(0, 0, 1) # Create transformations self._scaleTransform = misc.Transform_Scale() self._translateTransform = misc.Transform_Translate() self._rotateTransform = misc.Transform_Rotate() self._directionTransform = misc.Transform_Rotate() # Append transformations to THE list self.transformations.append(self._translateTransform) self.transformations.append(self._directionTransform) self.transformations.append(self._rotateTransform) self.transformations.append(self._scaleTransform)
def _OnMotion(self, event): # should we proceed? if self._selectedNode is None: return if self._selectedNode >= len(self._nodes): self._OnUp() return # calculate and limit new position x, y = event.x, event.y pos = Point(x,y) / Point(self.position.size) if pos.x<0: pos.x=0 if pos.y<0: pos.y=0 if pos.x>1: pos.x = 1 if pos.y>1: pos.y = 1 # Change node's position self._nodes[self._selectedNode,0] = pos.x self._nodes[self._selectedNode,1] = pos.y # Apply to line self._NodesToLine(self._nodes, self._line) # Draw (fast) self.Draw(True)
def _OnDown(self, event): # calculate distance of mouse to all points p = Point(event.x, event.y) dists = p.distance( self._nodes * Point(self.position.size) ) # is one close enough? i = -1 if self._nodes: # or .min() will not work tmp = dists.min() if tmp <= self._snapWidth: i, = np.where(dists==tmp) # if so, store that point if i>= 0: self._selectedNode = i else: self._selectedNode = None
def fset(self, value): # Store direction if isinstance(value, (list, tuple)) and len(value) == 3: self._direction = Point(*tuple(value)) elif is_Point(value) and value.ndim == 3: self._direction = value else: raise ValueError( 'Direction should be a 3D Point or 3-element tuple.') # Normalize if self._direction.norm() == 0: raise ValueError( 'Direction vector must have a non-zero length.') self._direction = self._direction.normalize() # Create ref point refPoint = self._refDirection # Convert to rotation. The cross product of two vectors results # in a vector normal to both vectors. This is the axis of rotation # over which the minimal rotation is achieved. axis = self._direction.cross(refPoint) if axis.norm() < 0.01: if self._direction.z > 0: # No rotation self._directionTransform.ax = 0.0 self._directionTransform.ay = 0.0 self._directionTransform.az = 1.0 self._directionTransform.angle = 0.0 else: # Flipped self._directionTransform.ax = 1.0 self._directionTransform.ay = 0.0 self._directionTransform.az = 0.0 self._directionTransform.angle = 180.0 else: axis = axis.normalize() angle = -refPoint.angle(self._direction) self._directionTransform.ax = axis.x self._directionTransform.ay = axis.y self._directionTransform.az = axis.z self._directionTransform.angle = angle * 180 / np.pi
def _OnMouseMotion(self, event): # Handle or pass? if not (self._interact_down and self._screenVec): return # Get vector relative to reference position refPos = Point(self._refPos) pos = Point(event.x, event.y) vec = pos - refPos # Length of reference vector, and its normalized version screenVec = Point(self._screenVec) L = screenVec.norm() V = screenVec.normalize() # Number of indexes to change n = vec.dot(V) / L # Apply! self.index = int(self._refIndex + n)
def drawtri(ia, ib, ic, div): a, b, c = vertices[ia] , vertices[ib], vertices[ic] if (div<=0): # Store faces here faces.extend([ia, ib, ic]) else: # Create new points ab = Point(0,0,0) ac = Point(0,0,0) bc = Point(0,0,0) for i in range(3): ab[i]=(a[i]+b[i])/2.0; ac[i]=(a[i]+c[i])/2.0; bc[i]=(b[i]+c[i])/2.0; ab = ab.normalize(); ac = ac.normalize(); bc = bc.normalize() # Add new points i_offset = len(vertices) vertices.append(ab) vertices.append(ac) vertices.append(bc) iab, iac, ibc = i_offset+0, i_offset+1, i_offset+2 # drawtri(ia, iab, iac, div-1) drawtri(ib, ibc, iab, div-1) drawtri(ic, iac, ibc, div-1) drawtri(iab, ibc, iac, div-1)
def drawtri(a, b, c, div): if (div <= 0): vertices.append(a) vertices.append(b) vertices.append(c) else: ab = Point(0, 0, 0) ac = Point(0, 0, 0) bc = Point(0, 0, 0) for i in range(3): ab[i] = (a[i] + b[i]) / 2.0 ac[i] = (a[i] + c[i]) / 2.0 bc[i] = (b[i] + c[i]) / 2.0 ab = ab.normalize() ac = ac.normalize() bc = bc.normalize() drawtri(a, ab, ac, div - 1) drawtri(b, bc, ab, div - 1) drawtri(c, ac, bc, div - 1) drawtri(ab, bc, ac, div - 1)
def __init__(self): # Set current and reference direction (default up) self._refDirection = Point(0,0,1) self._direction = Point(0,0,1) # Create transformations self._scaleTransform = misc.Transform_Scale() self._translateTransform = misc.Transform_Translate() self._rotateTransform = misc.Transform_Rotate() self._directionTransform = misc.Transform_Rotate() # Append transformations to THE list self.transformations.append(self._translateTransform) self.transformations.append(self._directionTransform) self.transformations.append(self._rotateTransform) self.transformations.append(self._scaleTransform)
def fset(self, value): # Store direction if isinstance(value, (list, tuple)) and len(value) == 3: self._direction = Point(*tuple(value)) elif is_Point(value) and value.ndim == 3: self._direction = value else: raise ValueError('Direction should be a 3D Point or 3-element tuple.') # Normalize if self._direction.norm()==0: raise ValueError('Direction vector must have a non-zero length.') self._direction = self._direction.normalize() # Create ref point refPoint = self._refDirection # Convert to rotation. The cross product of two vectors results # in a vector normal to both vectors. This is the axis of rotation # over which the minimal rotation is achieved. axis = self._direction.cross(refPoint) if axis.norm() < 0.01: if self._direction.z > 0: # No rotation self._directionTransform.ax = 0.0 self._directionTransform.ay = 0.0 self._directionTransform.az = 1.0 self._directionTransform.angle = 0.0 else: # Flipped self._directionTransform.ax = 1.0 self._directionTransform.ay = 0.0 self._directionTransform.az = 0.0 self._directionTransform.angle = 180.0 else: axis = axis.normalize() angle = -refPoint.angle(self._direction) self._directionTransform.ax = axis.x self._directionTransform.ay = axis.y self._directionTransform.az = axis.z self._directionTransform.angle = angle * 180 / np.pi
def drawtri(a, b, c, div): if (div<=0): vertices.append(a) vertices.append(b) vertices.append(c) else: ab = Point(0,0,0) ac = Point(0,0,0) bc = Point(0,0,0) for i in range(3): ab[i]=(a[i]+b[i])/2.0; ac[i]=(a[i]+c[i])/2.0; bc[i]=(b[i]+c[i])/2.0; ab = ab.normalize(); ac = ac.normalize(); bc = bc.normalize() drawtri(a, ab, ac, div-1) drawtri(b, bc, ab, div-1) drawtri(c, ac, bc, div-1) drawtri(ab, bc, ac, div-1)
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 = np.all(pp[0] == pp[-1]) # calculate normal vectors on each line point normals = pp[:-1].subtract(pp[1:]).copy() 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 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.utils.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 fget(self): d = self._translateTransform return Point(d.dx, d.dy, d.dz)
def fget(self): s = self._scaleTransform return Point(s.sx, s.sy, s.sz)
class OrientationForWobjects_mixClass(object): """ OrientationForWobjects_mixClass() This class can be mixed with a wobject class to enable easy orientation of the objects in space. It makes use of the tranformation list that each wobject has. The functionality provided by this class is not made part of the Wobject class because it does not make sense for all kind of wobjects (for example lines and images). The OrientableMesh is a class that inherits from this class. """ def __init__(self): # Set current and reference direction (default up) self._refDirection = Point(0, 0, 1) self._direction = Point(0, 0, 1) # Create transformations self._scaleTransform = misc.Transform_Scale() self._translateTransform = misc.Transform_Translate() self._rotateTransform = misc.Transform_Rotate() self._directionTransform = misc.Transform_Rotate() # Append transformations to THE list self.transformations.append(self._translateTransform) self.transformations.append(self._directionTransform) self.transformations.append(self._rotateTransform) self.transformations.append(self._scaleTransform) @misc.PropWithDraw def scaling(): """ Get/Set the scaling of the object. Can be set using a 3-element tuple, a 3D point, or a scalar. The getter always returns a Point. """ def fget(self): s = self._scaleTransform return Point(s.sx, s.sy, s.sz) def fset(self, value): if isinstance(value, (float, int)): self._scaleTransform.sx = float(value) self._scaleTransform.sy = float(value) self._scaleTransform.sz = float(value) elif isinstance(value, (list, tuple)) and len(value) == 3: self._scaleTransform.sx = float(value[0]) self._scaleTransform.sy = float(value[1]) self._scaleTransform.sz = float(value[2]) elif is_Point(value) and value.ndim == 3: self._scaleTransform.sx = value.x self._scaleTransform.sy = value.y self._scaleTransform.sz = value.z else: raise ValueError( 'Scaling should be a scalar, 3D Point, or 3-element tuple.' ) return locals() @misc.PropWithDraw def translation(): """ Get/Set the transaltion of the object. Can be set using a 3-element tuple or a 3D point. The getter always returns a Point. """ def fget(self): d = self._translateTransform return Point(d.dx, d.dy, d.dz) def fset(self, value): if isinstance(value, (list, tuple)) and len(value) == 3: self._translateTransform.dx = value[0] self._translateTransform.dy = value[1] self._translateTransform.dz = value[2] elif is_Point(value) and value.ndim == 3: self._translateTransform.dx = value.x self._translateTransform.dy = value.y self._translateTransform.dz = value.z else: raise ValueError( 'Translation should be a 3D Point or 3-element tuple.') return locals() @misc.PropWithDraw def direction(): """ Get/Set the direction (i.e. orientation) of the object. Can be set using a 3-element tuple or a 3D point. The getter always returns a Point. """ def fget(self): return self._direction.copy() def fset(self, value): # Store direction if isinstance(value, (list, tuple)) and len(value) == 3: self._direction = Point(*tuple(value)) elif is_Point(value) and value.ndim == 3: self._direction = value else: raise ValueError( 'Direction should be a 3D Point or 3-element tuple.') # Normalize if self._direction.norm() == 0: raise ValueError( 'Direction vector must have a non-zero length.') self._direction = self._direction.normalize() # Create ref point refPoint = self._refDirection # Convert to rotation. The cross product of two vectors results # in a vector normal to both vectors. This is the axis of rotation # over which the minimal rotation is achieved. axis = self._direction.cross(refPoint) if axis.norm() < 0.01: if self._direction.z > 0: # No rotation self._directionTransform.ax = 0.0 self._directionTransform.ay = 0.0 self._directionTransform.az = 1.0 self._directionTransform.angle = 0.0 else: # Flipped self._directionTransform.ax = 1.0 self._directionTransform.ay = 0.0 self._directionTransform.az = 0.0 self._directionTransform.angle = 180.0 else: axis = axis.normalize() angle = -refPoint.angle(self._direction) self._directionTransform.ax = axis.x self._directionTransform.ay = axis.y self._directionTransform.az = axis.z self._directionTransform.angle = angle * 180 / np.pi return locals() @misc.PropWithDraw def rotation(): """ Get/Set the rotation of the object (in degrees, around its direction vector). """ def fget(self): return self._rotateTransform.angle def fset(self, value): self._rotateTransform.angle = float(value) return locals()
class OrientationForWobjects_mixClass(object): """ OrientationForWobjects_mixClass() This class can be mixed with a wobject class to enable easy orientation of the objects in space. It makes use of the tranformation list that each wobject has. The functionality provided by this class is not made part of the Wobject class because it does not make sense for all kind of wobjects (for example lines and images). The OrientableMesh is a class that inherits from this class. """ def __init__(self): # Set current and reference direction (default up) self._refDirection = Point(0,0,1) self._direction = Point(0,0,1) # Create transformations self._scaleTransform = misc.Transform_Scale() self._translateTransform = misc.Transform_Translate() self._rotateTransform = misc.Transform_Rotate() self._directionTransform = misc.Transform_Rotate() # Append transformations to THE list self.transformations.append(self._translateTransform) self.transformations.append(self._directionTransform) self.transformations.append(self._rotateTransform) self.transformations.append(self._scaleTransform) @misc.PropWithDraw def scaling(): """ Get/Set the scaling of the object. Can be set using a 3-element tuple, a 3D point, or a scalar. The getter always returns a Point. """ def fget(self): s = self._scaleTransform return Point(s.sx, s.sy, s.sz) def fset(self, value): if isinstance(value, (float, int)): self._scaleTransform.sx = float(value) self._scaleTransform.sy = float(value) self._scaleTransform.sz = float(value) elif isinstance(value, (list, tuple)) and len(value) == 3: self._scaleTransform.sx = float(value[0]) self._scaleTransform.sy = float(value[1]) self._scaleTransform.sz = float(value[2]) elif is_Point(value) and value.ndim == 3: self._scaleTransform.sx = value.x self._scaleTransform.sy = value.y self._scaleTransform.sz = value.z else: raise ValueError('Scaling should be a scalar, 3D Point, or 3-element tuple.') return locals() @misc.PropWithDraw def translation(): """ Get/Set the transaltion of the object. Can be set using a 3-element tuple or a 3D point. The getter always returns a Point. """ def fget(self): d = self._translateTransform return Point(d.dx, d.dy, d.dz) def fset(self, value): if isinstance(value, (list, tuple)) and len(value) == 3: self._translateTransform.dx = value[0] self._translateTransform.dy = value[1] self._translateTransform.dz = value[2] elif is_Point(value) and value.ndim == 3: self._translateTransform.dx = value.x self._translateTransform.dy = value.y self._translateTransform.dz = value.z else: raise ValueError('Translation should be a 3D Point or 3-element tuple.') return locals() @misc.PropWithDraw def direction(): """ Get/Set the direction (i.e. orientation) of the object. Can be set using a 3-element tuple or a 3D point. The getter always returns a Point. """ def fget(self): return self._direction.copy() def fset(self, value): # Store direction if isinstance(value, (list, tuple)) and len(value) == 3: self._direction = Point(*tuple(value)) elif is_Point(value) and value.ndim == 3: self._direction = value else: raise ValueError('Direction should be a 3D Point or 3-element tuple.') # Normalize if self._direction.norm()==0: raise ValueError('Direction vector must have a non-zero length.') self._direction = self._direction.normalize() # Create ref point refPoint = self._refDirection # Convert to rotation. The cross product of two vectors results # in a vector normal to both vectors. This is the axis of rotation # over which the minimal rotation is achieved. axis = self._direction.cross(refPoint) if axis.norm() < 0.01: if self._direction.z > 0: # No rotation self._directionTransform.ax = 0.0 self._directionTransform.ay = 0.0 self._directionTransform.az = 1.0 self._directionTransform.angle = 0.0 else: # Flipped self._directionTransform.ax = 1.0 self._directionTransform.ay = 0.0 self._directionTransform.az = 0.0 self._directionTransform.angle = 180.0 else: axis = axis.normalize() angle = -refPoint.angle(self._direction) self._directionTransform.ax = axis.x self._directionTransform.ay = axis.y self._directionTransform.az = axis.z self._directionTransform.angle = angle * 180 / np.pi return locals() @misc.PropWithDraw def rotation(): """ Get/Set the rotation of the object (in degrees, around its direction vector). """ def fget(self): return self._rotateTransform.angle def fset(self, value): self._rotateTransform.angle = float(value) return locals()
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 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 list(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 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._bgcolor #if max(clr[0], clr[1], clr[2]) < 0.7: if clr[0] + clr[1] + clr[2] < 1.5: gl.glColor(1, 1, 1, 0.25) else: gl.glColor(0, 0, 0, 0.25) # 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 = '' label.textColor = self._textColor # 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 list(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)