Exemple #1
0
class MapObject(MapWritable):

    ObjectName = "object"

    def __init__(self, id):
        MapObjectInit.start()

        MapWritable.__init__(self, base.document)
        self.temporary = False
        self.id = id
        self.selected = False
        self.classname = ""
        self.parent = None
        self.children = {}
        self.boundingBox = BoundingBox(Vec3(-0.5, -0.5, -0.5), Vec3(0.5, 0.5, 0.5))
        self.boundsBox = Box()
        self.boundsBox.addView(GeomView.Lines, VIEWPORT_3D_MASK, state = BoundsBox3DState)
        self.boundsBox.addView(GeomView.Lines, VIEWPORT_2D_MASK, state = BoundsBox2DState)
        self.boundsBox.generateGeometry()
        self.collNp = None

        self.group = None

        self.properties = {}

        # All MapObjects have transform
        self.addProperty(OriginProperty(self))
        self.addProperty(AnglesProperty(self))
        self.addProperty(ScaleProperty(self))
        self.addProperty(ShearProperty(self))

        self.np = NodePath(ModelNode(self.ObjectName + ".%i" % self.id))
        self.np.setPythonTag("mapobject", self)
        self.applyCollideMask()
        # Test bounding volume at this node and but nothing below it.
        self.np.node().setFinal(True)

        MapObjectInit.stop()

    def getClassName(self):
        return self.classname

    def isWorld(self):
        return False

    def r_findAllParents(self, parents, type):
        if not self.parent or self.parent.isWorld():
            return

        if type is None or isinstance(self.parent, type):
            parents.append(self.parent)

        self.parent.r_findAllParents(parents, type)

    def findAllParents(self, type = None):
        parents = []
        self.r_findAllParents(parents, type)
        return parents

    def findTopmostParent(self, type = None):
        parents = self.findAllParents(type)
        if len(parents) == 0:
            return None

        return parents[len(parents) - 1]

    def r_findAllChildren(self, children, type):
        for child in self.children.values():
            if type is None or isinstance(child, type):
                children.append(child)
            child.r_findAllChildren(children, type)

    def findAllChildren(self, type = None):
        children = []

        self.r_findAllChildren(children, type)

        return children

    def applyCollideMask(self):
        self.np.setCollideMask(LEGlobals.ObjectMask)

    def setTemporary(self, flag):
        self.temporary = flag

    # Returns the bounding volume of the object itself, not including children objects.
    def getObjBounds(self, other = None):
        if not other:
            other = self.np.getParent()
        return self.np.getTightBounds(other)

    # Returns the min and max points of the bounds of the object, not including children.
    def getBounds(self, other = None):
        if not other:
            other = self.np.getParent()
        mins = Point3()
        maxs = Point3()
        self.np.calcTightBounds(mins, maxs, other)
        return [mins, maxs]

    def findChildByID(self, id):
        if id == self.id:
            return self

        if id in self.children:
            return self.children[id]

        for child in self.children.values():
            ret = child.findChildByID(id)
            if ret is not None:
                return ret

        return None

    def hasChildWithID(self, id):
        return id in self.children

    def copy(self, generator):
        raise NotImplementedError

    def paste(self, o, generator):
        raise NotImplementedError

    def clone(self):
        raise NotImplementedError

    def unclone(self, o):
        raise NotImplementedError

    #
    # Base copy and paste functions shared by all MapObjects.
    # Each specific MapObject must implement the functions above for their
    # specific functionality.
    #

    def copyProperties(self, props):
        newProps = {}
        for key, prop in props.items():
            newProp = prop.clone(self)
            newProp.setValue(prop.getValue())
            newProps[key] = newProp
        self.updateProperties(newProps)

    def copyBase(self, other, generator, clone = False):
        if clone and other.id != self.id:
            parent = other.parent
            setPar = other.parent is not None and other.parent.hasChildWithID(other.id) and other.parent.children[other.id] == other
            if setPar:
                other.reparentTo(NodePath())
            other.id = self.id
            if setPar:
                other.reparentTo(parent)

        other.parent = self.parent

        for child in self.children.values():
            if clone:
                newChild = child.clone()
            else:
                newChild = child.copy(generator)
            newChild.reparentTo(other)

        other.setClassname(self.classname)
        other.copyProperties(self.properties)
        other.selected = self.selected

    def pasteBase(self, o, generator, performUnclone = False):
        if performUnclone and o.id != self.id:
            parent = self.parent
            setPar = self.parent is not None and self.parent.hasChildWithID(self.id) and self.parent.children[self.id] == self
            if setPar:
                self.reparentTo(NodePath())
            self.id = o.id
            if setPar:
                self.reparentTo(parent)

        for child in o.children.values():
            if performUnclone:
                newChild = child.clone()
            else:
                newChild = child.copy(generator)
            newChild.reparentTo(self)

        self.setClassname(o.classname)
        self.copyProperties(o.properties)
        self.selected = o.selected

    def getName(self):
        return "Object"

    def getDescription(self):
        return "Object in a map."

    def addProperty(self, prop):
        self.properties[prop.name] = prop

    # Returns list of property names with the specified value types.
    def getPropsWithValueType(self, types):
        if isinstance(types, str):
            types = [types]
        props = []
        for propName, prop in self.properties.items():
            if prop.valueType in types:
                props.append(propName)
        return props

    def getPropNativeType(self, key):
        prop = self.properties.get(key, None)
        if not prop:
            return str

        return prop.getNativeType()

    def getPropValueType(self, key):
        prop = self.properties.get(key, None)
        if not prop:
            return "string"

        return prop.valueType

    def getPropDefaultValue(self, prop):
        if isinstance(prop, str):
            prop = self.properties.get(prop, None)

        if not prop:
            return ""

        return prop.defaultValue

    def getPropertyValue(self, key, asString = False, default = ""):
        prop = self.properties.get(key, None)
        if not prop:
            return default

        if asString:
            return prop.getSerializedValue()
        else:
            return prop.getValue()

    def getProperty(self, name):
        return self.properties.get(name, None)

    def updateProperties(self, data):
        for key, value in data.items():
            if not isinstance(value, ObjectProperty):
                # If only a value was specified and not a property object itself,
                # this is an update to an existing property.

                prop = self.properties.get(key, None)
                if not prop:
                    continue

                oldValue = prop.getValue()

                val = prop.getUnserializedValue(value)

                # If the property has a min/max range, ensure the value we want to
                # set is within that range.
                if (not prop.testMinValue(val)) or (not prop.testMaxValue(val)):
                    # Not within range. Use the default value
                    val = prop.defaultValue

                prop.setValue(val)
            else:
                # A property object was given, simply add it to the dict of properties.
                prop = value
                oldValue = None
                val = prop.getValue()
                self.properties[prop.name] = prop

            self.propertyChanged(prop, oldValue, val)

    def propertyChanged(self, prop, oldValue, newValue):
        if oldValue != newValue:
            self.send('objectPropertyChanged', [self, prop, newValue])

    def setAbsOrigin(self, origin):
        self.np.setPos(base.render, origin)
        self.transformChanged()

    def setOrigin(self, origin):
        self.np.setPos(origin)
        self.transformChanged()

    def getAbsOrigin(self):
        return self.np.getPos(base.render)

    def getOrigin(self):
        return self.np.getPos()

    def setAngles(self, angles):
        self.np.setHpr(angles)
        self.transformChanged()

    def setAbsAngles(self, angles):
        self.np.setHpr(base.render, angles)
        self.transformChanged()

    def getAbsAngles(self):
        return self.np.getHpr(base.render)

    def getAngles(self):
        return self.np.getHpr()

    def setScale(self, scale):
        self.np.setScale(scale)
        self.transformChanged()

    def setAbsScale(self, scale):
        self.np.setScale(base.render, scale)
        self.transformChanged()

    def getAbsScale(self):
        return self.np.getScale(base.render)

    def getScale(self):
        return self.np.getScale()

    def setShear(self, shear):
        self.np.setShear(shear)
        self.transformChanged()

    def setAbsShear(self, shear):
        self.np.setShear(base.render, shear)
        self.transformChanged()

    def getAbsShear(self):
        return self.np.getShear(base.render)

    def getShear(self):
        return self.np.getShear()

    def transformChanged(self):
        self.recalcBoundingBox()
        self.send('objectTransformChanged', [self])

    def showBoundingBox(self):
        self.boundsBox.np.reparentTo(self.np)

    def hideBoundingBox(self):
        self.boundsBox.np.reparentTo(NodePath())

    def select(self):
        self.selected = True
        self.showBoundingBox()
        #self.np.setColorScale(1, 0, 0, 1)

    def deselect(self):
        self.selected = False
        self.hideBoundingBox()
        #self.np.setColorScale(1, 1, 1, 1)

    def setClassname(self, classname):
        self.classname = classname

    def fixBounds(self, mins, maxs):
        # Ensures that the bounds are not flat on any axis
        sameX = mins.x == maxs.x
        sameY = mins.y == maxs.y
        sameZ = mins.z == maxs.z

        invalid = False

        if sameX:
            # Flat horizontal
            if sameY and sameZ:
                invalid = True
            elif not sameY:
                mins.x = mins.y
                maxs.x = maxs.y
            elif not sameZ:
                mins.x = mins.z
                maxs.x = maxs.z

        if sameY:
            # Flat forward/back
            if sameX and sameZ:
                invalid = True
            elif not sameX:
                mins.y = mins.x
                maxs.y = maxs.x
            elif not sameZ:
                mins.y = mins.z
                maxs.y = maxs.z

        if sameZ:
            if sameX and sameY:
                invalid = True
            elif not sameX:
                mins.z = mins.x
                maxs.z = maxs.x
            elif not sameY:
                mins.z = mins.y
                maxs.z = maxs.y

        return [invalid, mins, maxs]

    def recalcBoundingBox(self):
        if not self.np:
            return

        # Don't have the picker box or selection visualization contribute to the
        # calculation of the bounding box.
        if self.collNp:
            self.collNp.stash()
        self.hideBoundingBox()

        # Calculate a bounding box relative to ourself
        mins, maxs = self.getBounds(self.np)

        invalid, mins, maxs = self.fixBounds(mins, maxs)
        if invalid:
            mins = Point3(-8)
            maxs = Point3(8)

        self.boundingBox = BoundingBox(mins, maxs)
        self.boundsBox.setMinMax(mins, maxs)
        if self.selected:
            self.showBoundingBox()

        if self.collNp:
            self.collNp.unstash()
            self.collNp.node().clearSolids()
            self.collNp.node().addSolid(CollisionBox(mins, maxs))
            self.collNp.hide(~VIEWPORT_3D_MASK)

        self.send('mapObjectBoundsChanged', [self])

    def removePickBox(self):
        if self.collNp:
            self.collNp.removeNode()
            self.collNp = None

    def delete(self):
        if not self.temporary:
            # Take the children with us
            for child in list(self.children.values()):
                child.delete()
            self.children = None
            # if we are selected, deselect
            base.selectionMgr.deselect(self)

        if self.boundsBox:
            self.boundsBox.cleanup()
            self.boundsBox = None

        self.removePickBox()

        if not self.temporary:
            self.reparentTo(NodePath())
        self.np.removeNode()
        self.np = None
        self.properties = None
        self.metaData = None

        self.temporary = None

    def __clearParent(self):
        if self.parent:
            self.parent.__removeChild(self)
            self.np.reparentTo(NodePath())
            self.parent = None

    def __setParent(self, other):
        if isinstance(other, NodePath):
            # We are reparenting directly to a NodePath, outside of the MapObject tree.
            self.parent = None
            self.np.reparentTo(other)
        else:
            self.parent = other
            if self.parent:
                self.parent.__addChild(self)
                self.np.reparentTo(self.parent.np)
            else:
                # If None was passed, assume base.render
                self.np.reparentTo(base.render)

    def reparentTo(self, other):
        # If a NodePath is passed to this method, the object will be placed under the specified node
        # in the Panda3D scene graph, but will be taken out of the MapObject tree. If None is passed,
        # the object will be parented to base.render and taken out of the MapObject tree.
        #
        # Use reparentTo(NodePath()) to place the object outside of both the scene graph and the
        # MapObject tree.
        self.__clearParent()
        self.__setParent(other)

    def __addChild(self, child):
        self.children[child.id] = child
        #self.recalcBoundingBox()

    def __removeChild(self, child):
        if child.id in self.children:
            del self.children[child.id]
            #self.recalcBoundingBox()

    def doWriteKeyValues(self, parent):
        kv = CKeyValues(self.ObjectName, parent)
        self.writeKeyValues(kv)
        for child in self.children.values():
            child.doWriteKeyValues(kv)

    def writeKeyValues(self, keyvalues):
        keyvalues.setKeyValue("id", str(self.id))
        # Write out our object properties
        for name, prop in self.properties.items():
            prop.writeKeyValues(keyvalues)

    def readKeyValues(self, keyvalues):
        for i in range(keyvalues.getNumKeys()):
            key = keyvalues.getKey(i)
            value = keyvalues.getValue(i)
            if MetaData.isPropertyExcluded(key):
                continue
            # Find the property with this name.
            prop = self.properties.get(key, None)
            if not prop:
                # Prop wasn't explicit or part of FGD metadata (if it's an Entity)
                continue

            nativeValue = prop.getUnserializedValue(value)

            # Set the value!
            self.updateProperties({prop.name: nativeValue})
Exemple #2
0
    def __init__(self,
                 items,
                 parent=None,
                 sidePad=.0,
                 edgePos=PTop,
                 align=ALeft,
                 effect=ENone,
                 buttonThrower=None,
                 font=None,
                 baselineOffset=.0,
                 scale=.05,
                 itemHeight=1.,
                 leftPad=.0,
                 separatorHeight=.5,
                 underscoreThickness=1,
                 BGColor=(0, 0, 0, .7),
                 BGBorderColor=(1, .85, .4, 1),
                 separatorColor=(1, 1, 1, 1),
                 frameColorHover=(1, .85, .4, 1),
                 frameColorPress=(0, 1, 0, 1),
                 textColorReady=(1, 1, 1, 1),
                 textColorHover=(0, 0, 0, 1),
                 textColorPress=(0, 0, 0, 1),
                 textColorDisabled=(.5, .5, .5, 1),
                 draggable=False,
                 onMove=None):
        '''
      sidePad : additional space on the left and right of the text item
      edgePos : menu bar position on the screen,
                  use DropDownMenu.PLeft, PRight, PBottom, or PTop
      align   : menu items alignment on menu bar,
                  use DropDownMenu.ALeft, ACenter, or ARight
      effect  : the drop down appearance effect,
                  use DropDownMenu.ENone, EFade, ESlide, or EStretch
      draggable : menu bar's draggability status
      onMove : a function which will be called after changing edge position

      Read the remaining options documentation in PopupMenu class.
      '''
        self.parent = parent if parent else getattr(
            base, DropDownMenu.parents[edgePos][align])
        self.BT = buttonThrower if buttonThrower else base.buttonThrowers[
            0].node()
        self.menu = self.parent.attachNewNode('dropdownmenu-%s' % id(self))
        self.font = font if font else TextNode.getDefaultFont()
        self.baselineOffset = baselineOffset
        if isinstance(scale, (float, int)):
            scale = (scale, 1.0, scale)
        self.scale = scale
        self.itemHeight = itemHeight
        self.sidePad = sidePad
        self.edgePos = edgePos
        self.alignment = align
        self.effect = effect
        self.leftPad = leftPad
        self.underscoreThickness = underscoreThickness
        self.separatorHeight = separatorHeight
        self.BGColor = BGColor
        self.BGBorderColor = BGBorderColor
        self.separatorColor = separatorColor
        self.frameColorHover = frameColorHover
        self.frameColorPress = frameColorPress
        self.textColorReady = textColorReady
        self.textColorHover = textColorHover
        self.textColorPress = textColorPress
        self.textColorDisabled = textColorDisabled
        self.draggable = draggable
        self.onMove = onMove
        self.dropDownMenu = self.whoseDropDownMenu = None

        self.gapFromEdge = gapFromEdge = .008
        texMargin = self.font.getTextureMargin() * self.scale[0] * .25
        b = DirectButton(parent=NodePath(''),
                         text='^|g_',
                         text_font=self.font,
                         scale=self.scale)
        fr = b.node().getFrame()
        b.getParent().removeNode()
        baselineToCenter = (fr[2] + fr[3]) * self.scale[0]
        LH = (fr[3] - fr[2]) * self.itemHeight * self.scale[2]
        baselineToTop = (fr[3] * self.itemHeight * self.scale[2] /
                         LH) / (1. + self.baselineOffset)
        baselineToBot = LH / self.scale[2] - baselineToTop
        self.height = LH + .01
        l, r, b, t = 0, 5, -self.height, 0
        self.menuBG = DirectFrame(parent=self.menu,
                                  frameColor=BGColor,
                                  frameSize=(l, r, b, t),
                                  state=DGG.NORMAL,
                                  suppressMouse=1)
        if self.draggable:
            self.setDraggable(1)
        LSborder = LineSegs()
        LSborder.setThickness(2)
        LSborder.setColor(0, 0, 0, 1)
        LSborder.moveTo(l, 0, b)
        LSborder.drawTo(r, 0, b)
        self.menuBG.attachNewNode(LSborder.create())
        self.itemsParent = self.menu.attachNewNode('menu items parent')

        x = sidePad * self.scale[0] + gapFromEdge
        for t, menuItemsGenerator in items:
            underlinePos = t.find('_')
            t = t.replace('_', '')
            b = DirectButton(
                parent=self.itemsParent,
                text=t,
                text_font=self.font,
                pad=(sidePad, 0),
                scale=self.scale,
                pos=(x, 0, -baselineToTop * self.scale[2] - gapFromEdge),
                text_fg=textColorReady,
                # text color when mouse over
                text2_fg=textColorHover,
                # text color when pressed
                text1_fg=textColorPress,
                # framecolor when pressed
                frameColor=frameColorPress,
                command=self.__createMenu,
                extraArgs=[True, menuItemsGenerator],
                text_align=TextNode.ALeft,
                relief=DGG.FLAT,
                rolloverSound=0,
                clickSound=0)
            b['extraArgs'] += [b.getName()]
            b.stateNodePath[2].setColor(
                *frameColorHover)  # framecolor when mouse over
            b.stateNodePath[0].setColor(0, 0, 0, 0)  # framecolor when ready
            fr = b.node().getFrame()
            b['frameSize'] = (fr[0], fr[1], -baselineToBot, baselineToTop)
            self.accept(DGG.ENTER + b.guiId, self.__createMenu,
                        [False, menuItemsGenerator,
                         b.getName()])
            if underlinePos > -1:
                tn = TextNode('')
                tn.setFont(self.font)
                tn.setText(t[:underlinePos + 1])
                tnp = NodePath(tn.getInternalGeom())
                underlineXend = tnp.getTightBounds()[1][0]
                tnp.removeNode()
                tn.setText(t[underlinePos])
                tnp = NodePath(tn.getInternalGeom())
                b3 = tnp.getTightBounds()
                underlineXstart = underlineXend - (b3[1] - b3[0])[0]
                tnp.removeNode()
                LSunder = LineSegs()
                LSunder.setThickness(underscoreThickness)
                LSunder.moveTo(underlineXstart + texMargin, 0,
                               -.7 * baselineToBot)
                LSunder.drawTo(underlineXend - texMargin, 0,
                               -.7 * baselineToBot)
                underline = b.stateNodePath[0].attachNewNode(LSunder.create())
                underline.setColor(Vec4(*textColorReady), 1)
                underline.copyTo(b.stateNodePath[1],
                                 10).setColor(Vec4(*textColorPress), 1)
                underline.copyTo(b.stateNodePath[2],
                                 10).setColor(Vec4(*textColorHover), 1)
                self.accept('alt-' + t[underlinePos].lower(),
                            self.__createMenu,
                            [True, menuItemsGenerator,
                             b.getName()])
            x += (fr[1] - fr[0]) * self.scale[0]
        self.width = x - 2 * gapFromEdge
        self.align(align)
        self.setEdgePos(edgePos)
        self.minZ = base.a2dBottom + self.height if edgePos == DropDownMenu.PBottom else None
        viewPlaneNode = PlaneNode('cut menu')
        viewPlaneNode.setPlane(Plane(Vec3(0, 0, -1), Point3(0, 0, -LH)))
        self.clipPlane = self.menuBG.attachNewNode(viewPlaneNode)
Exemple #3
0
    def __init__(self,
                 items,
                 parent=None,
                 buttonThrower=None,
                 onDestroy=None,
                 font=None,
                 baselineOffset=.0,
                 scale=.05,
                 itemHeight=1.,
                 leftPad=.0,
                 separatorHeight=.5,
                 underscoreThickness=1,
                 BGColor=(0, 0, 0, .7),
                 BGBorderColor=(1, .85, .4, 1),
                 separatorColor=(1, 1, 1, 1),
                 frameColorHover=(1, .85, .4, 1),
                 frameColorPress=(0, 1, 0, 1),
                 textColorReady=(1, 1, 1, 1),
                 textColorHover=(0, 0, 0, 1),
                 textColorPress=(0, 0, 0, 1),
                 textColorDisabled=(.5, .5, .5, 1),
                 minZ=None,
                 useMouseZ=True):
        '''
      items : a collection of menu items
         Item format :
            ( 'Item text', 'path/to/image', command )
                        OR
            ( 'Item text', 'path/to/image', command, arg1,arg2,.... )
         If you don't want to use an image, pass 0.

         To create disabled item, pass 0 for the command :
            ( 'Item text', 'path/to/image', 0 )
         so, you can easily switch between enabled or disabled :
            ( 'Item text', 'path/to/image', command if commandEnabled else 0 )
                        OR
            ( 'Item text', 'path/to/image', (0,command)[commandEnabled] )

         To create submenu, pass a sequence of submenu items for the command.
         To create disabled submenu, pass an empty sequence for the command.

         To enable hotkey, insert an underscore before the character,
         e.g. hotkey of 'Item te_xt' is 'x' key.

         To add shortcut key text at the right side of the item, append it at the end of
         the item text, separated by "more than" sign, e.g. 'Item text>Ctrl-T'.

         To insert separator line, pass 0 for the whole item.


      parent : where to attach the menu, defaults to aspect2d

      buttonThrower : button thrower whose thrown events are blocked temporarily
                      when the menu is displayed. If not given, the default
                      button thrower is used

      onDestroy : user function which will be called after the menu is fully destroyed
      
      font           : text font
      baselineOffset : text's baseline Z offset

      scale       : text scale
      itemHeight  : spacing between items, defaults to 1
      leftPad     : blank space width before text
      separatorHeight : separator line height, relative to itemHeight

      underscoreThickness : underscore line thickness

      BGColor, BGBorderColor, separatorColor, frameColorHover, frameColorPress,
      textColorReady, textColorHover, textColorPress, textColorDisabled
      are some of the menu components' color

      minZ : minimum Z position to restrain menu's bottom from going offscreen (-1..1).
             If it's None, it will be set a little above the screen's bottom.
      '''
        self.parent = parent if parent else aspect2d
        self.onDestroy = onDestroy
        self.BT = buttonThrower if buttonThrower else base.buttonThrowers[
            0].node()
        self.menu = NodePath('menu-%s' % id(self))
        self.parentMenu = None
        self.submenu = None
        self.BTprefix = self.menu.getName() + '>'
        self.submenuCreationTaskName = 'createSubMenu-' + self.BTprefix
        self.submenuRemovalTaskName = 'removeSubMenu-' + self.BTprefix
        self.font = font if font else TextNode.getDefaultFont()
        self.baselineOffset = baselineOffset
        if isinstance(scale, (float, int)):
            scale = (scale, 1.0, scale)
        self.scale = scale
        self.itemHeight = itemHeight
        self.leftPad = leftPad
        self.separatorHeight = separatorHeight
        self.underscoreThickness = underscoreThickness
        self.BGColor = BGColor
        self.BGBorderColor = BGBorderColor
        self.separatorColor = separatorColor
        self.frameColorHover = frameColorHover
        self.frameColorPress = frameColorPress
        self.textColorReady = textColorReady
        self.textColorHover = textColorHover
        self.textColorPress = textColorPress
        self.textColorDisabled = textColorDisabled
        self.minZ = minZ
        self.mpos = Point2(base.mouseWatcherNode.getMouse())

        self.itemCommand = []
        self.hotkeys = {}
        self.numItems = 0
        self.sel = -1
        self.selByKey = False

        bgPad = self.bgPad = .0125
        texMargin = self.font.getTextureMargin() * self.scale[0] * .25
        b = DirectButton(parent=NodePath(''),
                         text='^|g_',
                         text_font=self.font,
                         scale=self.scale)
        fr = b.node().getFrame()
        b.getParent().removeNode()
        baselineToCenter = (fr[2] + fr[3]) * self.scale[0]
        LH = (fr[3] - fr[2]) * self.itemHeight * self.scale[2]
        imageHalfHeight = .5 * (fr[3] - fr[2]) * self.itemHeight * .85
        arrowHalfHeight = .5 * (fr[3] - fr[2]) * self.itemHeight * .5
        baselineToTop = (fr[3] * self.itemHeight * self.scale[2] /
                         LH) / (1. + self.baselineOffset)
        baselineToBot = LH / self.scale[2] - baselineToTop
        itemZcenter = (baselineToTop - baselineToBot) * .5
        separatorHalfHeight = .5 * separatorHeight * LH
        LSseparator = LineSegs()
        LSseparator.setColor(.5, .5, .5, .2)

        arrowVtx = [
            (0, itemZcenter),
            (-2 * arrowHalfHeight, itemZcenter + arrowHalfHeight),
            (-arrowHalfHeight, itemZcenter),
            (-2 * arrowHalfHeight, itemZcenter - arrowHalfHeight),
        ]
        tri = Triangulator()
        vdata = GeomVertexData('trig', GeomVertexFormat.getV3(), Geom.UHStatic)
        vwriter = GeomVertexWriter(vdata, 'vertex')
        for x, z in arrowVtx:
            vi = tri.addVertex(x, z)
            vwriter.addData3f(x, 0, z)
            tri.addPolygonVertex(vi)
        tri.triangulate()
        prim = GeomTriangles(Geom.UHStatic)
        for i in range(tri.getNumTriangles()):
            prim.addVertices(tri.getTriangleV0(i), tri.getTriangleV1(i),
                             tri.getTriangleV2(i))
            prim.closePrimitive()
        geom = Geom(vdata)
        geom.addPrimitive(prim)
        geomNode = GeomNode('arrow')
        geomNode.addGeom(geom)
        realArrow = NodePath(geomNode)
        z = -baselineToTop * self.scale[2] - bgPad
        maxWidth = .1 / self.scale[0]
        shortcutTextMaxWidth = 0
        anyImage = False
        anyArrow = False
        anyShortcut = False
        arrows = []
        shortcutTexts = []
        loadPrcFileData('', 'text-flatten 0')
        for item in items:
            if item:
                t, imgPath, f = item[:3]
                haveSubmenu = type(f) in SEQUENCE_TYPES
                anyArrow |= haveSubmenu
                anyImage |= isinstance(imgPath, bool) or bool(imgPath)
                disabled = not len(f) if haveSubmenu else not callable(f)
                args = item[3:]
                underlinePos = t.find('_')
                t = t.replace('_', '')
                shortcutSepPos = t.find('>')
                if shortcutSepPos > -1:
                    if haveSubmenu:
                        print(
                            "\nA SHORTCUT KEY POINTING TO A SUBMENU IS NON-SENSE, DON'T YOU AGREE ?"
                        )
                    else:
                        shortcutText = NodePath(
                            OnscreenText(
                                parent=self.menu,
                                text=t[shortcutSepPos + 1:],
                                font=self.font,
                                scale=1,
                                fg=(1, 1, 1, 1),
                                align=TextNode.ARight,
                            ))
                        shortcutTextMaxWidth = max(
                            shortcutTextMaxWidth,
                            abs(shortcutText.getTightBounds()[0][0]))
                        anyShortcut = True
                    t = t[:shortcutSepPos]
                else:
                    shortcutText = ''
                EoLcount = t.count('\n')
                arrowZpos = -self.font.getLineHeight() * EoLcount * .5
                if disabled:
                    b = NodePath(
                        OnscreenText(
                            parent=self.menu,
                            text=t,
                            font=self.font,
                            scale=1,
                            fg=textColorDisabled,
                            align=TextNode.ALeft,
                        ))
                    # don't pass the scale and position to OnscreenText constructor,
                    # to maintain correctness between the OnscreenText and DirectButton items
                    # due to the new text generation implementation
                    b.setScale(self.scale)
                    b.setZ(z)
                    maxWidth = max(maxWidth,
                                   b.getTightBounds()[1][0] / self.scale[0])
                    if shortcutText:
                        shortcutText.reparentTo(b)
                        shortcutText.setColor(Vec4(*textColorDisabled), 1)
                        shortcutText.setZ(arrowZpos)
                        shortcutTexts.append(shortcutText)
                else:
                    b = DirectButton(
                        parent=self.menu,
                        text=t,
                        text_font=self.font,
                        scale=self.scale,
                        pos=(0, 0, z),
                        text_fg=textColorReady,
                        # text color when mouse over
                        text2_fg=textColorHover,
                        # text color when pressed
                        text1_fg=textColorHover
                        if haveSubmenu else textColorPress,
                        # framecolor when pressed
                        frameColor=frameColorHover
                        if haveSubmenu else frameColorPress,
                        commandButtons=[DGG.LMB, DGG.RMB],
                        command=(lambda: 0)
                        if haveSubmenu else self.__runCommand,
                        extraArgs=[] if haveSubmenu else [f, args],
                        text_align=TextNode.ALeft,
                        relief=DGG.FLAT,
                        rolloverSound=0,
                        clickSound=0,
                        pressEffect=0)
                    b.stateNodePath[2].setColor(
                        *frameColorHover)  # framecolor when mouse over
                    b.stateNodePath[0].setColor(0, 0, 0,
                                                0)  # framecolor when ready
                    bframe = Vec4(b.node().getFrame())
                    if EoLcount:
                        bframe.setZ(EoLcount * 10)
                        b['frameSize'] = bframe
                    maxWidth = max(maxWidth, bframe[1])
                    if shortcutText:
                        for snpi, col in ((0, textColorReady),
                                          (1, textColorPress),
                                          (2, textColorHover)):
                            sct = shortcutText.copyTo(b.stateNodePath[snpi],
                                                      sort=10)
                            sct.setColor(Vec4(*col), 1)
                            sct.setZ(arrowZpos)
                            shortcutTexts.append(sct)
                        shortcutText.removeNode()
                if isinstance(imgPath, bool):
                    if imgPath:
                        if disabled:
                            fg = textColorDisabled
                        else:
                            fg = textColorReady
                        tick = NodePath(
                            OnscreenText(
                                parent=b,
                                text=u"\u2714",
                                font=self.font,
                                scale=1,
                                fg=fg,
                                align=TextNode.ALeft,
                            ))
                        tick.setX(-2 * imageHalfHeight - leftPad)
                elif imgPath:
                    img = loader.loadTexture(imgPath, okMissing=True)
                    if img is not None:
                        if disabled:
                            if imgPath in PopupMenu.grayImages:
                                img = PopupMenu.grayImages[imgPath]
                            else:
                                pnm = PNMImage()
                                img.store(pnm)
                                pnm.makeGrayscale(.2, .2, .2)
                                img = Texture()
                                img.load(pnm)
                                PopupMenu.grayImages[imgPath] = img
                        img.setMinfilter(Texture.FTLinearMipmapLinear)
                        img.setWrapU(Texture.WMClamp)
                        img.setWrapV(Texture.WMClamp)
                        CM = CardMaker('')
                        CM.setFrame(-2 * imageHalfHeight - leftPad, -leftPad,
                                    itemZcenter - imageHalfHeight,
                                    itemZcenter + imageHalfHeight)
                        imgCard = b.attachNewNode(CM.generate())
                        imgCard.setTexture(img)
                if underlinePos > -1:
                    oneLineText = t[:underlinePos + 1]
                    oneLineText = oneLineText[oneLineText.rfind('\n') + 1:]
                    tn = TextNode('')
                    tn.setFont(self.font)
                    tn.setText(oneLineText)
                    tnp = NodePath(tn.getInternalGeom())
                    underlineXend = tnp.getTightBounds()[1][0]
                    tnp.removeNode()
                    tn.setText(t[underlinePos])
                    tnp = NodePath(tn.getInternalGeom())
                    b3 = tnp.getTightBounds()
                    underlineXstart = underlineXend - (b3[1] - b3[0])[0]
                    tnp.removeNode()
                    underlineZpos = -.7 * baselineToBot - self.font.getLineHeight(
                    ) * t[:underlinePos].count('\n')
                    LSunder = LineSegs()
                    LSunder.setThickness(underscoreThickness)
                    LSunder.moveTo(underlineXstart + texMargin, 0,
                                   underlineZpos)
                    LSunder.drawTo(underlineXend - texMargin, 0, underlineZpos)
                    if disabled:
                        underline = b.attachNewNode(LSunder.create())
                        underline.setColor(Vec4(*textColorDisabled), 1)
                    else:
                        underline = b.stateNodePath[0].attachNewNode(
                            LSunder.create())
                        underline.setColor(Vec4(*textColorReady), 1)
                        underline.copyTo(b.stateNodePath[1], 10).setColor(
                            Vec4(*textColorHover
                                 if haveSubmenu else textColorPress), 1)
                        underline.copyTo(b.stateNodePath[2],
                                         10).setColor(Vec4(*textColorHover), 1)
                        hotkey = t[underlinePos].lower()
                        if hotkey in self.hotkeys:
                            self.hotkeys[hotkey].append(self.numItems)
                        else:
                            self.hotkeys[hotkey] = [self.numItems]
                            self.accept(self.BTprefix + hotkey,
                                        self.__processHotkey, [hotkey])
                            self.accept(self.BTprefix + 'alt-' + hotkey,
                                        self.__processHotkey, [hotkey])
                if haveSubmenu:
                    if disabled:
                        arrow = realArrow.instanceUnderNode(b, '')
                        arrow.setColor(Vec4(*textColorDisabled), 1)
                        arrow.setZ(arrowZpos)
                    else:
                        arrow = realArrow.instanceUnderNode(
                            b.stateNodePath[0], 'r')
                        arrow.setColor(Vec4(*textColorReady), 1)
                        arrow.setZ(arrowZpos)
                        arrPress = realArrow.instanceUnderNode(
                            b.stateNodePath[1], 'p')
                        arrPress.setColor(Vec4(*textColorHover), 1)
                        arrPress.setZ(arrowZpos)
                        arrHover = realArrow.instanceUnderNode(
                            b.stateNodePath[2], 'h')
                        arrHover.setColor(Vec4(*textColorHover), 1)
                        arrHover.setZ(arrowZpos)
                        # weird, if sort order is 0, it's obscured by the frame
                        for a in (arrPress, arrHover):
                            a.reparentTo(a.getParent(), sort=10)
                if not disabled:
                    extraArgs = [self.numItems, f if haveSubmenu else 0]
                    self.accept(DGG.ENTER + b.guiId, self.__hoverOnItem,
                                extraArgs)
                    self.accept(DGG.EXIT + b.guiId, self.__offItem)
                    #~ self.itemCommand.append((None,0) if haveSubmenu else (f,args))
                    self.itemCommand.append((f, args))
                    if self.numItems == 0:
                        self.firstButtonIdx = int(b.guiId[2:])
                    self.numItems += 1
                z -= LH + self.font.getLineHeight() * self.scale[2] * EoLcount
            else:  # SEPARATOR LINE
                z += LH - separatorHalfHeight - baselineToBot * self.scale[2]
                LSseparator.moveTo(0, 0, z)
                LSseparator.drawTo(self.scale[0] * .5, 0, z)
                LSseparator.drawTo(self.scale[0], 0, z)
                z -= separatorHalfHeight + baselineToTop * self.scale[2]
        maxWidth += 7 * arrowHalfHeight * (
            anyArrow or anyShortcut) + .2 + shortcutTextMaxWidth
        arrowXpos = maxWidth - arrowHalfHeight
        realArrow.setX(arrowXpos)
        if anyImage:
            leftPad += 2 * imageHalfHeight + leftPad
        for sct in shortcutTexts:
            sct.setX(maxWidth - 2 * (arrowHalfHeight * anyArrow + .2))
        for c in asList(self.menu.findAllMatches('**/DirectButton*')):
            numLines = c.node().getFrame()[2]
            c.node().setFrame(
                Vec4(
                    -leftPad, maxWidth, -baselineToBot -
                    (numLines * .1 * self.itemHeight if numLines >= 10 else 0),
                    baselineToTop))
        loadPrcFileData('', 'text-flatten 1')

        try:
            minZ = self.menu.getChild(0).getRelativePoint(
                b, Point3(0, 0,
                          b.node().getFrame()[2]))[2]
        except:
            minZ = self.menu.getChild(0).getRelativePoint(
                self.menu, Point3(
                    0, 0,
                    b.getTightBounds()[0][2]))[2] - baselineToBot * .5
        try:
            top = self.menu.getChild(0).node().getFrame()[3]
        except:
            top = self.menu.getChild(0).getZ() + baselineToTop
        l, r, b, t = -leftPad - bgPad / self.scale[
            0], maxWidth + bgPad / self.scale[0], minZ - bgPad / self.scale[
                2], top + bgPad / self.scale[2]
        menuBG = DirectFrame(parent=self.menu.getChild(0),
                             frameSize=(l, r, b, t),
                             frameColor=BGColor,
                             state=DGG.NORMAL,
                             suppressMouse=1)
        menuBorder = self.menu.getChild(0).attachNewNode('border')
        borderVtx = (
            (l, 0, b),
            (l, 0, .5 * (b + t)),
            (l, 0, t),
            (.5 * (l + r), 0, t),
            (r, 0, t),
            (r, 0, .5 * (b + t)),
            (r, 0, b),
            (.5 * (l + r), 0, b),
            (l, 0, b),
        )
        LSborderBG = LineSegs()
        LSborderBG.setThickness(4)
        LSborderBG.setColor(0, 0, 0, .7)
        LSborderBG.moveTo(*(borderVtx[0]))
        for v in borderVtx[1:]:
            LSborderBG.drawTo(*v)
        # fills the gap at corners
        for v in range(0, 7, 2):
            LSborderBG.moveTo(*(borderVtx[v]))
        menuBorder.attachNewNode(LSborderBG.create())
        LSborder = LineSegs()
        LSborder.setThickness(2)
        LSborder.setColor(*BGBorderColor)
        LSborder.moveTo(*(borderVtx[0]))
        for v in borderVtx[1:]:
            LSborder.drawTo(*v)
        menuBorder.attachNewNode(LSborder.create())
        for v in range(1, 8, 2):
            LSborderBG.setVertexColor(v, Vec4(0, 0, 0, .1))
            LSborder.setVertexColor(v, Vec4(.3, .3, .3, .5))
        menuBorderB3 = menuBorder.getTightBounds()
        menuBorderDims = menuBorderB3[1] - menuBorderB3[0]
        menuBG.wrtReparentTo(self.menu, sort=-1)
        self.menu.reparentTo(self.parent)
        x = -menuBorderB3[0][0] * self.scale[0]
        for c in asList(self.menu.getChildren()):
            c.setX(x)
        self.maxWidth = maxWidth = menuBorderDims[0]
        self.height = menuBorderDims[2]
        maxWidthR2D = maxWidth * self.menu.getChild(0).getSx(render2d)
        separatorLines = self.menu.attachNewNode(LSseparator.create(), 10)
        separatorLines.setSx(maxWidth)
        for v in range(1, LSseparator.getNumVertices(), 3):
            LSseparator.setVertexColor(v, Vec4(*separatorColor))
        x = clamp(-.98, .98 - maxWidthR2D, self.mpos[0] - maxWidthR2D * .5)
        minZ = (-.98 if self.minZ is None else self.minZ)
        z = clamp(
            minZ +
            menuBorderDims[2] * self.scale[2] * self.parent.getSz(render2d),
            .98, self.mpos[1] if useMouseZ else -1000)
        self.menu.setPos(render2d, x, 0, z)
        self.menu.setTransparency(1)

        self.origBTprefix = self.BT.getPrefix()
        self.BT.setPrefix(self.BTprefix)
        self.accept(self.BTprefix + 'escape', self.destroy)
        for e in ('mouse1', 'mouse3'):
            self.accept(self.BTprefix + e, self.destroy, [True])
        self.accept(self.BTprefix + 'arrow_down', self.__nextItem)
        self.accept(self.BTprefix + 'arrow_down-repeat', self.__nextItem)
        self.accept(self.BTprefix + 'arrow_up', self.__prevItem)
        self.accept(self.BTprefix + 'arrow_up-repeat', self.__prevItem)
        self.accept(self.BTprefix + 'enter', self.__runSelItemCommand)
        self.accept(self.BTprefix + 'space', self.__runSelItemCommand)
Exemple #4
0
class TQGraphicsNodePath:
    """ Anything that fundamentally is a only a graphics object in this engine should have these properties.

    Kwargs:
        TQGraphicsNodePath_creation_parent_node : this is assigned in the constructor, and after makeObject and the return of
        the p3d Nodepath, each TQGraphicsNodePath object has to call set_p3d_node """

    def __init__(self, **kwargs):
        """ """
        self.TQGraphicsNodePath_creation_parent_node = None
        self.p3d_nodepath = NodePath("empty")
        self.p3d_nodepath_changed_post_init_p = False
        self.p3d_parent_nodepath = NodePath("empty")

        self.node_p3d = None

        self.p3d_nodepath.reparentTo(self.p3d_parent_nodepath)

        self.apply_kwargs(**kwargs)

    def apply_kwargs(self, **kwargs):
        """ """
        if 'color' in kwargs:
            TQGraphicsNodePath.setColor(self, kwargs.get('color'))
        else:
            TQGraphicsNodePath.setColor(self, Vec4(1., 1., 1., 1.))

    def attach_to_render(self):
        """ """
        # assert self.p3d_nodepath
        # self.p3d_nodepath.reparentTo(render)
        assert self.p3d_nodepath
        self.reparentTo(engine.tq_graphics_basics.tq_render)

    def attach_to_aspect2d(self):
        """ """
        # assert self.p3d_nodepath
        # self.p3d_nodepath.reparentTo(aspect2d)
        assert self.p3d_nodepath
        self.reparentTo(engine.tq_graphics_basics.tq_aspect2d)

    def _set_p3d_nodepath_plain_post_init(p3d_nodepath):
        """ """
        self.p3d_nodepath = p3d_nodepath
        self.p3d_nodepath_changed_post_init_p = True

    @staticmethod
    def from_p3d_nodepath(p3d_nodepath, **tq_graphics_nodepath_kwargs):
        """ factory """
        go = TQGraphicsNodePath(**tq_graphics_nodepath_kwargs)
        go.set_p3d_nodepath(p3d_nodepath)
        return go

    def set_parent_node_for_nodepath_creation(
            self, TQGraphicsNodePath_creation_parent_node):
        """ when calling attachNewNode_p3d, a new node pat is generated.
        E.g.: To attach a line to render (3d world) is different than attaching
        it to aspect2d (2d GUI plane), since the aspect2d children are not directly
        affected by camera movements
        Args:
        - TQGraphicsNodePath_creation_parent_node : the NodePath to attach the TQGraphicsNodePath to
                                          (e.g. render or aspect2d in p3d)
        """

        # self.set_parent_node_for_nodepath_creation(self.TQGraphicsNodePath_creation_parent_node)
        self.TQGraphicsNodePath_creation_parent_node = TQGraphicsNodePath_creation_parent_node

    def set_p3d_nodepath(self, p3d_nodepath, remove_old_nodepath=True):
        """ """
        self.p3d_nodepath = p3d_nodepath

    def get_p3d_nodepath(self):
        """ """
        return self.p3d_nodepath

    def set_render_above_all(self, p):
        """ set render order to be such that it renders normally (false), or above all (true)
        Args:
            p: True or False to enable or disable the 'above all' rendering mode """

        try:
            if p == True:
                self.p3d_nodepath.setBin("fixed", 0)
                self.p3d_nodepath.setDepthTest(False)
                self.p3d_nodepath.setDepthWrite(False)
            else:
                self.p3d_nodepath.setBin("default", 0)
                self.p3d_nodepath.setDepthTest(True)
                self.p3d_nodepath.setDepthWrite(True)
        except NameError:       # if p3d_nodepath is not yet defined
            print("NameError in set_render_above_all()")

    def remove(self):
        """ """
        self.p3d_nodepath.removeNode()

    def setPos(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setPos(*args, **kwargs)

    def getPos(self):
        """ """
        return self.p3d_nodepath.getPos()

    def setScale(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setScale(*args, **kwargs)

    def getScale(self):
        """ """
        return self.p3d_nodepath.getScale()

    def setMat(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setMat(*args, **kwargs)

    def setMat_normal(self, mat4x4_normal_np):
        """ normal convention (numpy array), i.e. convert to forrowvecs convention for p3d setMat call """
        return self.p3d_nodepath.setMat(math_utils.to_forrowvecs(mat4x4_normal_np))


    def getMat(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.getMat(*args, **kwargs)

    def getMat_normal(self, *args, **kwargs):
        """ """
        return math_utils.from_forrowvecs(self.p3d_nodepath.getMat(*args, **kwargs))

    def setTexture(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setTexture(*args, **kwargs)

    def setColor(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setColor(*args, **kwargs)

    def setTwoSided(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setTwoSided(*args, **kwargs)

    def setRenderModeWireframe(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setRenderModeWireframe(*args, **kwargs)

    def reparentTo(self, *args, **kwargs):
        """ """
        new_args = list(args)
        new_args[0] = new_args[0].p3d_nodepath
        return self.p3d_nodepath.reparentTo(*new_args, **kwargs)

    def reparentTo_p3d(self, *args, **kwargs):
        """ input a p3d nodepath directly """
        # new_args = list(args)
        # new_args[0] = new_args[0].p3d_nodepath
        return self.p3d_nodepath.reparentTo(*args, **kwargs)

    def get_node_p3d(self):
        """ """
        # return self.p3d_nodepath.node()
        return self.node_p3d

    def set_node_p3d(self, node_p3d):
        """ not available in p3d NodePath class """
        self.node_p3d = node_p3d

    def setRenderModeFilled(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setRenderModeFilled(*args, **kwargs)

    def setLightOff(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setLightOff(*args, **kwargs)

    def show(self):
        """ """
        return self.p3d_nodepath.show()

    def hide(self):
        """ """
        return self.p3d_nodepath.hide()

    def setRenderModeThickness(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setRenderModeThickness(*args, **kwargs)

    def removeNode(self):
        """ """
        return self.p3d_nodepath.removeNode()

    def setHpr(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setHpr(*args, **kwargs)

    def showBounds(self):
        """ """
        return self.p3d_nodepath.showBounds()

    def setCollideMask(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setCollideMask(*args, **kwargs)

    def getParent_p3d(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.getParent(*args, **kwargs)

    def wrtReparentTo(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.wrtReparentTo(*args, **kwargs)

    def setBin(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setBin(*args, **kwargs)

    def setDepthTest(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setDepthTest(*args, **kwargs)

    def setDepthWrite(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setDepthWrite(*args, **kwargs)

    def get_children_p3d(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.get_children(*args, **kwargs)

    def attachNewNode_p3d(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.attachNewNode(*args, **kwargs)

    def lookAt(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.lookAt(*args, **kwargs)

    def setLight(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setLight(*args, **kwargs)

    def setAntialias(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setAntialias(*args, **kwargs)

    def getRelativeVector(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.getRelativeVector(*args, **kwargs)

    def setTransparency(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setTransparency(*args, **kwargs)

    def getHpr(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.getHpr(*args, **kwargs)

    def setHpr(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setHpr(*args, **kwargs)

    def getTightBounds(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.getTightBounds(*args, **kwargs)

    def showTightBounds(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.showTightBounds(*args, **kwargs)