Beispiel #1
0
 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
Beispiel #2
0
    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())
Beispiel #3
0
    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)
Beispiel #4
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)
Beispiel #5
0
 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]
Beispiel #6
0
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
Beispiel #7
0
 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( )
Beispiel #10
0
 def __init__(self, parentNodePath):
   self.mainNodePath = NodePath("Lines")
   self.mainNodePath.reparentTo(parentNodePath)
   
   self.lineSegs = LineSegs()
   self.lineSegs.setThickness(1)
   self.lineNodePaths = []
   self.points = []
Beispiel #11
0
 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)
Beispiel #12
0
	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)
Beispiel #15
0
    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
Beispiel #16
0
 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)]
Beispiel #17
0
 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)
Beispiel #18
0
 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)
Beispiel #19
0
    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
Beispiel #20
0
    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)
Beispiel #22
0
 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)
Beispiel #23
0
 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)
Beispiel #24
0
 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)]
Beispiel #25
0
 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)
Beispiel #26
0
 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)
Beispiel #27
0
    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())
Beispiel #28
0
    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())
Beispiel #29
0
    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())
Beispiel #30
0
    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
Beispiel #32
0
 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)
Beispiel #33
0
Datei: ui.py Projekt: tgbugs/desc
 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'])
Beispiel #34
0
 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()
Beispiel #36
0
 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
Beispiel #37
0
    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 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
Beispiel #40
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)
Beispiel #41
0
    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()
Beispiel #42
0
    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
Beispiel #43
0
    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 + "  ")
Beispiel #44
0
 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)
Beispiel #45
0
    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()
Beispiel #46
0
 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()))
Beispiel #47
0
    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)
Beispiel #48
0
    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)
Beispiel #50
0
    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)
Beispiel #51
0
 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
Beispiel #53
0
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()
Beispiel #56
0
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
Beispiel #57
0
    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
Beispiel #58
0
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()
Beispiel #59
0
    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())
Beispiel #60
0
    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()