def plot_xy(self): joy_in = self.js.getEvents() if joy_in: #print type(joy_in) for event_name in joy_in: #print event_name event = joy_in[event_name] #print event #print type(event) #print inspect.getmembers(event, predicate=inspect.ismethod) event_mag = event.getMagnitude() if event_name == 'moveForward': self.y_mag = event_mag print('forward', self.y_mag) elif event_name == 'moveBackward': self.y_mag = -event_mag print('backward', self.y_mag) elif event_name == 'turnRight': self.x_mag = event_mag print('right', self.x_mag) elif event_name == 'turnLeft': self.x_mag = -event_mag print('left', self.x_mag) plot_xy = LineSegs() plot_xy.setThickness(2.0) plot_xy.setColor(Vec4(1, 1, 0, 1)) plot_xy.moveTo(self.old_x, 0, self.old_y) plot_xy.drawTo(self.x_mag, 0, self.y_mag) base.render2d.attach_new_node(plot_xy.create(True)) self.old_x = self.x_mag self.old_y = self.y_mag
def createSelectionBox(self, corner1, corner2): """ Create a selection "box" given the coordinates of two opposite corners. The corners are given in world coordinates (well, 3d graphics coordinates). """ assert self.selectionBox is None p1, p2, p3, p4 = self.convert3dBoxToScreen(corner1, corner2) x1, y1 = p1 x2, y2 = p2 x3, y3 = p3 x4, y4 = p4 # TODO[#3]: Magic numbers bad. self.selectionBox = LineSegs("SelectionBox") self.selectionBox.setThickness(3.0) self.selectionBox.setColor(0.0, 1.0, 0.25, 1.0) self.selectionBox.move_to(x1, 0, y1) self.selectionBox.draw_to(x2, 0, y2) self.selectionBox.draw_to(x3, 0, y3) self.selectionBox.draw_to(x4, 0, y4) self.selectionBox.draw_to(x1, 0, y1) self.selectionBoxNode = self.render2d.attachNewNode( self.selectionBox.create())
def collisionevent(self, name, startx, starty, iterations): """Triggers an event upon a collision""" xnames = ["castle", "halloffame", "fifthline", "seventhline"] ynames = ["house", "tepee", "sixthline", "eighthline"] levelnames = ['castle', 'house', 'tepee', 'halloffame'] outsidenames = ['fifthline', 'sixthline', 'seventhline', 'eighthline'] self.name = LineSegs(name) self.name.moveTo(startx, starty, 0) x = startx y = starty for i in range(iterations): self.name.drawTo(x, y, 0) if name in xnames: x += 1 if name in ynames: y += 1 self.name.create() vertexlist = self.name.getVertices() coorlist = [] for i in range(len(vertexlist)): coorlist.append([vertexlist[i][0], vertexlist[i][1]]) moorecoor = [int(self.moore.getX()), int(self.moore.getY())] if (moorecoor in coorlist): if name in levelnames: base.graphicsEngine.removeWindow(self.win) self.secondWindow(name) if name in outsidenames: self.moore.setPos(0, 0, 0)
def __init__(self, input_file, output_file=None, prefabs=""): self.dir = os.path.dirname(input_file) self.depth = 0 self.cardmaker = CardMaker("image") self.cardmaker.set_frame(-0.5, 0.5, -0.5, 0.5) self.linesegs = LineSegs() self.textnode = TextNode("text") self.tilesheets = [] # Every tsx file loaded. self.tiles = {} # Every unique tile/card. self.node = NodePath("tmx_root") # load prefab models self.prefabs = {} if prefabs: loader = Loader.get_global_ptr() for prefab_node in loader.load_sync(prefabs).get_children(): prefab_node.clear_transform() self.prefabs[prefab_node.name] = NodePath(prefab_node) self.tmx = ET.parse(input_file).getroot() self.xscale = int(self.tmx.get("tilewidth")) self.yscale = int(self.tmx.get("tileheight")) self.size = [0, 0] self.load_group(self.tmx) if output_file: self.export_bam(output_file)
def move_map_avatar(self, move, stop): # print move # avatar is mapped assuming c_range of 0.5. What do I need to # change to use a different c_range? c_range of one is twice # the if move: avt = LineSegs() avt.setThickness(1) avt.setColor(1, 1, 1) # print 'last', self.last_avt avt.move_to(self.last_avt[0], -5, self.last_avt[1]) # print 'move', move new_move = [i + (j * self.avt_factor) for i, j in zip(self.last_avt, move)] # new_move = [i + j for i, j in zip(self.last_avt, move)] # would it be better to have a local stop condition? if stop[0]: new_move[0] = self.last_avt[0] # print 'stop x', self.last_avt[0] if stop[1]: new_move[1] = self.last_avt[1] # print 'stop y', self.last_avt[1] # print 'new', new_move self.last_avt = [new_move[0], new_move[1]] avt.draw_to(new_move[0], -5, new_move[1]) self.map_avt_node.append(self.render2d.attach_new_node(avt.create())) # print self.map_avt_node[-1] # can't let too many nodes pile up if len(self.map_avt_node) > 299: # removing the node does not remove the object from the list for i, j in enumerate(self.map_avt_node): j.removeNode() if i > 49: break del self.map_avt_node[0:50]
def createAxesCross(name, size, has_labels): def createAxisLine(label, color, draw_to): coords.setColor(color) coords.moveTo(0, 0, 0) coords.drawTo(draw_to) # Put the axis' name in the tip if label != "": text = TextNode(label) text.setText(label) text.setTextColor(color) axis_np = coords_np.attachNewNode(text) else: axis_np = coords_np.attachNewNode("") axis_np.setPos(draw_to) return axis_np coords_np = NodePath(name) coords = LineSegs() coords.setThickness(2) axis_x_np = createAxisLine("X" if has_labels else "", Color3D.RED, (size, 0, 0)) axis_y_np = createAxisLine("Y" if has_labels else "", Color3D.GREEN, (0, size, 0)) axis_z_np = createAxisLine("Z" if has_labels else "", Color3D.BLUE, (0, 0, size)) np = coords.create(True) coords_np.attachNewNode(np) return coords_np, axis_x_np, axis_y_np, axis_z_np
def make_circle(self, angle_deg=360): ls = LineSegs() angle_radians = np.deg2rad(angle_deg) # assume visual angle is approximately the same for x and y, # which probably is not true, maybe need to change #radius = 1 * Positions().visual_angle()[0] res = [1024, 768] # Screen size screen = [1337, 991] v_dist = 1219 # number of visual angles want circle radius to be # (so twice this is the x and y of the square) angle = 0.25 # visual angle returns degrees per pixel, so invert since # we want pixel per degree deg_per_pixel = visual_angle(screen, res, v_dist) x_radius = angle * 1 / deg_per_pixel[0] y_radius = angle * 1 / deg_per_pixel[0] for i in range(50): a = angle_radians * i / 49 y = y_radius * np.sin(a) #print y x = x_radius * np.cos(a) #print x ls.drawTo(x, self.depth, y) #node = ls.create() node = self.base.render.attachNewNode(ls.create(True)) return NodePath(node)
def __init__(self, root: NodePath) -> None: self.__roots = [root] self.__draw_nps = [] self.__circles = [] self.__thicknesses = [2.5] self.__line_segs = LineSegs() self.__line_segs.set_thickness(2.5)
def create(self, s): segs = LineSegs( ) segs.setThickness( 2.0 ) segs.setColor( Vec4(1,0,0,1) ) segs.moveTo( s.points[0] ) for p in s.points[1:]: segs.drawTo( p ) return segs.create( )
def __init__(self, parentNodePath): self.mainNodePath = NodePath("Lines") self.mainNodePath.reparentTo(parentNodePath) self.lineSegs = LineSegs() self.lineSegs.setThickness(1) self.lineNodePaths = [] self.points = []
def draw_line(self,p,col): line = LineSegs() line.setColor(col[0],col[1],col[2], 1) line.setThickness(2) line.moveTo(p[0],p[1],0) line.drawTo(p[2],p[3],0) line_node = line.create() node_path = NodePath(line_node) node_path.reparentTo(render)
def draw_edge(self,e,e_color): line_drawer = LineSegs('line_drawer') line_drawer.setColor(e_color) line_drawer.setThickness(1.5) line_drawer.moveTo(e.v1.pos) line_drawer.drawTo(e.v2.pos) edge_node = line_drawer.create() rendered_edge = self.render_root.attachNewNode(edge_node) self.render_nodes['edge_'+str(e.ID)] = rendered_edge
def draw(self, subdiv=1000): ls = LineSegs('mopath') p = Point3() for ti in range(subdiv): t = float(ti) / float(subdiv) * self.maxT tp = self.calcTime(t) self.xyzNurbsCurve.getPoint(tp, p) ls.drawTo(p) return NodePath(ls.create())
def drawLineSeg(self, loader, parent, start, end): lines = LineSegs() lines.setThickness(5.0) lines.setColor(VBase4(1, 0.5, 0.5, 1.0)) lines.moveTo(start) lines.drawTo(end) np = parent.attachNewNode(lines.create()) np.setDepthWrite(True) np.setDepthTest(True)
def __init__(self, manager, number): """ Initialises the drone as a bullet and panda object. :param manager: The drone manager creating this very drone. """ self.manager = manager # Drone manager handling this drone self.base = manager.base # Simulation self.crazyflie = None # object of real drone, if connected to one self.debug = False # If debugging info should be given self.in_flight = False # If currently in flight self.number = number # Number of drone in list # Every drone has its own vector to follow if an avoidance manouver has to be done self.avoidance_vector = LVector3f(random.uniform(-1, 1), random.uniform(-1, 1), random.uniform(-1, 1)).normalize() # Create bullet rigid body for drone drone_collision_shape = BulletSphereShape(self.COLLISION_SPHERE_RADIUS) self.drone_node_bullet = BulletRigidBodyNode("RigidSphere") self.drone_node_bullet.addShape(drone_collision_shape) self.drone_node_bullet.setMass(self.RIGID_BODY_MASS) # Set some values for the physics object self.drone_node_bullet.setLinearSleepThreshold( self.LINEAR_SLEEP_THRESHOLD) self.drone_node_bullet.setFriction(self.FRICTION) self.drone_node_bullet.setLinearDamping(self.LINEAR_DAMPING) # Attach to the simulation self.drone_node_panda = self.base.render.attachNewNode( self.drone_node_bullet) # ...and physics engine self.base.world.attachRigidBody(self.drone_node_bullet) # Add a model to the drone to be actually seen in the simulation drone_model = self.base.loader.loadModel( "models/drones/drone_florian.egg") drone_model.setScale(0.2) drone_model.reparentTo(self.drone_node_panda) # Set the position and target position to their default (origin) default_position = LPoint3f(0, 0, 0) self.drone_node_panda.setPos(default_position) self.target_position = default_position # Create a line renderer to draw a line from center to target point self.line_creator = LineSegs() # Then draw a default line so that the update function works as expected (with the removal) self.target_line_node = self.base.render.attachNewNode( self.line_creator.create(False)) # Create node for text self.drone_text_node_panda = None
def draw(self, start, end): segs = LineSegs() segs.set_color(*self.color) segs.moveTo(start._vec) # access to a protected member segs.drawTo(end._vec) # access to a protected member segs_node = segs.create() self.lines += [render.attachNewNode(segs_node)]
def plot_eye_trace(self, first_eye): # print 'plot trace' # if plotting too many eye positions, things slow down and # python goes into lala land. Never need more than 500, and # last 300 is definitely plenty, so every time it hits 500, # get rid of first 200. if len(self.eye_nodes) > 500: # print('get rid of eye nodes', len(self.eye_nodes)) # Since this just removes the node, but doesn't delete # the object in the list, can do this in a for loop, for index in range(200): self.eye_nodes[index].removeNode() # now get rid of the empty nodes in eye_nodes # print('new length', len(self.eye_nodes)) self.eye_nodes = self.eye_nodes[200:] # print('new length', len(self.eye_nodes)) eye = LineSegs() # eye.setThickness(2.0) eye.setThickness(2.0) # print 'last', last_eye # print 'now', self.current_eye_data eye.moveTo(first_eye[0], 55, first_eye[1]) for data_point in self.current_eye_data: eye.drawTo(data_point[0], 55, data_point[1]) # print('plotted eye', eye_data_to_plot) node = self.base.render.attachNewNode(eye.create(True)) node.show(BitMask32.bit(0)) node.hide(BitMask32.bit(1)) self.eye_nodes.append(node)
def show_window(self, target_pos): # draw line around target representing how close the subject has to be looking to get reward # print('show window around square', square_pos) tolerance = self.plot_variables[ self.text_dict['Tolerance']] / self.deg_per_pixel # print 'tolerance in pixels', tolerance # print 'square', square[0], square[2] eye_window = LineSegs() eye_window.setThickness(2.0) eye_window.setColor(1, 0, 0, 1) angle_radians = radians(360) for i in range(50): a = angle_radians * i / 49 y = tolerance * sin(a) x = tolerance * cos(a) eye_window.drawTo((x + target_pos[0], 55, y + target_pos[1])) # draw a radius line # eye_window.moveTo(square[0], 55, square[2]) # eye_window.drawTo(square[0], 55, square[2] + self.plot_variables[self.text_dict['Tolerance']]) # print 'distance drawn', self.distance((square[0], square[2]), (square[0], square[2] + self.plot_variables[self.text_dict['Tolerance']])) # True optimizes the line segments, which sounds useful node = self.base.render.attachNewNode(eye_window.create(True)) node.show(BitMask32.bit(0)) node.hide(BitMask32.bit(1)) self.eye_window.append(node)
def makeGizmoAxis(self, axis, text, textOffset=1.1): color = Vec4(0, 0, 0, 1) color[axis] = 1 pos = Vec3(0, 1, 0) if axis == 1: pos[1] = -pos[1] if axis == 1: textOffset = -textOffset direction = Vec3(0) direction[axis] = 1 segs = LineSegs() segs.setColor(color) segs.moveTo(Point3(0)) segs.drawTo(pos) np = self.np.attachNewNode(segs.create()) np.lookAt(direction) tn = TextNode('gizmoAxis%iText' % axis) tn.setTextColor(color) tn.setAlign(TextNode.ACenter) tn.setText(text) tnnp = np.attachNewNode(tn.generate()) tnnp.setY(textOffset) tnnp.setBillboardPointEye() tnnp.setScale(0.5) return np
def createMoveVis(self): # Instance each selected map object to the vis root for obj in base.selectionMgr.selectedObjects: instRoot = NodePath("instRoot") inst = obj.np.instanceTo(instRoot) instRoot.wrtReparentTo(self.toolVisRoot) self.xformObjects.append((obj, instRoot, inst)) # Show an infinite line along the axis we are moving the object # if we are using the 3D view if self.widget.activeAxis: axis = self.widget.activeAxis.axisIdx segs = LineSegs() col = Vec4(0, 0, 0, 1) col[axis] = 1.0 segs.setColor(col) p = Point3(0) p[axis] = -1000000 segs.moveTo(p) p[axis] = 1000000 segs.drawTo(p) self.axis3DLines = self.toolRoot.attachNewNode(segs.create()) self.axis3DLines.setLightOff(1) self.axis3DLines.setFogOff(1) self.widget.stash()
def create_lines(joints, color, thickness=5.0): for node, parent in joints: if parent is not None: lines = LineSegs() lines.setThickness(thickness) lines.setColor(color) lines.moveTo(0, 0, 0) lines.drawTo(node.getPos(parent)) np = parent.attachNewNode(lines.create()) np.setDepthWrite(True) np.setDepthTest(True)
def _drawSetpointLine(self): self.setpointNP.removeNode() ls = LineSegs() # ls.setThickness(1) ls.setColor(1.0, 1.0, 1.0, 1.0) ls.moveTo(self.getPos()) ls.drawTo(self.setpoint) node = ls.create() self.setpointNP = self.base.render.attachNewNode(node)
def _drawVelocityLine(self): self.velocityLineNP.removeNode() ls = LineSegs() # ls.setThickness(1) ls.setColor(0.0, 0.0, 1.0, 1.0) ls.moveTo(self.getPos()) ls.drawTo(self.getPos() + self.getVel()) node = ls.create() self.velocityLineNP = self.base.render.attachNewNode(node)
def draw(self, start, end): if self.car.fsm.getCurrentOrNextState() != 'Results': if self.car.name == game.player_car.name: segs = LineSegs() segs.set_color(*self.color) segs.moveTo(start) segs.drawTo(end) segs_node = segs.create() self.gnd_lines += [render.attachNewNode(segs_node)]
def _drawForceLine(self): self.forceLineNP.removeNode() ls = LineSegs() # ls.setThickness(1) ls.setColor(0.0, 1.0, 0.0, 1.0) ls.moveTo(self.getPos()) ls.drawTo(self.getPos() + self.rigidBody.getTotalForce() * 0.2) node = ls.create() self.forceLineNP = self.base.render.attachNewNode(node)
def _drawActualDroneLine(self): self.actualDroneLineNP.removeNode() ls = LineSegs() # ls.setThickness(1) ls.setColor(0.0, 0.0, 0.0, 1.0) ls.moveTo(self.getPos()) ls.drawTo(self.actualDronePosition) node = ls.create() self.actualDroneLineNP = self.base.render.attachNewNode(node)
def draw(self, subdiv=1000): """ Draws a quick and cheesy visualization of the Mopath using LineSegs. Returns the NodePath representing the drawing. """ ls = LineSegs('mopath') p = Point3() for ti in range(subdiv): t = float(ti) / float(subdiv) * self.maxT tp = self.calcTime(t) self.xyzNurbsCurve.getPoint(tp, p) ls.drawTo(p) return NodePath(ls.create())
def draw(self, subdiv = 1000): """ Draws a quick and cheesy visualization of the Mopath using LineSegs. Returns the NodePath representing the drawing. """ ls = LineSegs('mopath') p = Point3() for ti in range(subdiv): t = float(ti) / float(subdiv) * self.maxT tp = self.calcTime(t) self.xyzNurbsCurve.getPoint(tp, p) ls.drawTo(p) return NodePath(ls.create())
def __init__(self, manager, position: Vec3, uri="-1", printDebugInfo=False): self.base = manager.base self.manager = manager # The position of the real drone this virtual drone is connected to. # If a connection is active, this value is updated each frame. self.realDronePosition = Vec3(0, 0, 0) self.canConnect = False # true if the virtual drone has a uri to connect to a real drone self.isConnected = False # true if the connection to a real drone is currently active self.uri = uri if self.uri != "-1": self.canConnect = True self.randVec = Vec3(random.uniform(-1, 1), random.uniform(-1, 1), random.uniform(-1, 1)) # add the rigidbody to the drone, which has a mass and linear damping self.rigidBody = BulletRigidBodyNode("RigidSphere") # derived from PandaNode self.rigidBody.setMass(self.RIGIDBODYMASS) # body is now dynamic self.rigidBody.addShape(BulletSphereShape(self.RIGIDBODYRADIUS)) self.rigidBody.setLinearSleepThreshold(0) self.rigidBody.setFriction(0) self.rigidBody.setLinearDamping(self.LINEARDAMPING) self.rigidBodyNP = self.base.render.attachNewNode(self.rigidBody) self.rigidBodyNP.setPos(position) # self.rigidBodyNP.setCollideMask(BitMask32.bit(1)) self.base.world.attach(self.rigidBody) # add a 3d model to the drone to be able to see it in the 3d scene model = self.base.loader.loadModel(self.base.modelDir + "/drones/drone1.egg") model.setScale(0.2) model.reparentTo(self.rigidBodyNP) self.target = position # the long term target that the virtual drones tries to reach self.setpoint = position # the immediate target (setpoint) that the real drone tries to reach, usually updated each frame self.waitingPosition = Vec3(position[0], position[1], 0.7) self.printDebugInfo = printDebugInfo if self.printDebugInfo: # put a second drone model on top of drone that outputs debug stuff model = self.base.loader.loadModel(self.base.modelDir + "/drones/drone1.egg") model.setScale(0.4) model.setPos(0, 0, .2) model.reparentTo(self.rigidBodyNP) # initialize line renderers self.targetLineNP = self.base.render.attachNewNode(LineSegs().create()) self.velocityLineNP = self.base.render.attachNewNode(LineSegs().create()) self.forceLineNP = self.base.render.attachNewNode(LineSegs().create()) self.actualDroneLineNP = self.base.render.attachNewNode(LineSegs().create()) self.setpointNP = self.base.render.attachNewNode(LineSegs().create())
def update_LFP(self, dt, last_lfp, lfp_trace, offset, gen_lfp): # lfp data is taken at 1000Hz, and dt is the number of seconds since # the last frame was flipped, so plot number of points = dt * 1000 lfp = LineSegs() lfp.setThickness(1.0) #print('points to plot', int(dt * 1000)) #self.lfp_test += int(dt * 1000) #print('points so far', self.lfp_test) for i in range(int(dt * 1000)): try: last_lfp.append((next(gen_lfp) * self.lfp_gain) + offset) #last_lfp_x += 0.05 # only plotting 200 data points at a time while len(last_lfp) > 3500: last_lfp.pop(0) except StopIteration: #print('done with lfp') break if lfp_trace: lfp_trace[0].removeNode() lfp_trace.pop(0) lfp.moveTo(self.start_x_trace, 55, last_lfp[0]) x = self.start_x_trace for i in last_lfp: x += .1 lfp.drawTo(x, 55, i) node = self.base.pixel2d.attachNewNode(lfp.create()) lfp_trace.append(node)
def drawHilbertCurve(pts): # 2n + c for p in range(len(pts) - 1): # n seg = LineSegs() # c seg.setThickness(3) # c seg.draw_to(pts[p][0], 0, pts[p][2]) # c seg.draw_to(pts[p + 1][0], 0, pts[p + 1][2]) # c node = seg.create() # c nodes.append(node) # c for node in nodes: # n base.aspect2d.attach_new_node(node) # c
def line_small_lakes(self): for l in range(len(small_lake_lines)): line = LineSegs() line.setColor(0,0,0, 1) line.setThickness(2) for n in range(len(small_lake_lines[l])): x = (small_lake_nodes[small_lake_lines[l][n]]["x"]-map_center[0])*amplification y = (small_lake_nodes[small_lake_lines[l][n]]["y"]-map_center[1])*amplification if n == 0: line.moveTo(x,y,0) else: line.drawTo(x,y,0) line_node = line.create() node_path = NodePath(line_node) node_path.reparentTo(render)
def __make_border__(cls, parent, thickness, color, l, r , b, t): moveto_drawto = ( ((l,0,t), (l,0,b)), ((r,0,t), (r,0,b)), ((l,0,b), (r,0,b)), ((l,0,t), (r,0,t)), ) for moveto, drawto in moveto_drawto: Border = LineSegs() Border.setThickness(thickness) Border.setColor(*color) Border.moveTo(*moveto) Border.drawTo(*drawto) b = parent.attachNewNode(Border.create()) b.setBin(*cls.DRAW_ORDER['border'])
def __init__(self, widget, axis): TransformWidgetAxis.__init__(self, widget, axis) segs = LineSegs() segs.setThickness(2) vertices = LEUtils.circle(0, 0, 1, 64) for i in range(len(vertices)): x1, y1 = vertices[i] x2, y2 = vertices[(i + 1) % len(vertices)] segs.moveTo(x1, 0, y1) segs.drawTo(x2, 0, y2) self.axisCircle = self.attachNewNode(segs.create()) self.axisCircle.setAntialias(AntialiasAttrib.MLine)
def __init__(self, name: str = 'cube_mesh', wireframe_thickness: float = 5) -> None: self.name = name self.__vertex_data_format = GeomVertexFormat.getV3n3() self.__vertex_data = GeomVertexData(name, self.__vertex_data_format, Geom.UHStatic) self.geom = Geom(self.__vertex_data) self.__triangles = GeomTriangles(Geom.UHStatic) self.__triangle_data = self.__triangles.modifyVertices() self.__vertex = GeomVertexWriter(self.__vertex_data, 'vertex') self.__normal = GeomVertexWriter(self.__vertex_data, 'normal') self.__face_count = 0 def add_face(face: Face) -> None: self.__make_face(face) self.__make_face(Face.LEFT) self.__make_face(Face.RIGHT) self.__make_face(Face.BACK) self.__make_face(Face.FRONT) self.__make_face(Face.BOTTOM) self.__make_face(Face.TOP) self.__triangles.close_primitive() self.geom.add_primitive(self.__triangles) def is_connected(x, y, z, x1, y1, z1): return (abs(x - x1) == 1 and abs(y - y1) != 1 and abs(z - z1) != 1) or \ (abs(x - x1) != 1 and abs(y - y1) == 1 and abs(z - z1) != 1) or \ (abs(x - x1) != 1 and abs(y - y1) != 1 and abs(z - z1) == 1) ls = LineSegs() ls.set_thickness(wireframe_thickness) arr_x = [0, 0, 0, 0, 1, 1, 1, 1] arr_y = [0, 0, 1, 1, 1, 1, 0, 0] arr_z = [0, -1, -1, 0, 0, -1, -1, 0] for pos1 in range(len(arr_x) - 1): for pos2 in range(pos1, len(arr_x)): x = arr_x[pos1] y = arr_y[pos1] z = arr_z[pos1] x1 = arr_x[pos2] y1 = arr_y[pos2] z1 = arr_z[pos2] if (is_connected(x, y, z, x1, y1, z1)): ls.move_to(x, y, z) ls.draw_to(x1, y1, z1) self.__wireframe_node = ls.create()
def drawLine(self,fromP,toP,thickness=2,color=(1,0,0,1),autoClear=True): if autoClear: try: self.debugLineNP.removeNode() except: pass self.debugLine=LineSegs("DebugLine") self.debugLine.reset() self.debugLine.setThickness(thickness) self.debugLine.setColor(color) self.debugLine.moveTo(fromP) self.debugLine.drawTo(toP) self.debugLineNode=self.debugLine.create() self.debugLineNP=NodePath(self.debugLineNode) self.debugLineNP.reparentTo(self.Base.render) return self.debugLineNP
def fire_laser(self, panda3dworld, entity_id): now = globalClock.getRealTime() if now - self.last_time_laser_fired < self.laser_reload_time: if defines.ENTITIES[entity_id]['CATEGORY'] == 'ship': panda3dworld.keys["fire"] = 0 elif defines.ENTITIES[entity_id]['CATEGORY'] == 'ship2': panda3dworld.keys["p2fire"] = 0 else: self.last_time_laser_fired = now pos = defines.ENTITIES[entity_id]["NODE"].getPos() angle = 360 - defines.ENTITIES[entity_id]["NODE"].getR() # print angle start_pos_x = pos.x + 0.5 * cos(angle* pi/180) start_pos_y = pos.z + 0.5 * sin(angle* pi/180) pos_x = pos.x + 10 * cos(angle* pi/180) pos_y = pos.z + 10 * sin(angle* pi/180) callback = test_laser_collision(start_pos_x, start_pos_y, pos_x, pos_y) if callback.hit: pos_x = callback.point.x pos_y = callback.point.y for contact_id, entity in defines.ENTITIES.items(): if entity['BODY'].fixtures[0] == callback.fixture: if entity['CATEGORY'] == "ship" or entity['CATEGORY'] == "ship2" or entity['CATEGORY'] == "asteroid": entity['SHIELD'] -= 10 elif entity['CATEGORY'] == "bullet": defines.ENTITIES[contact_id]['NODE'].removeNode() defines.ENTITIES[contact_id]['PHYSIC_NODE'].removeNode() defines.world.DestroyBody(defines.ENTITIES[contact_id]['BODY']) del defines.ENTITIES[contact_id] ls = LineSegs("lines") ls.setColor(1,1,1,1) ls.drawTo(start_pos_x, 55, start_pos_y) ls.drawTo(pos_x, 55, pos_y) laser = ls.create(False) laserPath = render.attachNewNode(laser) laserPath.setBin("unsorted", 0) laserPath.setDepthTest(False) sound = base.loader.loadSfx("sounds/laser.ogg") sound.setVolume(0.2) sound.play() taskMgr.doMethodLater(0.05, remove_laser_task, 'remove laser', extraArgs=[laserPath], appendTask=True) defines.ENTITIES[entity_id]['ENERGY'] -= 5 if defines.ENTITIES[entity_id]['CATEGORY'] == 'ship': panda3dworld.keys["fire"] = 0 elif defines.ENTITIES[entity_id]['CATEGORY'] == 'ship2': panda3dworld.keys["p2fire"] = 0
def fire_laser(self, panda3dworld, entity_id): now = globalClock.getRealTime() if now - self.last_time_laser_fired < self.laser_reload_time: if defines.ENTITIES[entity_id]["CATEGORY"] == "ship": panda3dworld.keys["fire"] = 0 elif defines.ENTITIES[entity_id]["CATEGORY"] == "ship2": panda3dworld.keys["p2fire"] = 0 else: self.last_time_laser_fired = now pos = defines.ENTITIES[entity_id]["NODE"].getPos() angle = 360 - defines.ENTITIES[entity_id]["NODE"].getR() # print angle start_pos_x = pos.x + 0.5 * cos(angle * pi / 180) start_pos_y = pos.z + 0.5 * sin(angle * pi / 180) pos_x = pos.x + 10 * cos(angle * pi / 180) pos_y = pos.z + 10 * sin(angle * pi / 180) callback = test_laser_collision(start_pos_x, start_pos_y, pos_x, pos_y) if callback.hit: pos_x = callback.point.x pos_y = callback.point.y for contact_id, entity in defines.ENTITIES.items(): if entity["BODY"].fixtures[0] == callback.fixture: if ( entity["CATEGORY"] == "ship" or entity["CATEGORY"] == "ship2" or entity["CATEGORY"] == "asteroid" ): entity["SHIELD"] -= 10 elif entity["CATEGORY"] == "bullet": defines.ENTITIES[contact_id]["NODE"].removeNode() defines.ENTITIES[contact_id]["PHYSIC_NODE"].removeNode() defines.world.DestroyBody(defines.ENTITIES[contact_id]["BODY"]) del defines.ENTITIES[contact_id] ls = LineSegs("lines") ls.setColor(1, 1, 1, 1) ls.drawTo(start_pos_x, 55, start_pos_y) ls.drawTo(pos_x, 55, pos_y) laser = ls.create(False) laserPath = render.attachNewNode(laser) laserPath.setBin("unsorted", 0) laserPath.setDepthTest(False) sound = base.loader.loadSfx("sounds/laser.ogg") sound.setVolume(0.2) sound.play() taskMgr.doMethodLater(0.05, remove_laser_task, "remove laser", extraArgs=[laserPath], appendTask=True) defines.ENTITIES[entity_id]["ENERGY"] -= 5 if defines.ENTITIES[entity_id]["CATEGORY"] == "ship": panda3dworld.keys["fire"] = 0 elif defines.ENTITIES[entity_id]["CATEGORY"] == "ship2": panda3dworld.keys["p2fire"] = 0
class LineEffects(): def __init__(self): self.linesegs = LineSegs("lines") self.bullet = None def remove_bullet(self): if self.bullet: self.bullet.detach_node() def draw_bullet(self, a, b, color): if color == 1: color = (1,0,1,1) elif color == 2: color = (0,1,1,1) else: color = (1,1,1,1) self.linesegs.set_color(color) a = a.get_pos(render) self.linesegs.move_to(a) self.linesegs.draw_to(b) lines = self.linesegs.create() self.bullet = render.attach_new_node(lines) impact = base.icons["impact"] impact = impact.copy_to(self.bullet) impact.set_pos(b)
def createCurve(self): self.curve = [] self.progress = 0 self.showCurve = True lineThickness = 2 ls = LineSegs("LogSpiral") ls.setThickness(lineThickness) iteration_count = 10001 step_delta = 0.001 curve_length = step_delta * iteration_count for index in np.arange(1 + (curve_length * self.truncate_percentage), curve_length, step_delta): # Calculate curve point position spiral_x = self.radius_scale * self.a * pow( math.e, self.k * index) * math.cos(index) spiral_y = self.radius_scale * self.a * pow( math.e, self.k * index) * math.sin(index) spiral_z = self.height_scale * self.height_scale * math.log( index, math.e) + self.lower_bound if (self.showCurve): ls.drawTo(spiral_x, spiral_y, spiral_z) self.curve.append(Vec3(spiral_x, spiral_y, spiral_z)) self.curve.reverse() if (self.curve_segment != None): self.curve_segment.removeNode() node = ls.create(dynamic=False) body = BulletRigidBodyNode("lsRB") bodyNP = self.worldNP.attachNewNode(body) self.curve_segment = bodyNP.attachNewNode(node) if (not self.show_curve): self.curve_segment.hide()
def draw_path(self, current_position, path): from panda3d.core import LineSegs, Vec4, Vec3 path = [Vec3(*v) for v in path] segments = LineSegs() segments.set_thickness(2.0) segments.set_color((1, 1, 0, 1)) segments.move_to(current_position) for point in path: segments.draw_to(point) if self._path_node: self._path_node.remove_node() node = segments.create() self._path_node = render.attach_new_node(node) self._replan_timer = Timer(1.5) self._replan_timer.on_target = self._replan
def walkJointHierarchy(self, actor, part, parentNode = None, indent = ""): if isinstance(part, CharacterJoint): np = actor.exposeJoint(None, 'modelRoot', part.getName()) if parentNode and parentNode.getName() != "root": lines = LineSegs() lines.setThickness(3.0) lines.setColor(random.random(), random.random(), random.random()) lines.moveTo(0, 0, 0) lines.drawTo(np.getPos(parentNode)) lnp = parentNode.attachNewNode(lines.create()) lnp.setBin("fixed", 40) lnp.setDepthWrite(False) lnp.setDepthTest(False) parentNode = np for child in part.getChildren(): self.walkJointHierarchy(actor, child, parentNode, indent + " ")
def __init__(self, app): self.time = "0" self.fps = "0" self.fileInput = "" self.selectedComPort = "" self.selectedEntryId = 0 self.entryDict = {} self.timeDelta = 0 self.instVel = 0.0 self.velocityLogs = [] self.currentLogQuat = ["!"] #TODO remove this self.lastLogQuat = [] self.velLines = LineSegs("lines") self.velLines.setColor(0, 1, 1, 0) self.db = app.db segsnode = self.velLines.create(False) self.velocityLinesNode = app.render.attachNewNode(segsnode) self.simulatorTextDisplays(app) self.simulatorInteractives(app)
def __init__(self): self.cardmaker = CardMaker("card") self.linesegs = LineSegs("line") self.pointmaker = PointMaker("point") self.shapes = {} self.make_lod() self.pointmaker.add() self.shapes["Point"] = self.pointmaker.wrap_up() self.pointmaker.new()
def update_avt_p(self, t_time): avt = LineSegs() avt.setThickness(5) avt.setColor(self.avatar_color[0], self.avatar_color[1], self.avatar_color[2]) group_avatar = [] while self.avatar_pt[-1] < t_time: group_avatar.append(self.avatar_pos.pop()) # print points self.avatar_pt.pop() if not self.avatar_pt: break # print('positions', group_avatar) if group_avatar: avt.moveTo(self.last_avt[0], self.drawing_layer, self.last_avt[1]) self.last_avt = [i * self.scale_factor for i in group_avatar[0]] for i in group_avatar: # print(i[0], i[1], i[2]) pos = [j * self.scale_factor for j in i] avt.drawTo(pos[0], self.drawing_layer, pos[1]) self.avatar_node.append(self.base.render.attachNewNode(avt.create()))
def __init__(self): BareBonesEditor.__init__(self) camera.setPos( 0.0, 0.0, 50.0) camera.lookAt(0) # hole1 = HorseShoeCentered() # hole2 = SquareOffCenter() # holes = [] # holes.append(hole1) # holes.append(hole2) # # map10 = SquareMap10x10() # mapWholes = [] # mapWholes.append(map10) # mapWholes.append(holes) # for i in mapWholes: # print "mapWholes", i # # mesh_trilator = makeTriMesh(mapWholes[0], mapWholes[1]) # , holes) ############ mapThrs = TheirMap() # for i in mapThrs: # print "mapThrs", i mesh_trilator = makeTriMesh(mapThrs[0], mapThrs[1]) # , holes) ########### aLst = AdjacencyList(mesh_trilator[1]) # for i in aLst.aLst: # print i indsNP = drawInds(aLst.adjLst) # put text on each triangle indsNP.setPos(0, 0, .2) indsNP.setColor(0, 1, 1, 1) mapNP = render.attachNewNode(mesh_trilator[0]) wireNP = render.attachNewNode('wire') wireNP.setPos(0, 0, .1) wireNP.setColor(1, 0, 0, 1) wireNP.setRenderMode(RenderModeAttrib.MWireframe, .5, 0) mapNP.instanceTo(wireNP) # aStar = TriangulationAStar(aLst.adjLst, Point3(-11, -11, 0), Point3(11, 11, 0))aLst.adjLst[11].getCenter() aStar = TriangulationAStarR(aLst.adjLst, Point3(-11, 11, 0), aLst.adjLst[17].getCenter(), radius=.55) path = aStar.AStar() print "\n\nEND PATH\n", path # https://www.panda3d.org/manual/index.php?title=Putting_your_new_geometry_in_the_scene_graph&diff=prev&oldid=6303 linesegs = LineSegs("lines") linesegs.setColor(0, 0, 1, 1) linesegs.setThickness(5) for p in path: linesegs.drawTo(p) node = linesegs.create(False) nodePath = render.attachNewNode(node) nodePath.setZ(.15)
def plot_circles(self): if self.center_model is None: # center self.center_model = self.base.loader.loadModel('ball') self.center_model.setPos(self.weight_center[0], 25, self.weight_center[1]) self.center_model.setScale(0.2) self.center_model.setColor(1, 0, 1, 1) self.center_model.reparentTo(self.base.render) # first circle self.high_circle = LineSegs() self.high_circle.setThickness(2.0) self.high_circle.setColor(0, 1, 1, 1) # second circle self.middle_circle = LineSegs() self.middle_circle.setThickness(2.0) self.middle_circle.setColor(0, 1, 0, 1) if self.high_node is not None: self.high_node.detachNode() self.middle_node.detachNode() self.center_model.setPos(self.weight_center[0], 25, self.weight_center[1]) angle_radians = radians(360) for i in range(50): a = angle_radians * i / 49 y = self.high_radius * sin(a) x = self.high_radius * cos(a) self.high_circle.drawTo((x + self.weight_center[0], 25, y + self.weight_center[1])) self.high_node = self.base.render.attach_new_node(self.high_circle.create(True)) for i in range(50): a = angle_radians * i / 49 y = self.middle_radius * sin(a) x = self.middle_radius * cos(a) self.middle_circle.drawTo((x + self.weight_center[0], 25, y + self.weight_center[1])) self.middle_node = self.base.render.attach_new_node(self.middle_circle.create(True))
def selection_ring_create(self, segments = 16,size = 1.0): ls = LineSegs() ls.setThickness(2) ls.setColor(0.8,0.8,0.8) radians = deg2Rad(360) for i in range(segments+1): a = radians * i / segments y = math.sin(a)*size x = math.cos(a)*size ls.drawTo(x, y, 0.2) node = ls.create() return NodePath(node)
def __init__(self): ShowBase.__init__(self) #BareBonesEditor.__init__(self) PanditorDisableMouseFunc() camera.setPos( 0.0, 0.0, 50.0) camera.lookAt(0.0) PanditorEnableMouseFunc() # mapThrs = TheirMap() mapThrs = CrossWithHole() print mapThrs[1] mesh_trilator = makeTriMesh(mapThrs[0], mapThrs[1]) # , holes) ########### aLst = AdjacencyList(mesh_trilator[1]) indsNP = drawInds(aLst.adjLst) # put text on each triangle indsNP.setPos(0.0, 0.0, .2) indsNP.setColor(0.0, 1.0, 1.0, 1.0) mapNP = render.attachNewNode(mesh_trilator[0]) wireNP = render.attachNewNode('wire') wireNP.setPos(0.0, 0.0, .1) wireNP.setColor(1.0, 0.0, 0.0, 1) wireNP.setRenderMode(RenderModeAttrib.MWireframe, .5, 0) mapNP.instanceTo(wireNP) aStar = TriangulationAStarR(aLst.adjLst, Point3(0.0, -5.0, 0.0), Point3(0.0, 5.5, 0.0), radius=0.0) # aStar = TriangulationAStarR(aLst.adjLst, Point3(aLst.adjLst[17].getCenter() + Point3(5, 0, 0)), Point3(0, 11, 0), radius=.55) # aStar = TriangulationAStarR(aLst.adjLst, Point3(-5, 4, 0), Point3(aLst.adjLst[17].getCenter() + Point3(5, 0, 0)), radius=.55) path = aStar.AStar() print "\n\nEND PATH\n", path # https://www.panda3d.org/manual/index.php?title=Putting_your_new_geometry_in_the_scene_graph&diff=prev&oldid=6303 linesegs = LineSegs("lines") linesegs.setColor(0, 0, 1, 1) linesegs.setThickness(5) for p in path: linesegs.drawTo(p) node = linesegs.create(False) nodePath = render.attachNewNode(node) nodePath.setZ(.15)
def task_mouse_place(self,task): if base.mouseWatcherNode.isButtonDown(MouseButton.one()): self.placing_object = True self.place_pos = (self.anchor_x,self.anchor_y) self.line_dir.remove() ls = LineSegs() ls.move_to(self.anchor_x,self.anchor_y,1) ls.draw_to(self.model.getX(),self.model.getY(),1) node = ls.create() angle1 = math.atan2(self.anchor_y - self.anchor_y,self.anchor_x - self.anchor_x+50) angle2 = math.atan2(self.anchor_y - self.model.getY(),self.anchor_x - self.model.getY()); final_angle = angle1-angle2; self.model.setHpr(final_angle,0,0) self.line_dir = NodePath(node) self.line_dir.reparentTo(render) return task.again else: self.line_dir.hide() taskMgr.add(self.task_mouse_press_check, "checkMousePress") return task.done
def getWidthAcrossEdges(self, searchTri, edge1, edge2): """Calculates the path width through this triangle. Edge1 and edge2 are the edges being crossed.""" # this calculates the distance from the point shared by edge1 and edge2 to the nearest obstacle # 1st it sets the width of the triangle to the shortest edge being crossed # then it searches across the third edge to see if there is an obstacle closer than its own vertices # yes that can happen!!! for p in edge1: if p in edge2: pt = p # get the point that both edges share. This is the point we are measuring the distance to. if edge2 == searchTri.getEdge12() and searchTri.n12 is None\ or edge2 == searchTri.getEdge23() and searchTri.n23 is None\ or edge2 == searchTri.getEdge13() and searchTri.n13 is None: # if edge2 is on a constrained side swap it for edge1 # doint this makes it so we only have to check edge1. It cuts our code for the next step in half. tmp = edge2 edge2 = edge1 edge1 = tmp # TODO make this work with edge 1, 2, & 3 and local vars nayb 1, 2, & 3 so it's not sooo much code if edge1 == searchTri.getEdge12() and searchTri.n12 is None: if edge2 == searchTri.getEdge23() and searchTri.n23 is None: # Both search edges are constrained, so the width of the triangle is the width of the third edge. return getDistance(searchTri.getPoint1(), searchTri.getPoint3()) elif edge2 == searchTri.getEdge13() and searchTri.n13 is None: # ditto return getDistance(searchTri.getPoint2(), searchTri.getPoint3()) else: # the other edge is not constrained, so the initial width # should be the shortest of either the length of this unconstrained edge # or the distance from its non-shared point to the constrained side if edge2 == searchTri.getEdge23(): minWidth = getDistance(searchTri.getPoint2(), searchTri.getPoint3()) # we also need to find the end point for the next step if pt != edge2[0]: otherPt = edge2[0] else: otherPt = edge2[1] else: # the other (non-constrained) edge is 13 minWidth = getDistance(searchTri.getPoint1(), searchTri.getPoint3()) # we also need to find the end point for the next step if pt != edge2[0]: otherPt = edge2[0] else: otherPt = edge2[1] # so now get the distance from the end to the other (constrained) edge debugEdgeConstrained = edge1 ############################# DEBUG distAcrossTri = getDistance(getNearestPointOnLine(otherPt, edge1), otherPt) if distAcrossTri < minWidth: minWidth = distAcrossTri # ########################### this next bit seems off else: minWidth = getDistance(searchTri.getPoint1(), searchTri.getPoint3()) elif edge1 == searchTri.getEdge13() and searchTri.n13 is None: if edge2 == searchTri.getEdge23() and searchTri.n23 is None: # both are constrained, so the width of the triangle is the length of the unconstrained edge return getDistance(searchTri.getPoint1(), searchTri.getPoint2()) elif edge2 == searchTri.getEdge12() and searchTri.n12 is None: return getDistance(searchTri.getPoint2(), searchTri.getPoint3()) else: # the other edge is not constrained, so the initial width # should be either the length of this unconstrained edge # or the distance from its non-shared point to the constrained side, whichever is shortest if edge2 == searchTri.getEdge23(): minWidth = getDistance(searchTri.getPoint2(), searchTri.getPoint3()) # we also need to find the end point for the next step if pt != edge2[0]: otherPt = edge2[0] else: otherPt = edge2[1] else: # the other (non-constrained) edge is 12 minWidth = getDistance(searchTri.getPoint1(), searchTri.getPoint2()) # we also need to find the end point for the next step if pt != edge2[0]: otherPt = edge2[0] else: otherPt = edge2[1] # so now get the distance from the end to the other (constrained) edge debugEdgeConstrained = edge1 ############################# DEBUG distAcrossTri = getDistance(getNearestPointOnLine(otherPt, edge1), otherPt) if distAcrossTri < minWidth: minWidth = distAcrossTri # ########################### this next bit seems off else: minWidth = getDistance(searchTri.getPoint1(), searchTri.getPoint3()) elif edge1 == searchTri.getEdge23() and searchTri.n23 is None: if edge2 == searchTri.getEdge13() and searchTri.n13 is None: # both are constrained, so the width of the triangle is the length of the unconstrained edge return getDistance(searchTri.getPoint1(), searchTri.getPoint2()) elif edge2 == searchTri.getEdge12() and searchTri.n12 is None: return getDistance(searchTri.getPoint1(), searchTri.getPoint2()) else: # the other edge is not constrained, so the initial width # should be either the length of this unconstrained edge # or the distance from its non-shared point to the constrained side, whichever is shortest if edge2 == searchTri.getEdge12(): minWidth = getDistance(searchTri.getPoint1(), searchTri.getPoint2()) # we also need to find the end point for the next step if pt != edge2[0]: otherPt = edge2[0] else: otherPt = edge2[1] else: # the other (non-constrained) edge is 13 minWidth = getDistance(searchTri.getPoint1(), searchTri.getPoint3()) # we also need to find the end point for the next step if pt != edge2[0]: otherPt = edge2[0] else: otherPt = edge2[1] # so now get the distance from the end to the other (constrained) edge debugEdgeConstrained = edge1 ############################# DEBUG distAcrossTri = getDistance(getNearestPointOnLine(otherPt, edge1), otherPt) if distAcrossTri < minWidth: minWidth = distAcrossTri # ########################### this next bit seems off else: minWidth = getDistance(searchTri.getPoint1(), searchTri.getPoint3()) else: # edge1 and edge2 are not constrained # Get the width of the shortest of the these two edges minWidth = min((edge1[0] - edge1[1]).length(), (edge2[0] - edge2[1]).length()) # if minWidth < 1: # print "minWidth < 1 pt = ", pt, " || otherPt = ", otherPt, " || debugConstrained = ", debugEdgeConstrained # save these so we don't consider them as nearest points later, else every triangle's width will be 0 edgePts = [edge1[0], edge1[1]] edgePts.extend([edge2[0], edge2[1]]) # FINALLY search across the third edge for a constrained edge # that's closer (to the shared point) than this triangle's vertices # print self.adjLst # ################################################### counter = 0 # ################################################### for t in range(0, len(self.adjLst)): # if the edge is constrained, check to see if it narrows the width of this path tri = self.adjLst[t] # print tri if tri.selfInd != searchTri.selfInd: if tri.n12 is None: # if the constrained edge, is on the opposite side # from the point shared between the shared edges i.e. for point C check across edge c nearest = getNearestPointOnLine(pt, [tri.tri[0], tri.tri[1]], True) # print tri.selfInd, " 12 is none nearest", nearest if isPointInWedge(nearest, edge1, edge2) and nearest not in edgePts: # and it's in the wedge, check the distance against the current minimum width newW = getDistance(pt, nearest) # print "in wedge newW", newW if newW < minWidth: # #################################################### from panda3d.core import LineSegs # print "pt = ", pt, " || nearest = ", nearest counter += 1 linesegs2 = LineSegs("lines" + str(counter)) linesegs2.setColor(0, 1, 1, 1) linesegs2.setThickness(5) linesegs2.drawTo(pt) linesegs2.drawTo(nearest) node2 = linesegs2.create(False) nodePath = render.attachNewNode(node2) nodePath.setZ(.25) # #################################################### minWidth = newW # do likewise for the other edges if tri.n23 is None: nearest = getNearestPointOnLine(pt, [tri.tri[1], tri.tri[2]], True) # print tri.selfInd, " 23 is none nearest", nearest if isPointInWedge(nearest, edge1, edge2) and nearest not in edgePts: newW = getDistance(pt, nearest) # print "in wedge newW", newW if newW < minWidth: # #################################################### from panda3d.core import LineSegs # print "pt = ", pt, " || nearest = ", nearest counter += 1 linesegs2 = LineSegs("lines" + str(counter)) linesegs2.setColor(0, 1, 1, 1) linesegs2.setThickness(5) linesegs2.drawTo(pt) linesegs2.drawTo(nearest) node2 = linesegs2.create(False) nodePath = render.attachNewNode(node2) nodePath.setZ(.25) # #################################################### minWidth = newW if tri.n13 is None: nearest = getNearestPointOnLine(pt, [tri.tri[0], tri.tri[2]], True) # print tri.selfInd, " 13 is none nearest", nearest if isPointInWedge(nearest, edge1, edge2) and nearest not in edgePts: newW = getDistance(pt, nearest) # print "in wedge newW", newW if newW < minWidth: # #################################################### from panda3d.core import LineSegs # print "pt = ", pt, " || nearest = ", nearest counter += 1 linesegs2 = LineSegs("lines" + str(counter)) linesegs2.setColor(0, 1, 1, 1) linesegs2.setThickness(5) linesegs2.drawTo(pt) linesegs2.drawTo(nearest) node2 = linesegs2.create(False) nodePath = render.attachNewNode(node2) nodePath.setZ(.25) # #################################################### minWidth = newW return minWidth
class WartsApp(ShowBase): def __init__(self, graphicsInterface, backend, gameState): ShowBase.__init__(self) self.graphicsInterface = graphicsInterface self.backend = backend self.gameState = gameState self.groundNodes = None self.firstTick = True # This is available as a global, but pylint gives an undefined-variable # warning if we use it that way. Looking at # https://www.panda3d.org/manual/index.php/ShowBase # I would have thought we could reference it as either # self.globalClock, direct.showbase.ShowBase.globalClock, or possibly # direct.showbase.globalClock, but none of those seems to work. To # avoid the pylint warnings, create self.globalClock manually. self.globalClock = ClockObject.getGlobalClock() # Set up event handling. self.mouseState = {} self.keys = {} self.setupEventHandlers() # Set up camera control. self.cameraHolder = self.render.attachNewNode('CameraHolder') self.cameraHolder.setPos(0, 0, 100) self.prevCameraHpr = (0, -80, 0) self.usingCustomCamera = True self.setCameraCustom() self.prevMousePos = None self.selectionBox = None self.selectionBoxNode = None self.selectionBoxOrigin = None # Define the ground plane by a normal (+z) and a point (the origin). self.groundPlane = core.Plane(core.Vec3(0, 0, 1), core.Point3(0, 0, 0)) graphicsInterface.graphicsReady(self) def cleanup(self): pass # For backward compatibility. # TODO[#84]: Remove when old graphics goes away; have backend just call # tick() directly. def interfaceMessage(self, data): message = deserializeMessage(data) if isinstance(message, messages.Tick): self.tick() # Ignore everything else. def tick(self): # TODO: Multiple levels of log.debug. For now, this is too spammy, so # skip it. # log.debug("Graphics: tick()") if self.firstTick: if not self.gameState.hasSize: log.error("GameState must be assigned a size before first " "tick().") return width, height = self.gameState.sizeInChunks self.groundNodes = [[None for _x in range(height)] for _y in range(width)] for cx in range(width): for cy in range(height): self.addGround((cx, cy), self.gameState.groundTypes[cx][cy]) self.firstTick = False # For now, just call this every tick. Optimize later. self.rescanUnits() def addGround(self, chunkIndex, terrainType): cx, cy = chunkIndex wPos = Coord.fromCBU(chunk=(chunkIndex)) if terrainType == 0: modelName = "green-ground.egg" else: modelName = "red-ground.egg" if terrainType != 1: log.warn("Unrecognized terrain type %d", terrainType) gPos1 = worldToGraphicsPos(wPos) gPos2 = worldToGraphicsPos(wPos + Distance.fromCBU(chunk=(1,1))) # Figure out where we want the tile. goalCenterX = 0.5 * (gPos2[0] + gPos1[0]) goalCenterY = 0.5 * (gPos2[1] + gPos1[1]) goalWidthX = abs(gPos2[0] - gPos1[0]) goalWidthY = abs(gPos2[1] - gPos1[1]) model = self.loader.loadModel(getModelPath(modelName)) # Put the model in the scene, but don't position it yet. rootNode = self.render.attachNewNode("") model.reparentTo(rootNode) # Rescale the model about its origin. The x and y coordinates of the # model's origin should be chosen as wherever it looks like the model's # center of mass is, so that rotation about the origin (in the xy # plane) feels natural. # TODO[#9]: Set a convention for model bounds so we don't have to do a # getTightBounds every time. This is dumb. # TODO[#3]: Or, as an alternative shorter-term solution, just define a # scale in the config files for the few models that aren't ours. bound1, bound2 = model.getTightBounds() modelWidthX = abs(bound2[0] - bound1[0]) modelWidthY = abs(bound2[1] - bound1[1]) # Scale it to the largest it can be while still fitting within the goal # rect. If the aspect ratio of the goal rect is different from that of # the model, then it'll only fill that rect in one dimension. # altScaleFactor is used for sanity checks below. scaleFactor, altScaleFactor = minmax(goalWidthX / modelWidthX, goalWidthY / modelWidthY) # Sanity check the scale factor. if scaleFactor <= 0.0: if scaleFactor == 0.0: log.warn("Ground %s will be scaled negatively!", chunkIndex) else: log.warn("Ground %s will be scaled to zero size.", chunkIndex) else: # TODO[#9]: Currently the example panda triggers this warning. # TODO[#3]: Magic numbers bad. if altScaleFactor / scaleFactor > 1.001: log.warn("Ground %s has different aspect ratio than " "its model: model of size %.3g x %.3g being scaled " "into %.3g x %.3g.", chunkIndex, modelWidthX, modelWidthY, goalWidthX, goalWidthY) model.setScale(scaleFactor) # Place the model at z=0. The model's origin should be placed so that # this looks natural -- for most units this means it should be right at # the bottom of the model, but if we add any units that are intended to # float above the ground, then this can be accomplished by just # positioning the model above its origin. rootNode.setPos(goalCenterX, goalCenterY, 0.0) self.groundNodes[cx][cy] = rootNode def rescanUnits(self): """ Check for units that have moved or been added/removed since the last scan. Update the display accordingly. """ # TODO: Actually write this. pass # Divide all unit ids from my own list of units and the gamestate's # current list into three parts: # - ids in both lists (potential moves) # - ids only in the gamestate's list (additions) # - ids only in my list (removals) # # Call: # moveUnit() for each potential move # addUnit() for each addition # removeUnit() for each removal def moveUnit(self, uid, newPos): # TODO: Actually write this. pass # Set unit's node to move to new position. # Play "walk" animation # ...whatever else we used to do? def addUnit(self, uid, pos): # TODO: Actually write this. pass # Create unit/model/node as we used to. # Also add it to our uid->unit mapping. def removeUnit(self, uid, pos): # TODO: Actually write this. pass # Remove+cleanup unit/model/node as we used to. # Also remove it from our uid->unit mapping. def createSelectionBox(self, corner1, corner2): """ Create a selection "box" given the coordinates of two opposite corners. The corners are given in world coordinates (well, 3d graphics coordinates). """ assert self.selectionBox is None p1, p2, p3, p4 = self.convert3dBoxToScreen(corner1, corner2) x1, y1 = p1 x2, y2 = p2 x3, y3 = p3 x4, y4 = p4 # TODO[#3]: Magic numbers bad. self.selectionBox = LineSegs("SelectionBox") self.selectionBox.setThickness(3.0) self.selectionBox.setColor(0.0, 1.0, 0.25, 1.0) self.selectionBox.move_to(x1, 0, y1) self.selectionBox.draw_to(x2, 0, y2) self.selectionBox.draw_to(x3, 0, y3) self.selectionBox.draw_to(x4, 0, y4) self.selectionBox.draw_to(x1, 0, y1) self.selectionBoxNode = self.render2d.attachNewNode( self.selectionBox.create()) def moveSelectionBox(self, corner1, corner2): assert self.selectionBox is not None p1, p2, p3, p4 = self.convert3dBoxToScreen(corner1, corner2) x1, y1 = p1 x2, y2 = p2 x3, y3 = p3 x4, y4 = p4 self.selectionBox.setVertex(0, x1, 0, y1) self.selectionBox.setVertex(1, x2, 0, y2) self.selectionBox.setVertex(2, x3, 0, y3) self.selectionBox.setVertex(3, x4, 0, y4) self.selectionBox.setVertex(4, x1, 0, y1) def removeSelectionBox(self): self.selectionBoxNode.removeNode() self.selectionBox = None self.selectionBoxNode = None def convert3dBoxToScreen(self, corner1, corner3): """ Return screen coordinates of the 4 corners of a box, given in 3d coordinates. The box is specified using 2 opposite corners. """ wx1, wy1, wz1 = corner1 wx3, wy3, wz3 = corner3 wx2, wy2 = (wx1, wy3) wx4, wy4 = (wx3, wy1) # Note: corner1 and corner2 could have nonzero z because floating-point # calculations, but they should at least be close. We'll just average # their z and not worry about it. wz2 = wz4 = 0.5 * (wz1 + wz3) p1 = self.coord3dToScreen((wx1, wy1, wz1)) p2 = self.coord3dToScreen((wx2, wy2, wz2)) p3 = self.coord3dToScreen((wx3, wy3, wz3)) p4 = self.coord3dToScreen((wx4, wy4, wz4)) return (p1, p2, p3, p4) def setCameraCustom(self): """ Change to using our custom task to control the camera. """ # Disable the default mouse-based camera control task, so we don't have # to fight with it for control of the camera. self.disableMouse() # Face the camera in the appropriate angle. self.camera.setHpr(self.prevCameraHpr) # Put it in the same location as the cameraHolder, and make it stay # put relative to the cameraHolder (so we can move the camera around by # changing the cameraHolder's position). self.camera.reparentTo(self.cameraHolder) self.camera.setPos(0, 0, 0) # Substitute our own camera control task. self.taskMgr.add(self.updateCameraTask, "UpdateCameraTask") self.usingCustomCamera = True # Need a task to handle mouse-dragging because there doesn't seem to be # a built-in mouseMove event. self.taskMgr.add(self.mouseMoveTask, "MouseMoveTask") def setCameraDefault(self): """ Change to using the default mouse-based camera controls. """ self.taskMgr.remove("UpdateCameraTask") # Save current location for when this control style is restored. self.prevCameraHpr = self.camera.getHpr() # Use the existing camera location, rather than jumping back to the one # from last time the default camera controller was active. # Copied from https://www.panda3d.org/manual/index.php/Mouse_Support mat = Mat4(self.camera.getMat()) mat.invertInPlace() self.mouseInterfaceNode.setMat(mat) self.enableMouse() self.usingCustomCamera = False def toggleCameraStyle(self): """ Switch to whichever style of camera control isn't currently active. """ if self.usingCustomCamera: self.setCameraDefault() else: self.setCameraCustom() # We don't use task, but we can't remove it because the function signature # is from Panda3D. def updateCameraTask(self, task): # pylint: disable=unused-argument """ Move the camera sensibly. """ dt = self.globalClock.getDt() translateSpeed = 30 * dt rotateSpeed = 50 * dt # Separately track whether the camera should translate in each of the 4 # directions. These 4 are initialized based on the various inputs that # might tell us to scroll, and different inputs saying the same thing # don't stack. That way if we get inputs saying both "left" and # "right", they can cancel and the camera just won't move along that # axis -- even if, say, there are two inputs saying "left" and only one # saying "right'. moveLeft = self.keys["arrow_left"] moveRight = self.keys["arrow_right"] moveUp = self.keys["arrow_up"] moveDown = self.keys["arrow_down"] # Check if the mouse is over the window. if self.mouseWatcherNode.hasMouse(): # Get the position. # Each coordinate is normalized to the interval [-1, 1]. mousePos = self.mouseWatcherNode.getMouse() xPos, yPos = mousePos.getX(), mousePos.getY() # Only move if the mouse is close to the edge, and actually within # the window. if (1.0 - EDGE_SCROLL_WIDTH) < xPos <= 1.0: moveRight = 1 if -(1.0 - EDGE_SCROLL_WIDTH) > xPos >= -1.0: moveLeft = 1 if (1.0 - EDGE_SCROLL_WIDTH) < yPos <= 1.0: moveUp = 1 if -(1.0 - EDGE_SCROLL_WIDTH) > yPos >= -1.0: moveDown = 1 forward = translateSpeed * (moveUp - moveDown) sideways = translateSpeed * (moveRight - moveLeft) self.cameraHolder.setPos(self.cameraHolder, sideways, forward, 0) # Selection box logic if sideways != 0 or forward != 0: self.updateSelectionBox() rotate = rotateSpeed * (self.keys["a"] - self.keys["d"]) self.cameraHolder.setHpr(self.cameraHolder, rotate, 0, 0) return Task.cont def zoomCamera(self, inward): """ Zoom in or out. """ dt = self.globalClock.getDt() zoomSpeed = 100 * dt zoom = -zoomSpeed if inward else zoomSpeed self.cameraHolder.setPos(self.cameraHolder, 0, 0, zoom) # We don't use task, but we can't remove it because the function signature # is from Panda3D. def mouseMoveTask(self, task): # pylint: disable=unused-argument """ Handle mouse movement. """ mousePos = self.getMousePos() # NOTE: We don't handle clicking and dragging at the same time. if mousePos is not None and mousePos != self.prevMousePos: for (buttonId, state) in self.mouseState.iteritems(): state.lastPos = mousePos if state.hasMoved: self.handleMouseDragMove(buttonId, state.modifiers, state.startPos, mousePos) else: startX, startY = state.startPos mouseX, mouseY = mousePos distance = math.hypot(mouseX - startX, mouseY - startY) # TODO[#3]: Magic numbers bad. # Check if the mouse has moved outside the dead zone. if distance > 0.0314: self.handleMouseDragStart(buttonId, state.modifiers, state.startPos, mousePos) state.hasMoved = True if mousePos != self.prevMousePos: self.prevMousePos = mousePos return Task.cont def pandaEventMouseDown(self, buttonId, modifiers): log.debug("Mouse down: button %s w/ mod %s", buttonId, modifiers) if buttonId in self.mouseState: # Call pandaEventMouseUp just to clear any state related to the # button being down, so we can handle this buttonDown event as if # it were a fresh press of the button. log.warn("Mouse button %s is already down.", buttonId) self.pandaEventMouseUp(buttonId) assert buttonId not in self.mouseState state = MouseButtonState(modifiers[:], self.getMousePos()) self.mouseState[buttonId] = state def pandaEventMouseUp(self, buttonId): log.debug("Mouse up: button %s", buttonId) if buttonId not in self.mouseState: # Drop the event, since there's nothing to do. log.warn("Mouse button %s is already up.", buttonId) return state = self.mouseState[buttonId] if state.hasMoved: endPos = self.getMousePos() if endPos is None: endPos = state.lastPos self.handleMouseDragEnd(buttonId, state.modifiers, state.startPos, endPos) else: self.handleMouseClick(buttonId, state.modifiers, state.startPos) del self.mouseState[buttonId] def handleMouseClick(self, button, modifiers, pos): # Make sure the mouse is inside the screen # TODO: Move this check to pandaEventMouseUp? if self.mouseWatcherNode.hasMouse() and self.usingCustomCamera: x, y, _z = self.coordScreenTo3d(pos) uPos = graphicsToWorldPos((x, y)) self.backend.worldClick(uPos, button, modifiers) def handleMouseDragStart(self, buttonId, modifiers, startPos, endPos): log.debug("Start dragging from %s to %s", startPos, endPos) if buttonId == 1 and modifiers == []: assert self.selectionBoxOrigin is None self.selectionBoxOrigin = self.coordScreenTo3d(startPos) endPos = self.coordScreenTo3d(endPos) self.createSelectionBox(self.selectionBoxOrigin, endPos) def handleMouseDragMove(self, buttonId, modifiers, startPos, endPos): log.debug("Continue dragging from %s to %s", startPos, endPos) if buttonId == 1 and modifiers == []: assert self.selectionBoxOrigin is not None endPos = self.coordScreenTo3d(endPos) self.moveSelectionBox(self.selectionBoxOrigin, endPos) def handleMouseDragEnd(self, buttonId, modifiers, startPos, endPos): log.debug("End dragging from %s to %s", startPos, endPos) # TODO: Do we need this check? What is the effect of only calling # removeSelectionBox() under this check? if buttonId == 1 and modifiers == []: # Actually select the units. startGPos = self.selectionBoxOrigin[:2] endGPos = self.coordScreenTo3d(endPos)[:2] startUPos = graphicsToWorldPos(startGPos) endUPos = graphicsToWorldPos(endGPos) self.backend.worldDrag(startUPos, endUPos, buttonId, modifiers) # Clear the selection box; we're done dragging. self.selectionBoxOrigin = None self.removeSelectionBox() def updateSelectionBox(self): if self.selectionBoxOrigin is not None: mousePos = self.getMousePos() if mousePos is not None: endPos = self.coordScreenTo3d(mousePos) self.moveSelectionBox(self.selectionBoxOrigin, endPos) def getMousePos(self): # Check if the mouse is over the window. if self.mouseWatcherNode.hasMouse(): # Get the position. # Each coordinate is normalized to the interval [-1, 1]. mousePoint = self.mouseWatcherNode.getMouse() # Create a copy of mousePoint rather than returning a reference to # it, because mousePoint will be modified in place by Panda. return (mousePoint.getX(), mousePoint.getY()) else: return None def handleWindowClose(self): log.info("Window close requested -- shutting down client.") # When in Rome, send messages like the Romans do, I guess. # TODO: Get rid of messages, I think. message = cmessages.RequestQuit() self.graphicsInterface.graphicsMessage(message.serialize()) def setupEventHandlers(self): def pushKey(key, value): self.keys[key] = value for key in ["arrow_up", "arrow_left", "arrow_right", "arrow_down", "w", "a", "d", "s"]: self.keys[key] = False self.accept(key, pushKey, [key, True]) self.accept("shift-%s" % key, pushKey, [key, True]) self.accept("%s-up" % key, pushKey, [key, False]) # Camera toggle. self.accept("f3", self.toggleCameraStyle, []) self.accept("shift-f3", self.toggleCameraStyle, []) # Center view. # self.accept("space", self.centerView, []) -- TODO # Handle mouse wheel. self.accept("wheel_up", self.zoomCamera, [True]) self.accept("wheel_down", self.zoomCamera, [False]) # Handle clicking. self.accept("mouse1", self.pandaEventMouseDown, [1, []]) self.accept("mouse1-up", self.pandaEventMouseUp, [1]) # TODO: Make sure this is always the right mouse button. self.accept("mouse3", self.pandaEventMouseDown, [3, []]) self.accept("mouse3-up", self.pandaEventMouseUp, [3]) # Handle clicking with modifier keys. self.accept("shift-mouse1", self.pandaEventMouseDown, [1, ["shift"]]) self.accept("control-mouse1", self.pandaEventMouseDown, [1, ["control"]]) self.accept("shift-mouse3", self.pandaEventMouseDown, [3, ["shift"]]) self.accept("control-mouse3", self.pandaEventMouseDown, [3, ["control"]]) # Handle window close request (clicking the X, Alt-F4, etc.) self.win.set_close_request_event("window-close") self.accept("window-close", self.handleWindowClose) def coord3dToScreen(self, coord3d): # Empirically, Lens.project takes coordinates in the *camera*'s # coordinate system, not its parent or the render. This was not very # clear from the documentation, and you'd be surprised how long it took # us to figure this out. Anyway, we need to convert the point to be # relative to self.camera here; otherwise we'll get bizarre, # nonsensical, and hard-to-debug results. coord3d = self.camera.getRelativePoint(self.render, coord3d) screenCoord = Point2() if not self.camLens.project(coord3d, screenCoord): log.debug("Attempting 3d-to-screen conversion on point outside of " "camera's viewing frustum.") # Convert to a tuple to ensure no one else is keeping a reference # around. x, y = screenCoord return (x, y) def coordScreenTo3d(self, screenCoord): x, y = screenCoord screenPoint = Point2(x, y) # Do this calculation using simple geometry, rather than the absurd # collision-traversal nonsense we used to use. Thanks to # https://www.panda3d.org/forums/viewtopic.php?t=5409 # for pointing us at the right methods to make this work. # Get two points along the ray extending from the camera, in the # direction of the mouse click. nearPoint = Point3() farPoint = Point3() self.camLens.extrude(screenPoint, nearPoint, farPoint) # These points are relative to the camera, so need to be converted to # be relative to the render. Thanks to the example code (see link # above) for saving us probably some hours of debugging figuring that # one out again :) nearPoint = self.render.getRelativePoint(self.camera, nearPoint) farPoint = self.render.getRelativePoint(self.camera, farPoint) intersection = Point3() if self.groundPlane.intersectsLine(intersection, nearPoint, farPoint): # Convert to a tuple to ensure no one else is keeping a reference # around. x, y, z = intersection return (x, y, z) # The ray didn't intersect the ground. This is almost certainly going # to happen at some point; all you have to do is find a way to aim the # camera (or manipulate the screen coordinate) so that the ray points # horizontally. But we don't have code to handle it, so for now just # abort. thisIsNotHandled()
def funnel(self, channel): # pick the starting point and the starting leftVec and rightVec start = channel[0] second = channel[1] funnler = self.makeFunVecs(start, second) apexTriangles = [] pathPts = [funnler.start] i = 0 while channel[i + 1] != self.goal: # [leftOrRightFailed, i, funVecs, path] pack = self.funnelIter(i, channel, funnler, pathPts) print "############## after iter #####################" i = pack[1] # get the updated index pathPts = pack[3] if i + 1 == len(channel): break apexTriangles.append(channel[i]) # record this as a triangle where we turned a corner # region Handled adding pathPts # if channel[i] != self.goal: # if pack[0] != 'leftVec': # if it's rightVec make leftVec the new apex # print "rPt", funnler.rPt # pathPts.append(funnler.rPt) # record the next path point # # funnler.startPt = funnler.rPt # # pts = channel[i].getSharedEdgeStr(channel[i + 1]) # # print "Shared pts", pts # # # pick the not equal to leftVec point on the shared edge # # for k in channel[i + 1].tri: # # if k not in pts: # # funnler.updateRight(k) # # if funnler.lPt == pts[0]: # # funnler.updateRight(pts[1]) # # else: # # funnler.updateRight(pts[0]) # elif pack[0] != 'rightVec': # print "lPt", funnler.lPt # pathPts.append(funnler.lPt) # record the next path point # # funnler.startPt = funnler.lPt # # pts = channel[i].getSharedEdgeStr(channel[i + 1]) # # print "Shared pts", pts # # for k in channel[i + 1].tri: # # if k not in pts: # # funnler.updateLeft(k) # # # # # if funnler.rPt == pts[0]: # # # funnler.updateLeft(pts[1]) # # # else: # # # funnler.updateLeft(pts[0]) # else: # print "ERRROR??? funnel() defaulted leftVec or rightVec = ", pack[0] # break # endregion #funnler = self.makeFunVecs(channel[i], channel[i + 1]) # pack[2] print "######################## funnler", funnler print "path", pathPts linesegs = LineSegs('pathLine') linesegs.setColor(0, 0, 1, 1) for point in pathPts: linesegs.drawTo(point.x, point.y, .5) node = linesegs.create(False) render.attachNewNode(node) return channel
def renderCharts(facegraph, verts, vert_indices, lineset=None): from meshtool.filters.panda_filters.pandacore import getVertexData, attachLights, ensureCameraAt from meshtool.filters.panda_filters.pandacontrols import KeyboardMovement, MouseDrag, MouseScaleZoom, ButtonUtils from panda3d.core import GeomTriangles, Geom, GeomNode, GeomVertexFormat, GeomVertexData, GeomVertexWriter, LineSegs from direct.showbase.ShowBase import ShowBase vformat = GeomVertexFormat.getV3c4() vdata=GeomVertexData('tris', vformat, Geom.UHDynamic) vertex=GeomVertexWriter(vdata, 'vertex') color=GeomVertexWriter(vdata, 'color') colors = gen_color3(len(facegraph)) numtris = 0 for chart, data in facegraph.nodes_iter(data=True): curcolor = next(colors) for tri in data['tris']: triv = verts[vert_indices[tri]] vertex.addData3f(triv[0][0], triv[0][1], triv[0][2]) vertex.addData3f(triv[1][0], triv[1][1], triv[1][2]) vertex.addData3f(triv[2][0], triv[2][1], triv[2][2]) color.addData4f(curcolor[0],curcolor[1], curcolor[2], 1) color.addData4f(curcolor[0],curcolor[1], curcolor[2], 1) color.addData4f(curcolor[0],curcolor[1], curcolor[2], 1) numtris += 1 tris=GeomTriangles(Geom.UHDynamic) tris.addConsecutiveVertices(0, 3*numtris) tris.closePrimitive() linenodes = [] if lineset: for lines in lineset: ls = LineSegs() ls.setThickness(4) curcolor = next(colors) ls.setColor(curcolor[0]/256.0, curcolor[1]/256.0, curcolor[2]/256.0, 1) tuples = False for blah in lines: if isinstance(blah, tuple): tuples = True break if tuples: for i, j in lines: frompt = verts[i] topt = verts[j] ls.moveTo(frompt[0], frompt[1], frompt[2]) ls.drawTo(topt[0], topt[1], topt[2]) else: for i in range(len(lines)-1): frompt = verts[lines[i]] topt = verts[lines[i+1]] ls.moveTo(frompt[0], frompt[1], frompt[2]) ls.drawTo(topt[0], topt[1], topt[2]) linenodes.append(ls.create()) pgeom = Geom(vdata) pgeom.addPrimitive(tris) node = GeomNode("primitive") node.addGeom(pgeom) p3dApp = ShowBase() #attachLights(render) geomPath = render.attachNewNode(node) for linenode in linenodes: geomPath.attachNewNode(linenode) #geomPath.setRenderModeWireframe() ensureCameraAt(geomPath, base.cam) boundingSphere = geomPath.getBounds() base.cam.setPos(boundingSphere.getCenter() + boundingSphere.getRadius()) base.cam.lookAt(boundingSphere.getCenter()) KeyboardMovement() ButtonUtils(geomPath) MouseDrag(geomPath) MouseScaleZoom(geomPath) #render.setShaderAuto() p3dApp.run()
class GolfScoreBoard: notify = directNotify.newCategory('GolfScoreBoard') def __init__(self, golfCourse): self.golfCourse = golfCourse self.numPlayas = len(golfCourse.avIdList) self.avIdList = golfCourse.avIdList self.playaTags = [] self.scoreTags = [] self.totalTags = [] self.scoreLabels = [] self.holeLabels = [] self.parLabels = [] self.numExited = 0 self.setup() def setup(self): self.scoreboard = DirectFrame(parent=aspect2d, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1.9, 1, 1.05), pos=(0, 0, 0.375)) self.lines = LineSegs() self.lines.setColor(0, 0, 0, 1) self.lines.setThickness(2) guiModel = loader.loadModel('phase_6/models/golf/golf_gui') highlight = loader.loadModel('phase_6/models/golf/headPanel') self.maximizeB = DirectButton(parent=base.a2dBottomRight, pos=(-0.15, 0, 0.15), relief=None, state=DGG.NORMAL, image=(guiModel.find('**/score_card_icon'), guiModel.find('**/score_card_icon_rollover'), guiModel.find('**/score_card_icon_rollover')), image_scale=(0.2, 1, 0.2), command=self.showBoard) self.vertOffset = 0.13 self.playaTop = 0.12 horzOffset = 0.12 holeTop = 0.3 self.vCenter = 0.025 totScore = 0 totPar = 0 self.lineVStart = -0.465 self.lineHStart = 0.17 self.lineHorOffset = 0.13 self.lineVertOffset = 0.125 self.lineVCenter = 0.025 buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') self.minimizeB = DirectButton(parent=self.scoreboard, pos=(0, 0, self.lineHStart - 0.59), relief=None, state=DGG.NORMAL, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), image_scale=(1, 1, 1), command=self.hideBoard, extraArgs=[None]) self.exitCourseB = DirectButton(parent=self.scoreboard, pos=(0, 0, self.lineHStart - 0.59), relief=None, state=DGG.NORMAL, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), image_scale=(1, 1, 1), text=TTLocalizer.GolfExitCourse, text_scale=0.04, text_pos=TTLocalizer.GSBexitCourseBPos, command=self.exitCourse) self.exitCourseB.hide() self.highlightCur = DirectLabel(parent=self.scoreboard, relief=None, pos=(-0.003, 0, 0.038), image=highlight, image_scale=(1.82, 1, 0.135)) self.titleBar = DirectLabel(parent=self.scoreboard, relief=None, pos=(-0.003, 0, 0.166), color=(0.7, 0.7, 0.7, 0.3), image=highlight, image_scale=(1.82, 1, 0.195)) self.titleBar.show() self.highlightCur.show() buttons.removeNode() guiModel.removeNode() title = GolfGlobals.getCourseName(self.golfCourse.courseId) + ' - ' + GolfGlobals.getHoleName(self.golfCourse.holeIds[self.golfCourse.curHoleIndex]) self.titleLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(0, 0, holeTop + 0.1), text_align=TextNode.ACenter, text=title, text_scale=TTLocalizer.GSBtitleLabel, text_font=ToontownGlobals.getSignFont(), text_fg=(0, 0.5, 0.125, 1)) self.playaLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart - 0.23, 0, holeTop), text_align=TextNode.ACenter, text=TTLocalizer.GolfHole, text_font=ToontownGlobals.getMinnieFont(), text_scale=0.05) for holeLIndex in xrange(self.golfCourse.numHoles): holeLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.055 + horzOffset * holeLIndex, 0, holeTop), text_align=TextNode.ACenter, text='%s' % (holeLIndex + 1), text_scale=0.05) self.holeLabels.append(holeLabel) self.totalLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.1 + horzOffset * 9.5, 0, holeTop), text_align=TextNode.ACenter, text=TTLocalizer.GolfTotal, text_font=ToontownGlobals.getMinnieFont(), text_scale=0.05) self.parTitleLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart - 0.23, 0, holeTop - 0.1), text_align=TextNode.ACenter, text=TTLocalizer.GolfPar, text_font=ToontownGlobals.getMinnieFont(), text_scale=0.05) for parHoleIndex in xrange(self.golfCourse.numHoles): parLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.055 + horzOffset * parHoleIndex, 0, holeTop - 0.1), text_align=TextNode.ACenter, text='%s' % GolfGlobals.HoleInfo[self.golfCourse.holeIds[parHoleIndex]]['par'], text_scale=0.05, text_wordwrap=10) totPar = totPar + GolfGlobals.HoleInfo[self.golfCourse.holeIds[parHoleIndex]]['par'] self.parLabels.append(parLabel) parLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.1 + horzOffset * 9.5, 0, holeTop - 0.1), text_align=TextNode.ACenter, text='%s' % totPar, text_scale=0.05, text_wordwrap=10) self.parLabels.append(parLabel) vert = 0.0 self.numPlayas = len(self.golfCourse.avIdList) for playaIndex in xrange(self.numPlayas): name = TTLocalizer.GolfUnknownPlayer av = base.cr.doId2do.get(self.golfCourse.avIdList[playaIndex]) if av: name = av.getName() playaLabel = DirectLabel(parent=self.scoreboard, relief=None, text_align=TextNode.ACenter, text=name, text_scale=0.05, text_wordwrap=9) self.playaTags.append(playaLabel) textN = playaLabel.component(playaLabel.components()[0]) if type(textN) == OnscreenText: try: if textN.textNode.getWordwrappedWtext() != name: vert = self.playaTop - self.vertOffset * playaIndex else: vert = self.playaTop - self.vertOffset * playaIndex - self.vCenter except: vert = self.playaTop - self.vertOffset * playaIndex self.playaTags[playaIndex].setPos(self.lineVStart - 0.23, 0, vert) self.notify.debug('self.text height = %f' % self.playaTags[playaIndex].getHeight()) holeIndex = 0 for holeIndex in xrange(self.golfCourse.numHoles): holeLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.055 + horzOffset * holeIndex, 0, self.playaTop - self.vertOffset * playaIndex - self.vCenter), text_align=TextNode.ACenter, text='-', text_scale=0.05, text_wordwrap=10) self.scoreTags.append(holeLabel) holeLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.1 + horzOffset * 9.5, 0, self.playaTop - self.vertOffset * playaIndex - self.vCenter), text_align=TextNode.ACenter, text='-', text_scale=0.05, text_wordwrap=10) self.totalTags.append(holeLabel) self.lines.moveTo(self.lineVStart - 0.45, 0, self.lineHStart + 0.19) self.lines.drawTo(self.lineVStart + 11 * self.lineVertOffset, 0, self.lineHStart + 0.19) self.lines.moveTo(self.lineVStart - 0.45, 0, self.lineHStart + 0.09) self.lines.drawTo(self.lineVStart + 11 * self.lineVertOffset, 0, self.lineHStart + 0.09) self.lines.moveTo(self.lineVStart - 0.45, 0, self.lineHStart) self.lines.drawTo(self.lineVStart + 11 * self.lineVertOffset, 0, self.lineHStart) self.lines.moveTo(self.lineVStart - 0.45, 0, self.lineHStart + 0.19) self.lines.drawTo(self.lineVStart - 0.45, 0, self.lineHStart - 4 * 0.13) self.lines.moveTo(self.lineVStart, 0, self.lineHStart + 0.19) self.lines.drawTo(self.lineVStart, 0, self.lineHStart - 4 * 0.13) for x in xrange(4): self.lines.moveTo(self.lineVStart - 0.45, 0, self.lineHStart - (x + 1) * self.lineHorOffset) self.lines.drawTo(self.lineVStart + 11 * self.lineVertOffset + 0.005, 0, self.lineHStart - (x + 1) * self.lineHorOffset) for y in xrange(10): self.lines.moveTo(self.lineVStart + y * self.lineVertOffset, 0, self.lineHStart + 0.19) self.lines.drawTo(self.lineVStart + y * self.lineVertOffset, 0, self.lineHStart - 4 * 0.13) self.lines.moveTo(self.lineVStart + 11 * self.lineVertOffset, 0, self.lineHStart + 0.19) self.lines.drawTo(self.lineVStart + 11 * self.lineVertOffset, 0, self.lineHStart - 4 * 0.13) self.scoreboard.attachNewNode(self.lines.create()) self.hide() return def getScoreLabel(self, avIdorIndex, holeNum): index = None if avIdorIndex < 100: index = avIdorIndex else: for playaIndex in xrange(self.numPlayas): if self.golfCourse.avIdList[playaIndex] == avIdorIndex: index = playaIndex return self.scoreTags[index * self.golfCourse.numHoles + holeNum] def update(self): self.showBoard() taskMgr.doMethodLater(AUTO_HIDE_TIMEOUT, self.hideBoard, 'hide score board') def hideBoard(self, task): self.hide() def hide(self): self.scoreboard.hide() self.maximizeB.show() def showBoardFinal(self, task = None): self.exitCourseB.show() self.minimizeB.hide() self.showBoard() def showBoard(self, task = None): scoreDict = self.golfCourse.scores x = 0 currentGolfer = self.golfCourse.getCurGolfer() for playaIndex in xrange(self.numPlayas): if self.golfCourse.isGameDone(): self.playaTags[playaIndex].setColor(0, 0, 0, 1) elif currentGolfer == self.golfCourse.avIdList[playaIndex]: self.highlightCur.setColor(*GolfGlobals.PlayerColors[playaIndex]) self.highlightCur.setAlphaScale(0.4) self.highlightCur.setPos(-0.003, 0, 0.038 - playaIndex * (self.lineVertOffset + 0.005)) self.highlightCur.show() else: self.playaTags[playaIndex].setColor(0, 0, 0, 1) for avId in self.avIdList: holeIndex = 0 totScore = 0 playerExited = False for y in xrange(len(self.golfCourse.exitedAvIdList)): if self.golfCourse.exitedAvIdList[y] == avId: self.playaTags[x].setColor(0.7, 0.7, 0.7, 1) holeIndex = 0 for holeIndex in xrange(self.golfCourse.numHoles): self.getScoreLabel(self.avIdList[x], holeIndex).setColor(0.7, 0.7, 0.7, 1) self.totalTags[x].setColor(0.7, 0.7, 0.7, 1) playerExited = True if playerExited == False: for holeIndex in xrange(self.golfCourse.numHoles): if holeIndex <= self.golfCourse.curHoleIndex: self.getScoreLabel(avId, holeIndex)['text'] = '%s' % scoreDict[avId][holeIndex] totScore = totScore + scoreDict[avId][holeIndex] if self.golfCourse.isGameDone() == False: if holeIndex == self.golfCourse.curHoleIndex: self.getScoreLabel(avId, holeIndex).setColor(1, 0, 0, 1) self.holeLabels[holeIndex].setColor(1, 0, 0, 1) self.parLabels[holeIndex].setColor(1, 0, 0, 1) title = GolfGlobals.getCourseName(self.golfCourse.courseId) + ' - ' + GolfGlobals.getHoleName(self.golfCourse.holeIds[self.golfCourse.curHoleIndex]) self.titleLabel['text'] = title else: self.getScoreLabel(avId, holeIndex).setColor(0, 0, 0, 1) self.holeLabels[holeIndex].setColor(0, 0, 0, 1) self.parLabels[holeIndex].setColor(0, 0, 0, 1) self.totalTags[x]['text'] = '%s' % totScore if self.golfCourse.isGameDone(): self.getScoreLabel(avId, self.golfCourse.numHoles - 1).setColor(0, 0, 0, 1) self.totalTags[x].setColor(1, 0, 0, 1) x = x + 1 y = 0 if self.golfCourse.isGameDone(): self.parLabels[self.golfCourse.numHoles - 1].setColor(0, 0, 0, 1) self.holeLabels[self.golfCourse.numHoles - 1].setColor(0, 0, 0, 1) self.parLabels[self.golfCourse.numHoles].setColor(1, 0, 0, 1) self.totalLabel.setColor(1, 0, 0, 1) self.scoreboard.show() self.maximizeB.hide() def exitCourse(self): course = self.golfCourse self.delete() course.exitEarly() def delete(self): if self.maximizeB: self.maximizeB.destroy() self.maximizeB = None if self.scoreboard: self.scoreboard.destroy() self.scoreboard = None self.golfCourse = None taskMgr.remove('hide score board') return
def setup(self): self.scoreboard = DirectFrame(parent=aspect2d, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1.9, 1, 1.05), pos=(0, 0, 0.375)) self.lines = LineSegs() self.lines.setColor(0, 0, 0, 1) self.lines.setThickness(2) guiModel = loader.loadModel('phase_6/models/golf/golf_gui') highlight = loader.loadModel('phase_6/models/golf/headPanel') self.maximizeB = DirectButton(parent=base.a2dBottomRight, pos=(-0.15, 0, 0.15), relief=None, state=DGG.NORMAL, image=(guiModel.find('**/score_card_icon'), guiModel.find('**/score_card_icon_rollover'), guiModel.find('**/score_card_icon_rollover')), image_scale=(0.2, 1, 0.2), command=self.showBoard) self.vertOffset = 0.13 self.playaTop = 0.12 horzOffset = 0.12 holeTop = 0.3 self.vCenter = 0.025 totScore = 0 totPar = 0 self.lineVStart = -0.465 self.lineHStart = 0.17 self.lineHorOffset = 0.13 self.lineVertOffset = 0.125 self.lineVCenter = 0.025 buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') self.minimizeB = DirectButton(parent=self.scoreboard, pos=(0, 0, self.lineHStart - 0.59), relief=None, state=DGG.NORMAL, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), image_scale=(1, 1, 1), command=self.hideBoard, extraArgs=[None]) self.exitCourseB = DirectButton(parent=self.scoreboard, pos=(0, 0, self.lineHStart - 0.59), relief=None, state=DGG.NORMAL, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), image_scale=(1, 1, 1), text=TTLocalizer.GolfExitCourse, text_scale=0.04, text_pos=TTLocalizer.GSBexitCourseBPos, command=self.exitCourse) self.exitCourseB.hide() self.highlightCur = DirectLabel(parent=self.scoreboard, relief=None, pos=(-0.003, 0, 0.038), image=highlight, image_scale=(1.82, 1, 0.135)) self.titleBar = DirectLabel(parent=self.scoreboard, relief=None, pos=(-0.003, 0, 0.166), color=(0.7, 0.7, 0.7, 0.3), image=highlight, image_scale=(1.82, 1, 0.195)) self.titleBar.show() self.highlightCur.show() buttons.removeNode() guiModel.removeNode() title = GolfGlobals.getCourseName(self.golfCourse.courseId) + ' - ' + GolfGlobals.getHoleName(self.golfCourse.holeIds[self.golfCourse.curHoleIndex]) self.titleLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(0, 0, holeTop + 0.1), text_align=TextNode.ACenter, text=title, text_scale=TTLocalizer.GSBtitleLabel, text_font=ToontownGlobals.getSignFont(), text_fg=(0, 0.5, 0.125, 1)) self.playaLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart - 0.23, 0, holeTop), text_align=TextNode.ACenter, text=TTLocalizer.GolfHole, text_font=ToontownGlobals.getMinnieFont(), text_scale=0.05) for holeLIndex in xrange(self.golfCourse.numHoles): holeLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.055 + horzOffset * holeLIndex, 0, holeTop), text_align=TextNode.ACenter, text='%s' % (holeLIndex + 1), text_scale=0.05) self.holeLabels.append(holeLabel) self.totalLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.1 + horzOffset * 9.5, 0, holeTop), text_align=TextNode.ACenter, text=TTLocalizer.GolfTotal, text_font=ToontownGlobals.getMinnieFont(), text_scale=0.05) self.parTitleLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart - 0.23, 0, holeTop - 0.1), text_align=TextNode.ACenter, text=TTLocalizer.GolfPar, text_font=ToontownGlobals.getMinnieFont(), text_scale=0.05) for parHoleIndex in xrange(self.golfCourse.numHoles): parLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.055 + horzOffset * parHoleIndex, 0, holeTop - 0.1), text_align=TextNode.ACenter, text='%s' % GolfGlobals.HoleInfo[self.golfCourse.holeIds[parHoleIndex]]['par'], text_scale=0.05, text_wordwrap=10) totPar = totPar + GolfGlobals.HoleInfo[self.golfCourse.holeIds[parHoleIndex]]['par'] self.parLabels.append(parLabel) parLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.1 + horzOffset * 9.5, 0, holeTop - 0.1), text_align=TextNode.ACenter, text='%s' % totPar, text_scale=0.05, text_wordwrap=10) self.parLabels.append(parLabel) vert = 0.0 self.numPlayas = len(self.golfCourse.avIdList) for playaIndex in xrange(self.numPlayas): name = TTLocalizer.GolfUnknownPlayer av = base.cr.doId2do.get(self.golfCourse.avIdList[playaIndex]) if av: name = av.getName() playaLabel = DirectLabel(parent=self.scoreboard, relief=None, text_align=TextNode.ACenter, text=name, text_scale=0.05, text_wordwrap=9) self.playaTags.append(playaLabel) textN = playaLabel.component(playaLabel.components()[0]) if type(textN) == OnscreenText: try: if textN.textNode.getWordwrappedWtext() != name: vert = self.playaTop - self.vertOffset * playaIndex else: vert = self.playaTop - self.vertOffset * playaIndex - self.vCenter except: vert = self.playaTop - self.vertOffset * playaIndex self.playaTags[playaIndex].setPos(self.lineVStart - 0.23, 0, vert) self.notify.debug('self.text height = %f' % self.playaTags[playaIndex].getHeight()) holeIndex = 0 for holeIndex in xrange(self.golfCourse.numHoles): holeLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.055 + horzOffset * holeIndex, 0, self.playaTop - self.vertOffset * playaIndex - self.vCenter), text_align=TextNode.ACenter, text='-', text_scale=0.05, text_wordwrap=10) self.scoreTags.append(holeLabel) holeLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.1 + horzOffset * 9.5, 0, self.playaTop - self.vertOffset * playaIndex - self.vCenter), text_align=TextNode.ACenter, text='-', text_scale=0.05, text_wordwrap=10) self.totalTags.append(holeLabel) self.lines.moveTo(self.lineVStart - 0.45, 0, self.lineHStart + 0.19) self.lines.drawTo(self.lineVStart + 11 * self.lineVertOffset, 0, self.lineHStart + 0.19) self.lines.moveTo(self.lineVStart - 0.45, 0, self.lineHStart + 0.09) self.lines.drawTo(self.lineVStart + 11 * self.lineVertOffset, 0, self.lineHStart + 0.09) self.lines.moveTo(self.lineVStart - 0.45, 0, self.lineHStart) self.lines.drawTo(self.lineVStart + 11 * self.lineVertOffset, 0, self.lineHStart) self.lines.moveTo(self.lineVStart - 0.45, 0, self.lineHStart + 0.19) self.lines.drawTo(self.lineVStart - 0.45, 0, self.lineHStart - 4 * 0.13) self.lines.moveTo(self.lineVStart, 0, self.lineHStart + 0.19) self.lines.drawTo(self.lineVStart, 0, self.lineHStart - 4 * 0.13) for x in xrange(4): self.lines.moveTo(self.lineVStart - 0.45, 0, self.lineHStart - (x + 1) * self.lineHorOffset) self.lines.drawTo(self.lineVStart + 11 * self.lineVertOffset + 0.005, 0, self.lineHStart - (x + 1) * self.lineHorOffset) for y in xrange(10): self.lines.moveTo(self.lineVStart + y * self.lineVertOffset, 0, self.lineHStart + 0.19) self.lines.drawTo(self.lineVStart + y * self.lineVertOffset, 0, self.lineHStart - 4 * 0.13) self.lines.moveTo(self.lineVStart + 11 * self.lineVertOffset, 0, self.lineHStart + 0.19) self.lines.drawTo(self.lineVStart + 11 * self.lineVertOffset, 0, self.lineHStart - 4 * 0.13) self.scoreboard.attachNewNode(self.lines.create()) self.hide() return
class WartsApp(ShowBase): """ The application running all the graphics. """ def __init__(self, graphicsInterface): ShowBase.__init__(self) # This is available as a global, but pylint gives an undefined-variable # warning if we use it that way. Looking at # https://www.panda3d.org/manual/index.php/ShowBase # I would have thought we could reference it as either # self.globalClock, direct.showbase.ShowBase.globalClock, or possibly # direct.showbase.globalClock, but none of those seems to work. To # avoid the pylint warnings, create self.globalClock manually. self.globalClock = ClockObject.getGlobalClock() self.graphicsInterface = graphicsInterface # Mapping from gids to entities. self.entities = {} # Set up event handling. self.mouseState = {} self.keys = {} self.setupEventHandlers() # Set up camera control. self.cameraHolder = self.render.attachNewNode('CameraHolder') self.cameraHolder.setPos(0, 0, 100) self.prevCameraHpr = (0, -80, 0) self.usingCustomCamera = True self.setCameraCustom() self.prevMousePos = None self.selectionBox = None self.selectionBoxNode = None self.selectionBoxOrigin = None # TODO[#3]: Magic numbers bad. self.resourceDisplay = OnscreenText(pos=(-0.98,.9), align=TextNode.ALeft, mayChange=True) # Define the ground plane by a normal (+z) and a point (the origin). self.groundPlane = core.Plane(core.Vec3(0, 0, 1), core.Point3(0, 0, 0)) self.graphicsInterface.graphicsReady(self) def cleanup(self): pass def interfaceMessage(self, data): # Messages from GraphicsInterface to Graphics are always internal # client messages, so no need to catch InvalidMessageError. message = deserializeMessage(data) if isinstance(message, messages.Tick): pass elif isinstance(message, cmessages.AddEntity): self.addEntity(message.gid, message.pos, message.modelPath, message.isExample, message.isUnit, message.goalSize) elif isinstance(message, cmessages.RemoveEntity): self.removeEntity(message.gid) elif isinstance(message, cmessages.MoveEntity): self.moveEntity(message.gid, message.pos) elif isinstance(message, cmessages.MarkEntitySelected): self.markSelected(message.gid, message.isSelected) elif isinstance(message, cmessages.DisplayResources): self.displayResources(message.resourceAmt) else: badIMessageCommand(message, log) def addEntity(self, gid, pos, modelPath, isExample, isUnit, goalSize): """ pos is given in graphics coordinates. goalSize, if specified, is a pair (width, height) -- the model will be scaled in the xy plane so that it's as large as possible while still fitting within that width and height. Don't pass 0 as the width or the height, because that's just not nice. """ if gid in self.entities: raise RuntimeError("Already have entity with gid {gid}." .format(gid=gid)) log.debug("Adding graphical entity %s at %s", gid, pos) x, y = pos if isExample: # The example panda from the Panda3D "Hello world" tutorial. # TODO[#9]: Figure out a more general way of specifying animations. model = Actor(modelPath, {"walk": "models/panda-walk4"}) else: model = self.loader.loadModel(getModelPath(modelPath)) # Put the model in the scene, but don't position it yet. rootNode = self.render.attachNewNode("") model.reparentTo(rootNode) # Rescale the model about its origin. The x and y coordinates of the # model's origin should be chosen as wherever it looks like the model's # center of mass is, so that rotation about the origin (in the xy # plane) feels natural. goalWidthX, goalWidthY = goalSize bound1, bound2 = model.getTightBounds() modelWidthX = abs(bound2[0] - bound1[0]) modelWidthY = abs(bound2[1] - bound1[1]) # Scale it to the largest it can be while still fitting within the goal # rect. If the aspect ratio of the goal rect is different from that of # the model, then it'll only fill that rect in one dimension. # altScaleFactor is used for sanity checks below. scaleFactor, altScaleFactor = minmax(goalWidthX / modelWidthX, goalWidthY / modelWidthY) # Sanity check the scale factor. if scaleFactor <= 0.0: if scaleFactor == 0.0: log.warn("Graphical entity %s will be scaled negatively!", gid) else: log.warn("Graphical entity %s will be scaled to zero size.", gid) else: # TODO[#9]: Currently the example panda triggers this warning. # TODO[#3]: Magic numbers bad. if altScaleFactor / scaleFactor > 1.001: log.warn("Graphical entity %s has different aspect ratio than " "its model: model of size %.3g x %.3g being scaled " "into %.3g x %.3g.", gid, modelWidthX, modelWidthY, goalWidthX, goalWidthY) model.setScale(scaleFactor) # Place the model at z=0. The model's origin should be placed so that # this looks natural -- for most units this means it should be right at # the bottom of the model, but if we add any units that are intended to # float above the ground, then this can be accomplished by just # positioning the model above its origin. rootNode.setPos(x, y, 0.0) entity = Entity(gid, model, rootNode, isExample) self.entities[gid] = entity if isUnit: # TODO[#52]: Sigh. This is a terrible hack. I guess we could pipe # through yet another bool for "is this my unit", but I don't want # to have a growing collection of bools that need to be passed into # the graphics for each unit. For now, "is this an example model?" # and "is this my unit" are equivalent, so I guess we'll just # piggyback off of isExample.... if isExample: entity.setIndicator(self.loader.loadModel( getModelPath("unit-indicator-mine.egg") )) else: entity.setIndicator(self.loader.loadModel( getModelPath("unit-indicator-notmine.egg") )) def removeEntity(self, gid): log.debug("Removing graphical entity %s", gid) entity = self.entities.pop(gid) entity.cleanup() def moveEntity(self, gid, newPos): log.debug("Moving graphical entity %s to %s", gid, newPos) entity = self.entities[gid] x, y = newPos oldX, oldY, oldZ = entity.rootNode.getPos() z = oldZ # Ensure the entity is facing the right direction. heading = math.atan2(y - oldY, x - oldX) heading *= 180.0 / math.pi # Magic angle adjustment needed to stop the panda always facing # sideways. # TODO[#9]: Establish a convention about which way _our_ models face; # figure out whether we need something like this. (Hopefully not?) heading += 90.0 entity.rootNode.setHpr(heading, 0, 0) moveInterval = entity.rootNode.posInterval(config.TICK_LENGTH, (x, y, z)) moveInterval.start() if entity.isActor and "walk" in entity.model.getAnimNames(): currFrame = entity.model.getCurrentFrame("walk") if currFrame is None: currFrame = 0 # Supposedly, it's possible to pass a startFrame and a duration to # actorInterval, instead of calculating the endFrame ourself. But # for some reason, that doesn't seem to work; if I do that, then # the animation just keeps jumping around the early frames and # never gets past frame 5 or so. I'm not sure why. For now at # least, just calculate the endFrame ourselves to work around this. log.debug("Animating entity %s from frame %s/%s", gid, currFrame, entity.model.getNumFrames("walk")) frameRate = entity.model.getAnimControl("walk").getFrameRate() endFrame = currFrame + int(math.ceil(frameRate * config.TICK_LENGTH)) animInterval = entity.model.actorInterval( "walk", loop=1, startFrame=currFrame, endFrame=endFrame ) animInterval.start() def markSelected(self, gid, isSelected): log.debug("Marking graphical entity %s as %sselected", gid, "" if isSelected else "not ") entity = self.entities[gid] if isSelected: entity.setIndicator(self.loader.loadModel( getModelPath("unit-indicator-selected.egg") )) else: # You can't currently select others' units, so if a unit is being # deselected it must be mine. entity.setIndicator(self.loader.loadModel( getModelPath("unit-indicator-mine.egg") )) def displayResources(self, resourceAmt): self.resourceDisplay.setText("Resource: {}".format(resourceAmt)) def createSelectionBox(self, corner1, corner2): """ Create a selection "box" given the coordinates of two opposite corners. The corners are given in world coordinates (well, 3d graphics coordinates). """ assert self.selectionBox is None p1, p2, p3, p4 = self.convert3dBoxToScreen(corner1, corner2) x1, y1 = p1 x2, y2 = p2 x3, y3 = p3 x4, y4 = p4 # TODO[#3]: Magic numbers bad. self.selectionBox = LineSegs("SelectionBox") self.selectionBox.setThickness(3.0) self.selectionBox.setColor(0.0, 1.0, 0.25, 1.0) self.selectionBox.move_to(x1, 0, y1) self.selectionBox.draw_to(x2, 0, y2) self.selectionBox.draw_to(x3, 0, y3) self.selectionBox.draw_to(x4, 0, y4) self.selectionBox.draw_to(x1, 0, y1) self.selectionBoxNode = self.render2d.attachNewNode( self.selectionBox.create()) def moveSelectionBox(self, corner1, corner2): assert self.selectionBox is not None p1, p2, p3, p4 = self.convert3dBoxToScreen(corner1, corner2) x1, y1 = p1 x2, y2 = p2 x3, y3 = p3 x4, y4 = p4 self.selectionBox.setVertex(0, x1, 0, y1) self.selectionBox.setVertex(1, x2, 0, y2) self.selectionBox.setVertex(2, x3, 0, y3) self.selectionBox.setVertex(3, x4, 0, y4) self.selectionBox.setVertex(4, x1, 0, y1) def removeSelectionBox(self): self.selectionBoxNode.removeNode() self.selectionBox = None self.selectionBoxNode = None def convert3dBoxToScreen(self, corner1, corner3): """ Return screen coordinates of the 4 corners of a box, given in 3d coordinates. The box is specified using 2 opposite corners. """ wx1, wy1, wz1 = corner1 wx3, wy3, wz3 = corner3 wx2, wy2 = (wx1, wy3) wx4, wy4 = (wx3, wy1) # Note: corner1 and corner2 could have nonzero z because floating-point # calculations, but they should at least be close. We'll just average # their z and not worry about it. wz2 = wz4 = 0.5 * (wz1 + wz3) p1 = self.coord3dToScreen((wx1, wy1, wz1)) p2 = self.coord3dToScreen((wx2, wy2, wz2)) p3 = self.coord3dToScreen((wx3, wy3, wz3)) p4 = self.coord3dToScreen((wx4, wy4, wz4)) return (p1, p2, p3, p4) def setCameraCustom(self): """ Change to using our custom task to control the camera. """ # Disable the default mouse-based camera control task, so we don't have # to fight with it for control of the camera. self.disableMouse() # Face the camera in the appropriate angle. self.camera.setHpr(self.prevCameraHpr) # Put it in the same location as the cameraHolder, and make it stay # put relative to the cameraHolder (so we can move the camera around by # changing the cameraHolder's position). self.camera.reparentTo(self.cameraHolder) self.camera.setPos(0, 0, 0) # Substitute our own camera control task. self.taskMgr.add(self.updateCameraTask, "UpdateCameraTask") self.usingCustomCamera = True # Need a task to handle mouse-dragging because there doesn't seem to be # a built-in mouseMove event. self.taskMgr.add(self.mouseMoveTask, "MouseMoveTask") def setCameraDefault(self): """ Change to using the default mouse-based camera controls. """ self.taskMgr.remove("UpdateCameraTask") # Save current location for when this control style is restored. self.prevCameraHpr = self.camera.getHpr() # Use the existing camera location, rather than jumping back to the one # from last time the default camera controller was active. # Copied from https://www.panda3d.org/manual/index.php/Mouse_Support mat = Mat4(self.camera.getMat()) mat.invertInPlace() self.mouseInterfaceNode.setMat(mat) self.enableMouse() self.usingCustomCamera = False def toggleCameraStyle(self): """ Switch to whichever style of camera control isn't currently active. """ if self.usingCustomCamera: self.setCameraDefault() else: self.setCameraCustom() # We don't use task, but we can't remove it because the function signature # is from Panda3D. def updateCameraTask(self, task): # pylint: disable=unused-argument """ Move the camera sensibly. """ dt = self.globalClock.getDt() translateSpeed = 30 * dt rotateSpeed = 50 * dt # Separately track whether the camera should translate in each of the 4 # directions. These 4 are initialized based on the various inputs that # might tell us to scroll, and different inputs saying the same thing # don't stack. That way if we get inputs saying both "left" and # "right", they can cancel and the camera just won't move along that # axis -- even if, say, there are two inputs saying "left" and only one # saying "right'. moveLeft = self.keys["arrow_left"] moveRight = self.keys["arrow_right"] moveUp = self.keys["arrow_up"] moveDown = self.keys["arrow_down"] # Check if the mouse is over the window. if self.mouseWatcherNode.hasMouse(): # Get the position. # Each coordinate is normalized to the interval [-1, 1]. mousePos = self.mouseWatcherNode.getMouse() xPos, yPos = mousePos.getX(), mousePos.getY() # Only move if the mouse is close to the edge, and actually within # the window. if (1.0 - EDGE_SCROLL_WIDTH) < xPos <= 1.0: moveRight = 1 if -(1.0 - EDGE_SCROLL_WIDTH) > xPos >= -1.0: moveLeft = 1 if (1.0 - EDGE_SCROLL_WIDTH) < yPos <= 1.0: moveUp = 1 if -(1.0 - EDGE_SCROLL_WIDTH) > yPos >= -1.0: moveDown = 1 forward = translateSpeed * (moveUp - moveDown) sideways = translateSpeed * (moveRight - moveLeft) self.cameraHolder.setPos(self.cameraHolder, sideways, forward, 0) if sideways != 0 or forward != 0: self.updateSelectionBox() rotate = rotateSpeed * (self.keys["a"] - self.keys["d"]) self.cameraHolder.setHpr(self.cameraHolder, rotate, 0, 0) return Task.cont def zoomCamera(self, inward): """ Zoom in or out. """ dt = self.globalClock.getDt() zoomSpeed = 100 * dt zoom = -zoomSpeed if inward else zoomSpeed self.cameraHolder.setPos(self.cameraHolder, 0, 0, zoom) def centerView(self): """ Center the view sensibly. """ message = cmessages.RequestCenter() self.graphicsInterface.graphicsMessage(message.serialize()) # We don't use task, but we can't remove it because the function signature # is from Panda3D. def mouseMoveTask(self, task): # pylint: disable=unused-argument """ Handle mouse movement. """ mousePos = self.getMousePos() # NOTE: We don't handle clicking and dragging at the same time. if mousePos is not None and mousePos != self.prevMousePos: for (buttonId, state) in self.mouseState.iteritems(): state.lastPos = mousePos if state.hasMoved: self.handleMouseDragMove(buttonId, state.modifiers, state.startPos, mousePos) else: startX, startY = state.startPos mouseX, mouseY = mousePos distance = math.hypot(mouseX - startX, mouseY - startY) # TODO[#3]: Magic numbers bad. # Check if the mouse has moved outside the dead zone. if distance > 0.0314: self.handleMouseDragStart(buttonId, state.modifiers, state.startPos, mousePos) state.hasMoved = True if mousePos != self.prevMousePos: self.prevMousePos = mousePos return Task.cont def pandaEventMouseDown(self, buttonId, modifiers): if buttonId in self.mouseState: # Call pandaEventMouseUp just to clear any state related to the # button being down, so we can handle this buttonDown event as if # it were a fresh press of the button. log.warn("Mouse button %s is already down.", buttonId) self.pandaEventMouseUp(buttonId) assert buttonId not in self.mouseState state = MouseButtonState(modifiers[:], self.getMousePos()) self.mouseState[buttonId] = state def pandaEventMouseUp(self, buttonId): if buttonId not in self.mouseState: # Drop the event, since there's nothing to do. log.warn("Mouse button %s is already up.", buttonId) return state = self.mouseState[buttonId] if state.hasMoved: endPos = self.getMousePos() if endPos is None: endPos = state.lastPos self.handleMouseDragEnd(buttonId, state.modifiers, state.startPos, endPos) else: self.handleMouseClick(buttonId, state.modifiers, state.startPos) del self.mouseState[buttonId] def handleMouseClick(self, button, modifiers, pos): # Make sure the mouse is inside the screen # TODO: Move this check to pandaEventMouseUp? if self.mouseWatcherNode.hasMouse() and self.usingCustomCamera: x, y, _z = self.coordScreenTo3d(pos) if modifiers == []: # TODO: This component should take care of decoding the # click as far as "left" or "right"; we shouldn't send a # numerical button id to the graphicsInterface. message = cmessages.Click(button, (x, y)) elif button == 1 and modifiers == ["shift"]: message = cmessages.ShiftLClick((x, y)) elif button == 1 and modifiers == ["control"]: message = cmessages.ControlLClick((x, y)) elif button == 3 and modifiers == ["shift"]: message = cmessages.ShiftRClick((x, y)) elif button == 3 and modifiers == ["control"]: message = cmessages.ControlRClick((x, y)) else: thisShouldNeverHappen( "Unhandled modifiers for click: {}".format(modifiers)) self.graphicsInterface.graphicsMessage(message.serialize()) def handleMouseDragStart(self, buttonId, modifiers, startPos, endPos): log.debug("Start dragging from %s to %s", startPos, endPos) if buttonId == 1 and modifiers == []: assert self.selectionBoxOrigin is None self.selectionBoxOrigin = self.coordScreenTo3d(startPos) endPos = self.coordScreenTo3d(endPos) self.createSelectionBox(self.selectionBoxOrigin, endPos) def handleMouseDragMove(self, buttonId, modifiers, startPos, endPos): log.debug("Continue dragging from %s to %s", startPos, endPos) if buttonId == 1 and modifiers == []: assert self.selectionBoxOrigin is not None endPos = self.coordScreenTo3d(endPos) self.moveSelectionBox(self.selectionBoxOrigin, endPos) def handleMouseDragEnd(self, buttonId, modifiers, startPos, endPos): log.debug("End dragging from %s to %s", startPos, endPos) if buttonId == 1 and modifiers == []: # Actually select the units. endPos = self.coordScreenTo3d(endPos) # TODO[#55]: Use 3d graphics coords in messages so we don't have to # remove the z coordinates everywhere. message = cmessages.DragBox(self.selectionBoxOrigin[:2], endPos[:2]) self.graphicsInterface.graphicsMessage(message.serialize()) # Clear the selection box; we're done dragging. self.selectionBoxOrigin = None self.removeSelectionBox() def updateSelectionBox(self): if self.selectionBoxOrigin is not None: mousePos = self.getMousePos() if mousePos is not None: endPos = self.coordScreenTo3d(mousePos) self.moveSelectionBox(self.selectionBoxOrigin, endPos) def getMousePos(self): # Check if the mouse is over the window. if self.mouseWatcherNode.hasMouse(): # Get the position. # Each coordinate is normalized to the interval [-1, 1]. mousePoint = self.mouseWatcherNode.getMouse() # Create a copy of mousePoint rather than returning a reference to # it, because mousePoint will be modified in place by Panda. return (mousePoint.getX(), mousePoint.getY()) else: return None def handleWindowClose(self): log.info("Window close requested -- shutting down client.") message = cmessages.RequestQuit() self.graphicsInterface.graphicsMessage(message.serialize()) def setupEventHandlers(self): def pushKey(key, value): self.keys[key] = value for key in ["arrow_up", "arrow_left", "arrow_right", "arrow_down", "w", "a", "d", "s"]: self.keys[key] = False self.accept(key, pushKey, [key, True]) self.accept("shift-%s" % key, pushKey, [key, True]) self.accept("%s-up" % key, pushKey, [key, False]) # Camera toggle. self.accept("f3", self.toggleCameraStyle, []) self.accept("shift-f3", self.toggleCameraStyle, []) # Center view. self.accept("space", self.centerView, []) # Handle mouse wheel. self.accept("wheel_up", self.zoomCamera, [True]) self.accept("wheel_down", self.zoomCamera, [False]) # Handle clicking. self.accept("mouse1", self.pandaEventMouseDown, [1, []]) self.accept("mouse1-up", self.pandaEventMouseUp, [1]) # TODO: Make sure this is always the right mouse button. self.accept("mouse3", self.pandaEventMouseDown, [3, []]) self.accept("mouse3-up", self.pandaEventMouseUp, [3]) # Handle clicking with modifier keys. self.accept("shift-mouse1", self.pandaEventMouseDown, [1, ["shift"]]) self.accept("control-mouse1", self.pandaEventMouseDown, [1, ["control"]]) self.accept("shift-mouse3", self.pandaEventMouseDown, [3, ["shift"]]) self.accept("control-mouse3", self.pandaEventMouseDown, [3, ["control"]]) # Handle window close request (clicking the X, Alt-F4, etc.) self.win.set_close_request_event("window-close") self.accept("window-close", self.handleWindowClose) def coord3dToScreen(self, coord3d): # Empirically, Lens.project takes coordinates in the *camera*'s # coordinate system, not its parent or the render. This was not very # clear from the documentation, and you'd be surprised how long it took # us to figure this out. Anyway, we need to convert the point to be # relative to self.camera here; otherwise we'll get bizarre, # nonsensical, and hard-to-debug results. coord3d = self.camera.getRelativePoint(self.render, coord3d) screenCoord = Point2() if not self.camLens.project(coord3d, screenCoord): log.debug("Attempting 3d-to-screen conversion on point outside of " "camera's viewing frustum.") # Convert to a tuple to ensure no one else is keeping a reference # around. x, y = screenCoord return (x, y) def coordScreenTo3d(self, screenCoord): x, y = screenCoord screenPoint = Point2(x, y) # Do this calculation using simple geometry, rather than the absurd # collision-traversal nonsense we used to use. Thanks to # https://www.panda3d.org/forums/viewtopic.php?t=5409 # for pointing us at the right methods to make this work. # Get two points along the ray extending from the camera, in the # direction of the mouse click. nearPoint = Point3() farPoint = Point3() self.camLens.extrude(screenPoint, nearPoint, farPoint) # These points are relative to the camera, so need to be converted to # be relative to the render. Thanks to the example code (see link # above) for saving us probably some hours of debugging figuring that # one out again :) nearPoint = self.render.getRelativePoint(self.camera, nearPoint) farPoint = self.render.getRelativePoint(self.camera, farPoint) intersection = Point3() if self.groundPlane.intersectsLine(intersection, nearPoint, farPoint): # Convert to a tuple to ensure no one else is keeping a reference # around. x, y, z = intersection return (x, y, z) # The ray didn't intersect the ground. This is almost certainly going # to happen at some point; all you have to do is find a way to aim the # camera (or manipulate the screen coordinate) so that the ray points # horizontally. But we don't have code to handle it, so for now just # abort. thisIsNotHandled()
def _createDebugLine(self, points, connectToEnd=False): """ Helper for visualizing the light bounds. Draws a line trough all points. When connectToEnd is true, the last point will get connected to the first point. """ segs = LineSegs() segs.setThickness(1.0) segs.setColor(Vec4(1, 1, 0, 1)) segs.moveTo(points[0]) for point in points[1:]: segs.drawTo(point) if connectToEnd: segs.drawTo(points[0]) return NodePath(segs.create())
def __init__(self): ShowBase.__init__(self) winProps = WindowProperties() winProps.setTitle("Triangle and SimpleCircle Unittest") # base.win.requestProperties(winProps) (same as below e.g. self == base) self.win.requestProperties(winProps) # 1) create GeomVertexData frmt = GeomVertexFormat.getV3n3cp() vdata = GeomVertexData('triangle_developer', frmt, Geom.UHDynamic) # 2) create Writers/Rewriters (must all be created before any readers and readers are one-pass-temporary) vertex = GeomVertexRewriter(vdata, 'vertex') normal = GeomVertexRewriter(vdata, 'normal') color = GeomVertexRewriter(vdata, 'color') zUp = Vec3(0, 0, 1) wt = Vec4(1.0, 1.0, 1.0, 1.0) gr = Vec4(0.5, 0.5, 0.5, 1.0) # 3) write each column on the vertex data object (for speed, have a different writer for each column) def addPoint(x, y, z): vertex.addData3f(x, y, z) normal.addData3f(zUp) color.addData4f(wt) addPoint(0.0, 0.0, 0.0) addPoint(5.0, 0.0, 0.0) addPoint(0.0, 5.0, 0.0) addPoint(15.0, 15.0, 0.0) # 4) create a primitive and add the vertices via index (not truely associated with the actual vertex table, yet) tris = GeomTriangles(Geom.UHDynamic) t1 = Triangle(0, 1, 2, vdata, tris, vertex) t2 = Triangle(2, 1, 3, vdata, tris, vertex) c1 = t1.getCircumcircle() t1AsEnum = t1.asPointsEnum() r0 = (t1AsEnum.point0 - c1.center).length() r1 = (t1AsEnum.point1 - c1.center).length() r2 = (t1AsEnum.point2 - c1.center).length() assert abs(r0 - r2) < utilities.EPSILON and abs(r0 - r1) < utilities.EPSILON t2AsEnum = t2.asPointsEnum() c2 = t2.getCircumcircle() r0 = (t2AsEnum.point0 - c2.center).length() r1 = (t2AsEnum.point1 - c2.center).length() r2 = (t2AsEnum.point2 - c2.center).length() assert abs(r0 - r2) < utilities.EPSILON and abs(r0 - r1) < utilities.EPSILON assert t1.getAngleDeg0() == 90.0 assert t1.getAngleDeg1() == t1.getAngleDeg2() oldInd0 = t1.pointIndex0 oldInd1 = t1.pointIndex1 oldInd2 = t1.pointIndex2 t1.pointIndex0 = t1.pointIndex1 t1.pointIndex1 = oldInd0 assert t1.pointIndex0 == oldInd1 assert t1.pointIndex1 == oldInd0 assert t1.pointIndex0 != t1.pointIndex1 t1.reverse() assert t1.pointIndex1 == oldInd2 # 5.1) (adding to scene) create a Geom and add primitives of like base-type i.e. triangles and triangle strips geom = Geom(vdata) geom.addPrimitive(tris) # 5.2) create a GeomNode to hold the Geom(s) and add the Geom(s) gn = GeomNode('gnode') gn.addGeom(geom) # 5.3) attache the node to the scene gnNodePath = render.attachNewNode(gn) # setup a wire frame wireNP = render.attachNewNode('wire') wireNP.setPos(0.0, 0.0, .1) wireNP.setColor(0.1, 0.1, 0.1, 1) wireNP.setRenderMode(RenderModeAttrib.MWireframe, .5, 0) gnNodePath.instanceTo(wireNP) # test and draw intersections and circles pt1 = Point3(0.0, 5.0, 0.0) pt2 = Point3(1.0, 5.0, 0.0) intersection = t2.getIntersectionsWithCircumcircle(pt1, pt2) circle = t2.getCircumcircle() cuts = 128 border = circle.getBorder(cuts, closed=True) assert len(border) == cuts or (len(border) == cuts + 1 and border[0] == border[len(border) - 1]) n = len(border) xMid = yMid = 0 for p in border: xMid += p.x yMid += p.y mid = Point3(xMid / n, yMid / n, border[0].z) assert mid.almostEqual(circle.center, 0.06) assert t2.isCcw() assert t1.containsPoint(c1.center) != t1.containsPoint(c1.center, includeEdges=False) circleSegs = LineSegs("circleLines") circleSegs.setColor(1.0, 0.0, 0.0, 1.0) for p in border: circleSegs.drawTo(*p) circleNode = circleSegs.create(False) circleNP = render.attachNewNode(circleNode) circleNP.setZ(-5) originSpot = LineSegs("intersection") originSpot.setColor(1.0, 0.0, 0.0, 1.0) originSpot.setThickness(10) for p in intersection: originSpot.drawTo(p) spotNode = originSpot.create(False) spotNP = render.attachNewNode(spotNode) circleNP.setZ(-0.75) # fix the camera rot/pos PHF.PanditorDisableMouseFunc() camera.setPos(0.0, 0.0, 50.0) camera.lookAt(c2.center) PHF.PanditorEnableMouseFunc()