PGItemNotify interface, PGScrollFrame, and more general PGSliderBar

This commit is contained in:
David Rose 2005-08-27 00:15:54 +00:00
parent 80efd7c253
commit 1e81e656ab
88 changed files with 5429 additions and 992 deletions

View File

@ -28,6 +28,7 @@ class DirectButton(DirectFrame):
('pgFunc', PGButton, None),
('numStates', 4, None),
('state', NORMAL, None),
('relief', RAISED, None),
('invertedFrames', (1,), None),
# Command to be called on button click
('command', None, None),

View File

@ -17,6 +17,8 @@ from DirectLabel import *
from DirectScrolledList import *
from DirectDialog import *
from DirectWaitBar import *
from DirectSliderBar import *
from DirectSlider import *
from DirectScrollBar import *
from DirectScrolledFrame import *
from DirectCheckButton import *
from DirectOptionMenu import *

View File

@ -685,6 +685,7 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
('frameSize', None, self.setFrameSize),
('frameColor', (.8,.8,.8,1), self.setFrameColor),
('frameTexture', None, self.setFrameTexture),
('frameVisibleScale', (1,1), self.setFrameVisibleScale),
('pad', (0,0), self.resetFrameSize),
# Override button id (beware! your name may not be unique!)
('guiId', None, INITOPT),
@ -866,6 +867,8 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
# Use user specified bounds
self.bounds = self['frameSize']
#print "%s bounds = %s" % (self.getName(),self.bounds)
bw = (0,0)
else:
if fClearFrame and (frameType != PGFrameStyle.TNone):
self.frameStyle[0].setType(PGFrameStyle.TNone)
@ -878,11 +881,13 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
if (frameType != PGFrameStyle.TNone):
self.frameStyle[0].setType(frameType)
self.guiItem.setFrameStyle(0, self.frameStyle[0])
if ((frameType != PGFrameStyle.TNone) and
(frameType != PGFrameStyle.TFlat)):
bw = self['borderWidth']
else:
bw = (0,0)
if ((frameType != PGFrameStyle.TNone) and
(frameType != PGFrameStyle.TFlat)):
bw = self['borderWidth']
else:
bw = (0,0)
# Set frame to new dimensions
self.guiItem.setFrame(
self.bounds[0] - bw[0],
@ -974,6 +979,12 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
self.frameStyle[i].setTexture(texture)
self.updateFrameStyle()
def setFrameVisibleScale(self):
scale = self['frameVisibleScale']
for i in range(self['numStates']):
self.frameStyle[i].setVisibleScale(scale[0], scale[1])
self.updateFrameStyle()
def setBorderWidth(self):
width = self['borderWidth']
for i in range(self['numStates']):

View File

@ -29,6 +29,10 @@ FrameStyleDict = {'flat' : FLAT, 'raised' : RAISED, 'sunken': SUNKEN,
'groove' : GROOVE, 'ridge' : RIDGE
}
# Orientation of DirectSlider and DirectScrollBar
HORIZONTAL = 'horizontal'
VERTICAL = 'vertical'
# Dialog button values
DIALOG_NO = 0
DIALOG_OK = DIALOG_YES = DIALOG_RETRY = 1
@ -55,6 +59,8 @@ OVERFLOW = PGEntry.getOverflowPrefix()
ACCEPT = PGEntry.getAcceptPrefix() + KeyboardButton.enter().getName() + '-'
TYPE = PGEntry.getTypePrefix()
ERASE = PGEntry.getErasePrefix()
# For DirectSlider and DirectScrollBar widgets
ADJUST = PGSliderBar.getAdjustPrefix()
# For setting the sorting order of a widget's visible components

View File

@ -0,0 +1,123 @@
from DirectFrame import *
from DirectButton import *
from DirectGuiBase import _OPT_VALUE
"""
import DirectScrollBar
d = DirectScrollBar(borderWidth=(0,0))
"""
class DirectScrollBar(DirectFrame):
"""
DirectScrollBar -- a widget which represents a scroll bar the user can
use for paging through a large document or panel.
"""
def __init__(self, parent = None, **kw):
optiondefs = (
# Define type of DirectGuiWidget
('pgFunc', PGSliderBar, None),
('state', NORMAL, None),
('frameColor', (0.6,0.6,0.6,1), None),
('range', (0, 1), self.setRange),
('value', 0, self.__setValue),
('pageSize', 0.1, self.setPageSize),
('orientation', HORIZONTAL, self.setOrientation),
('manageButtons', 1, self.setManageButtons),
('resizeThumb', 1, self.setResizeThumb),
# Function to be called repeatedly as the bar is scrolled
('command', None, None),
('extraArgs', [], None),
)
if kw.get('orientation') == VERTICAL:
# These are the default options for a vertical layout.
optiondefs += (
('frameSize', (-0.04,0.04,-0.5,0.5), None),
)
else:
# These are the default options for a horizontal layout.
optiondefs += (
('frameSize', (-0.5,0.5,-0.04,0.04), None),
)
# Merge keyword options with default options
self.defineoptions(kw, optiondefs)
# Initialize superclasses
DirectFrame.__init__(self, parent)
self.thumb = self.createcomponent(
"thumb", (), None,
DirectButton, (self,),
borderWidth = self['borderWidth'])
self.incButton = self.createcomponent(
"incButton", (), None,
DirectButton, (self,),
borderWidth = self['borderWidth'])
self.decButton = self.createcomponent(
"decButton", (), None,
DirectButton, (self,),
borderWidth = self['borderWidth'])
self.guiItem.setThumbButton(self.thumb.guiItem)
self.guiItem.setLeftButton(self.decButton.guiItem)
self.guiItem.setRightButton(self.incButton.guiItem)
# Bind command function
self.bind(ADJUST, self.commandFunc)
# Call option initialization functions
self.initialiseoptions(DirectScrollBar)
def setRange(self):
# Try to preserve the value across a setRange call.
v = self['value']
r = self['range']
self.guiItem.setRange(r[0], r[1])
self['value'] = v
def __setValue(self):
# This is the internal function that is called when
# self['value'] is directly assigned.
self.guiItem.setValue(self['value'])
def setValue(self, value):
# This is the public function that is meant to be called by a
# user that doesn't like to use (or doesn't understand) the
# preferred interface of self['value'].
self['value'] = value
def getValue(self):
return self.guiItem.getValue()
def getRatio(self):
return self.guiItem.getRatio()
def setPageSize(self):
self.guiItem.setPageSize(self['pageSize'])
def setOrientation(self):
if self['orientation'] == HORIZONTAL:
self.guiItem.setAxis(Vec3(1, 0, 0))
else:
self.guiItem.setAxis(Vec3(0, 0, -1))
def setManageButtons(self):
self.guiItem.setManagePieces(self['manageButtons'])
def setResizeThumb(self):
self.guiItem.setResizeThumb(self['resizeThumb'])
def destroy(self):
DirectFrame.destroy(self)
def commandFunc(self):
# Store the updated value in self['value']
self._optionInfo['value'][_OPT_VALUE] = self.guiItem.getValue()
if self['command']:
apply(self['command'], self['extraArgs'])

View File

@ -0,0 +1,95 @@
from DirectFrame import *
from DirectScrollBar import *
"""
import DirectScrolledFrame
d = DirectScrolledFrame(borderWidth=(0,0))
"""
class DirectScrolledFrame(DirectFrame):
"""
DirectScrolledFrame -- a special frame that uses DirectScrollBar to
implement a small window (the frameSize) into a potentially much
larger virtual canvas (the canvasSize, scrolledFrame.getCanvas()).
Unless specified otherwise, scroll bars are automatically created
and managed as needed, based on the relative sizes od the
frameSize and the canvasSize. You can also set manageScrollBars =
0 and explicitly position and hide or show the scroll bars
yourself.
"""
def __init__(self, parent = None, **kw):
optiondefs = (
# Define type of DirectGuiWidget
('pgFunc', PGScrollFrame, None),
('frameSize', (-0.5,0.5,-0.5,0.5), None),
('canvasSize', (-1,1,-1,1), self.setCanvasSize),
('manageScrollBars', 1, self.setManageScrollBars),
('autoHideScrollBars', 1, self.setAutoHideScrollBars),
('scrollBarWidth', 0.08, None),
)
# Merge keyword options with default options
self.defineoptions(kw, optiondefs)
# Initialize superclasses
DirectFrame.__init__(self, parent)
# The scrollBarWidth parameter is just used at scroll bar
# construction time, and supplies a default frame. It does
# not override an explicit frame specified on the scroll bar.
# If you want to change the frame width after construction,
# you must specify their frameSize tuples explicitly.
w = self['scrollBarWidth']
self.verticalScroll = self.createcomponent(
"verticalScroll", (), None,
DirectScrollBar, (self,),
borderWidth = self['borderWidth'],
frameSize = (-w / 2.0, w / 2.0, -1, 1),
orientation = VERTICAL)
self.horizontalScroll = self.createcomponent(
"horizontalScroll", (), None,
DirectScrollBar, (self,),
borderWidth = self['borderWidth'],
frameSize = (-1, 1, -w / 2.0, w / 2.0),
orientation = HORIZONTAL)
self.guiItem.setVerticalSlider(self.verticalScroll.guiItem)
self.guiItem.setHorizontalSlider(self.horizontalScroll.guiItem)
self.canvas = NodePath(self.guiItem.getCanvasNode())
# Call option initialization functions
self.initialiseoptions(DirectScrolledFrame)
def setCanvasSize(self):
f = self['canvasSize']
self.guiItem.setVirtualFrame(f[0], f[1], f[2], f[3])
def getCanvas(self):
return self.canvas
def setManageScrollBars(self):
self.guiItem.setManagePieces(self['manageScrollBars'])
def setAutoHideScrollBars(self):
self.guiItem.setAutoHide(self['autoHideScrollBars'])
def destroy(self):
DirectFrame.destroy(self)
def commandFunc(self):
if self['command']:
apply(self['command'], self['extraArgs'])
def destroy(self):
# Destroy children of the canvas
for child in self.canvas.getChildrenAsList():
childGui = self.guiDict.get(child.getName())
if childGui: childGui.destroy()
DirectFrame.DirectFrame.destroy(self)

View File

@ -0,0 +1,113 @@
from DirectFrame import *
from DirectButton import *
from DirectGuiBase import _OPT_VALUE
"""
import DirectSlider
d = DirectSlider(borderWidth=(0,0))
"""
class DirectSlider(DirectFrame):
"""
DirectSlider -- a widget which represents a slider that the
user can pull left and right to represent a continuous value.
"""
def __init__(self, parent = None, **kw):
optiondefs = (
# Define type of DirectGuiWidget
('pgFunc', PGSliderBar, None),
('state', NORMAL, None),
('frameColor', (0.6,0.6,0.6,1), None),
('range', (0, 1), self.setRange),
('value', 0, self.__setValue),
('pageSize', 0.1, self.setPageSize),
('orientation', HORIZONTAL, self.setOrientation),
# Function to be called repeatedly as slider is moved
('command', None, None),
('extraArgs', [], None),
)
if kw.get('orientation') == VERTICAL:
# These are the default options for a vertical layout.
optiondefs += (
('frameSize', (-0.08,0.08,-1,1), None),
('frameVisibleScale', (0.25,1), None),
)
else:
# These are the default options for a horizontal layout.
optiondefs += (
('frameSize', (-1,1,-0.08,0.08), None),
('frameVisibleScale', (1,0.25), None),
)
# Merge keyword options with default options
self.defineoptions(kw, optiondefs)
# Initialize superclasses
DirectFrame.__init__(self, parent)
self.thumb = self.createcomponent("thumb", (), None,
DirectButton, (self,),
borderWidth = self['borderWidth'])
if self.thumb['frameSize'] == None and \
self.thumb.bounds == [0.0, 0.0, 0.0, 0.0]:
# Compute a default frameSize for the thumb.
f = self['frameSize']
if self['orientation'] == HORIZONTAL:
self.thumb['frameSize'] = (f[0]*0.05,f[1]*0.05,f[2],f[3])
else:
self.thumb['frameSize'] = (f[0],f[1],f[2]*0.05,f[3]*0.05)
self.guiItem.setThumbButton(self.thumb.guiItem)
# Bind command function
self.bind(ADJUST, self.commandFunc)
# Call option initialization functions
self.initialiseoptions(DirectSlider)
def setRange(self):
# Try to preserve the value across a setRange call.
v = self['value']
r = self['range']
self.guiItem.setRange(r[0], r[1])
self['value'] = v
def __setValue(self):
# This is the internal function that is called when
# self['value'] is directly assigned.
self.guiItem.setValue(self['value'])
def setValue(self, value):
# This is the public function that is meant to be called by a
# user that doesn't like to use (or doesn't understand) the
# preferred interface of self['value'].
self['value'] = value
def getValue(self):
return self.guiItem.getValue()
def getRatio(self):
return self.guiItem.getRatio()
def setPageSize(self):
self.guiItem.setPageSize(self['pageSize'])
def setOrientation(self):
if self['orientation'] == HORIZONTAL:
self.guiItem.setAxis(Vec3(1, 0, 0))
else:
self.guiItem.setAxis(Vec3(0, 0, 1))
def destroy(self):
DirectFrame.destroy(self)
def commandFunc(self):
# Store the updated value in self['value']
self._optionInfo['value'][_OPT_VALUE] = self.guiItem.getValue()
if self['command']:
apply(self['command'], self['extraArgs'])

View File

@ -1,147 +0,0 @@
from DirectButton import *
from DirectFrame import *
"""
import DirectSliderBar
d = DirectSliderBar(borderWidth=(0,0))
"""
class DirectSliderButton(DirectButton):
def __init__(self, parent = None, **kw):
optiondefs = (
# Define type of DirectGuiWidget
('pgFunc', PGSliderButton, None),
)
# Merge keyword options with default options
self.defineoptions(kw, optiondefs)
# Initialize superclasses
DirectButton.__init__(self,parent)
# Call option initialization functions
self.initialiseoptions(DirectSliderButton)
class DirectSliderBar(DirectFrame):
"""
DirectEntry(parent) - Create a DirectGuiWidget which responds
to keyboard buttons
"""
def __init__(self, parent = None, **kw):
# Inherits from DirectFrame
# A Direct Frame can have:
# - A background texture (pass in path to image, or Texture Card)
# - A midground geometry item (pass in geometry)
# - A foreground text Node (pass in text string or Onscreen Text)
optiondefs = (
# Define type of DirectGuiWidget
('pgFunc', PGSliderBar, None),
('width', 10, None),
('height', 1, None),
('button', None, None),
('sliderOnly', 0, self.setSliderOnly),
('negativeMapping',0, self.setNegativeMapping),
('range', 100, self.setRange),
('value', 0, self.setValue),
('barBorderWidth', (0,0), self.setBarBorderWidth),
('barColor', (1,0,0,1), self.setBarColor),
('barRelief', FLAT, self.setBarRelief),
('active', 0, self.setActive),
('sortOrder', NO_FADE_SORT_INDEX, None),
# Command to be called on button movement
('command', None, None),
('extraArgs', [], None),
)
if kw.has_key('text'):
textoptiondefs = (
('text_pos', (0,-0.025), None),
('text_scale', 0.1, None),
('text_fg', (1,1,1,1), None),
('text_shadow', (0,0,0,1), None),
('text_align', TextNode.ALeft, None)
)
else:
textoptiondefs = ()
# Merge keyword options with default options
self.defineoptions(kw, optiondefs + textoptiondefs)
# Initialize superclasses
DirectFrame.__init__(self, parent)
self.barStyle = PGFrameStyle()
# Call option initialization functions
self.initialiseoptions(DirectSliderBar)
if (self['button'] != None):
self.guiItem.setSliderButton(self['button'], self['button'].guiItem)
if (self['image'] != None):
self.guiItem.setScale(self['image_scale'][0])
self.guiItem.setup(self.getWidth(), self.getHeight(), self['range'])
else:
#figure out what is happening?????
#self.guiItem.setState(0)
#self.guiItem.clearStateDef(0)
self.guiItem.setFrame(-3.0, 3.0, -0.125, 0.125)
self.barStyle.setWidth(0.05, 0.05)
self.barStyle.setColor(0.6,0.6,0.6,1)
self.barStyle.setType(PGFrameStyle.TBevelIn)
self.guiItem.setFrameStyle(0, self.barStyle)
self.guiItem.setScale(2)
self.guiItem.setup(6, 0.5, self['range'])
self.guiItem.setValue(self['value'])
if (self['scale'] != None):
self.setScale(self['scale'])
else:
self.setScale(0.1)
self.guiItem.setActive(1)
#self.barStyle.setColor(0.8,0.8,0.8,1)
#self.barStyle.setType(PGFrameStyle.TBevelOut)
#self.updateBarStyle()
if (self['command'] != None):
# Attach command function to slider button movement
self.bind('updated-slider-', self.commandFunc)
def destroy(self):
del self.barStyle
DirectFrame.destroy(self)
def setSliderOnly(self):
self.guiItem.setSliderOnly(self['sliderOnly'])
def setNegativeMapping(self):
self.guiItem.setNegativeMapping(self['negativeMapping'])
def setRange(self):
self.guiItem.setRange(self['range'])
def setValue(self):
self.guiItem.setValue(self['value'])
def getPercent(self):
return self.guiItem.getPercent()
def updateBarStyle(self):
if not self.fInit:
self.guiItem.setBarStyle(self.barStyle)
def setBarRelief(self):
self.barStyle.setType(self['barRelief'])
self.updateBarStyle()
def setBarBorderWidth(self):
self.barStyle.setWidth(*self['barBorderWidth'])
self.updateBarStyle()
def setBarColor(self):
color = self['barColor']
self.barStyle.setColor(color[0], color[1], color[2], color[3])
self.updateBarStyle()
def setActive(self):
self.guiItem.setActive(self['active'])
def commandFunc(self):
if self['command']:
# Pass any extra args to command
apply(self['command'], self['extraArgs'])

View File

@ -230,10 +230,6 @@ write(ostream &out, int indent_level) const {
////////////////////////////////////////////////////////////////////
void CollisionEntry::
check_clip_planes() {
const RenderAttrib *cpa_attrib =
_into_node_path.get_net_state()->get_attrib(ClipPlaneAttrib::get_class_type());
if (cpa_attrib != (const RenderAttrib *)NULL) {
_into_clip_planes = DCAST(ClipPlaneAttrib, cpa_attrib);
}
_into_clip_planes = _into_node_path.get_net_state()->get_clip_plane();
_flags |= F_checked_clip_planes;
}

View File

@ -32,7 +32,7 @@
#include "datagramIterator.h"
#include "bamReader.h"
#include "bamWriter.h"
#include "omniBoundingVolume.h"
#include "boundingPlane.h"
#include "geom.h"
#include "geomTrifans.h"
#include "geomLinestrips.h"
@ -94,11 +94,10 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
BoundingVolume *CollisionPlane::
recompute_bound() {
// Planes always have an infinite bounding volume.
BoundedObject::recompute_bound();
// Less than ideal: we throw away whatever we just allocated in
// BoundedObject.
return set_bound_ptr(new OmniBoundingVolume);
return set_bound_ptr(new BoundingPlane(_plane));
}
////////////////////////////////////////////////////////////////////

View File

@ -279,9 +279,8 @@ get_collision_origin() const {
PT(PandaNode) CollisionPolygon::
get_viz(const CullTraverser *trav, const CullTraverserData &data,
bool bounds_only) const {
const RenderAttrib *cpa_attrib =
data._state->get_attrib(ClipPlaneAttrib::get_class_type());
if (cpa_attrib == (const RenderAttrib *)NULL) {
const ClipPlaneAttrib *cpa = data._state->get_clip_plane();
if (cpa == (const ClipPlaneAttrib *)NULL) {
// Fortunately, the polygon is not clipped. This is the normal,
// easy case.
return CollisionSolid::get_viz(trav, data, bounds_only);
@ -289,7 +288,7 @@ get_viz(const CullTraverser *trav, const CullTraverserData &data,
if (collide_cat.is_debug()) {
collide_cat.debug()
<< "drawing polygon with clip plane " << *cpa_attrib << "\n";
<< "drawing polygon with clip plane " << *cpa << "\n";
}
// The polygon is clipped. We need to render it clipped. We could
@ -298,7 +297,6 @@ get_viz(const CullTraverser *trav, const CullTraverserData &data,
// and clip it by hand instead, just to prove that our clipping
// algorithm works properly. This does require some more dynamic
// work.
const ClipPlaneAttrib *cpa = DCAST(ClipPlaneAttrib, cpa_attrib);
Points new_points;
if (apply_clip_plane(new_points, cpa, data.get_net_transform(trav))) {
// All points are behind the clip plane; just draw the original
@ -1093,15 +1091,21 @@ clip_polygon(CollisionPolygon::Points &new_points,
const LPoint2f &this_point = (*pi)._p;
bool this_is_in = !is_right(this_point - from2d, delta2d);
if (this_is_in != last_is_in) {
// There appears to be a compiler bug in gcc 4.0: we need to
// extract this comparison outside of the if statement.
bool crossed_over = (this_is_in != last_is_in);
if (crossed_over) {
// We have just crossed over the clipping line. Find the point
// of intersection.
LVector2f d = this_point - last_point;
float t = -(a * last_point[0] + b * last_point[1] + c) / (a * d[0] + b * d[1]);
LPoint2f p = last_point + t * d;
float denom = (a * d[0] + b * d[1]);
if (denom != 0.0) {
float t = -(a * last_point[0] + b * last_point[1] + c) / denom;
LPoint2f p = last_point + t * d;
new_points.push_back(PointDef(p[0], p[1]));
last_is_in = this_is_in;
new_points.push_back(PointDef(p[0], p[1]));
last_is_in = this_is_in;
}
}
if (this_is_in) {

View File

@ -34,6 +34,7 @@
#include "depthOffsetAttrib.h"
#include "colorScaleAttrib.h"
#include "transparencyAttrib.h"
#include "clipPlaneAttrib.h"
#ifdef DO_COLLISION_RECORDING
@ -137,7 +138,8 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
xform_data._modelview_transform = trav->get_world_transform();
xform_data.apply_transform_and_state(trav, net_transform,
RenderState::make_empty(),
RenderEffects::make_empty());
RenderEffects::make_empty(),
ClipPlaneAttrib::make());
// Draw all the collision solids.
Solids::const_iterator si;

View File

@ -12,7 +12,9 @@
#define SOURCES \
boundingHexahedron.I boundingHexahedron.h boundingLine.I \
boundingLine.h boundingSphere.I boundingSphere.h \
boundingLine.h \
boundingPlane.I boundingPlane.h \
boundingSphere.I boundingSphere.h \
boundingVolume.I boundingVolume.h config_mathutil.h \
fftCompressor.h finiteBoundingVolume.h frustum.h \
frustum_src.I frustum_src.h geometricBoundingVolume.I \
@ -24,7 +26,9 @@
plane_src.h rotate_to.h rotate_to_src.cxx
#define INCLUDED_SOURCES \
boundingHexahedron.cxx boundingLine.cxx boundingSphere.cxx \
boundingHexahedron.cxx boundingLine.cxx \
boundingPlane.cxx \
boundingSphere.cxx \
boundingVolume.cxx config_mathutil.cxx fftCompressor.cxx \
finiteBoundingVolume.cxx geometricBoundingVolume.cxx \
look_at.cxx \
@ -33,7 +37,9 @@
#define INSTALL_HEADERS \
boundingHexahedron.I boundingHexahedron.h boundingLine.I \
boundingLine.h boundingSphere.I boundingSphere.h boundingVolume.I \
boundingLine.h \
boundingPlane.I boundingPlane.h \
boundingSphere.I boundingSphere.h boundingVolume.I \
boundingVolume.h config_mathutil.h fftCompressor.h \
finiteBoundingVolume.h frustum.h frustum_src.I frustum_src.h \
geometricBoundingVolume.I geometricBoundingVolume.h look_at.h \

View File

@ -1,5 +1,5 @@
// Filename: pgButton.I
// Created by: masad (21Oct04)
// Filename: boundingPlane.I
// Created by: drose (19Aug05)
//
////////////////////////////////////////////////////////////////////
//
@ -16,32 +16,37 @@
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: PGSliderButton::is_drag_n_drop
// Function: BoundingPlane::Default Constructor
// Access: Published
// Description: Returns if this button is drag_n_drop kind
// Description: Constructs an empty "plane" that has no
// intersections.
////////////////////////////////////////////////////////////////////
INLINE bool PGSliderButton::
is_drag_n_drop() {
return _drag_n_drop;
INLINE_MATHUTIL BoundingPlane::
BoundingPlane() {
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderButton::set_drag_n_drop
// Function: BoundingPlane::Constructor
// Access: Published
// Description: Makes this button a drag_n_drop kind
// Description:
////////////////////////////////////////////////////////////////////
INLINE void PGSliderButton::
set_drag_n_drop(bool value) {
_drag_n_drop = value;
INLINE_MATHUTIL BoundingPlane::
BoundingPlane(const Planef &plane) :
_plane(plane)
{
_flags = 0;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderButton::set_slider_bar
// Function: BoundingPlane::get_plane
// Access: Published
// Description: Store a copy of the controlling slider bar item
// Description:
////////////////////////////////////////////////////////////////////
INLINE void PGSliderButton::
set_slider_bar(PGItem *item) {
_slider_bar = item;
INLINE_MATHUTIL const Planef &BoundingPlane::
get_plane() const {
nassertr(!is_empty(), _plane);
nassertr(!is_infinite(), _plane);
return _plane;
}

View File

@ -0,0 +1,153 @@
// Filename: boundingPlane.cxx
// Created by: drose (19Aug05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "boundingPlane.h"
#include "boundingSphere.h"
#include "config_mathutil.h"
TypeHandle BoundingPlane::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: BoundingPlane::make_copy
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
BoundingVolume *BoundingPlane::
make_copy() const {
return new BoundingPlane(*this);
}
////////////////////////////////////////////////////////////////////
// Function: BoundingPlane::get_approx_center
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
LPoint3f BoundingPlane::
get_approx_center() const {
nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
nassertr(!is_infinite(), LPoint3f(0.0f, 0.0f, 0.0f));
return _plane.get_point();
}
////////////////////////////////////////////////////////////////////
// Function: BoundingPlane::xform
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void BoundingPlane::
xform(const LMatrix4f &mat) {
nassertv(!mat.is_nan());
if (!is_empty() && !is_infinite()) {
_plane.xform(mat);
}
}
////////////////////////////////////////////////////////////////////
// Function: BoundingPlane::output
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void BoundingPlane::
output(ostream &out) const {
if (is_empty()) {
out << "bplane, empty";
} else if (is_infinite()) {
out << "bplane, infinite";
} else {
out << "bplane: " << _plane;
}
}
////////////////////////////////////////////////////////////////////
// Function: BoundingPlane::extend_other
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
bool BoundingPlane::
extend_other(BoundingVolume *other) const {
return other->extend_by_plane(this);
}
////////////////////////////////////////////////////////////////////
// Function: BoundingPlane::around_other
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
bool BoundingPlane::
around_other(BoundingVolume *other,
const BoundingVolume **first,
const BoundingVolume **last) const {
return other->around_planes(first, last);
}
////////////////////////////////////////////////////////////////////
// Function: BoundingPlane::contains_other
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
int BoundingPlane::
contains_other(const BoundingVolume *other) const {
return other->contains_plane(this);
}
////////////////////////////////////////////////////////////////////
// Function: BoundingPlane::extend_by_plane
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
bool BoundingPlane::
extend_by_plane(const BoundingPlane *plane) {
nassertr(!plane->is_empty() && !plane->is_infinite(), false);
nassertr(!is_infinite(), false);
if (is_empty()) {
_plane = plane->get_plane();
_flags = 0;
} else {
_flags = F_infinite;
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: BoundingPlane::contains_sphere
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
int BoundingPlane::
contains_sphere(const BoundingSphere *sphere) const {
nassertr(!is_empty() && !is_infinite(), 0);
nassertr(!sphere->is_empty() && !sphere->is_infinite(), 0);
float r = sphere->get_radius();
float d = _plane.dist_to_plane(sphere->get_center());
if (d <= -r) {
// The sphere is completely behind the plane.
return IF_all | IF_possible | IF_some;
} else if (d <= r) {
// The sphere is intersecting with the plane itself.
return IF_possible | IF_some;
} else {
// The sphere is completely in front of the plane.
return IF_no_intersection;
}
}

View File

@ -0,0 +1,86 @@
// Filename: boundingPlane.h
// Created by: drose (19Aug05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef BOUNDINGPLANE_H
#define BOUNDINGPLANE_H
#include "pandabase.h"
#include "geometricBoundingVolume.h"
#include "plane.h"
////////////////////////////////////////////////////////////////////
// Class : BoundingPlane
// Description : This funny bounding volume is an infinite plane that
// divides space into two regions: the part behind the
// normal, which is "inside" the bounding volume, and
// the part in front of the normal, which is "outside"
// the bounding volume.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA BoundingPlane : public GeometricBoundingVolume {
PUBLISHED:
INLINE_MATHUTIL BoundingPlane();
INLINE_MATHUTIL BoundingPlane(const Planef &plane);
public:
virtual BoundingVolume *make_copy() const;
virtual LPoint3f get_approx_center() const;
virtual void xform(const LMatrix4f &mat);
virtual void output(ostream &out) const;
PUBLISHED:
INLINE_MATHUTIL const Planef &get_plane() const;
protected:
virtual bool extend_other(BoundingVolume *other) const;
virtual bool around_other(BoundingVolume *other,
const BoundingVolume **first,
const BoundingVolume **last) const;
virtual int contains_other(const BoundingVolume *other) const;
virtual bool extend_by_plane(const BoundingPlane *plane);
virtual int contains_sphere(const BoundingSphere *sphere) const;
private:
Planef _plane;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
GeometricBoundingVolume::init_type();
register_type(_type_handle, "BoundingPlane",
GeometricBoundingVolume::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
};
#include "boundingPlane.I"
#endif

View File

@ -119,6 +119,19 @@ extend_by_line(const BoundingLine *) {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: BoundingVolume::extend_by_plane
// Access: Protected, Virtual
// Description: Double-dispatch support: called by extend_other()
// when the type we're extending by is known to be a
// plane.
////////////////////////////////////////////////////////////////////
bool BoundingVolume::
extend_by_plane(const BoundingPlane *) {
_flags = F_infinite;
return false;
}
////////////////////////////////////////////////////////////////////
// Function: BoundingVolume::around_spheres
// Access: Protected, Virtual
@ -167,6 +180,28 @@ around_lines(const BoundingVolume **, const BoundingVolume **) {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: BoundingVolume::around_planes
// Access: Protected, Virtual
// Description: Double-dispatch support: called by around_other()
// when the type of the first element in the list is
// known to be a nonempty plane.
////////////////////////////////////////////////////////////////////
bool BoundingVolume::
around_planes(const BoundingVolume **, const BoundingVolume **) {
_flags = F_infinite;
if (is_of_type(FiniteBoundingVolume::get_class_type())) {
// If it's a FiniteBoundingVolume, we can't do any better than
// making it infinite. So we return true.
return true;
}
// Otherwise, we might do better, and we require each class to
// define a function. If we get here, the function isn't defined,
// so we return false to indicate this.
return false;
}
////////////////////////////////////////////////////////////////////
// Function: BoundingVolume::contains_sphere
// Access: Protected, Virtual
@ -202,3 +237,15 @@ int BoundingVolume::
contains_line(const BoundingLine *) const {
return IF_dont_understand;
}
////////////////////////////////////////////////////////////////////
// Function: BoundingVolume::contains_plane
// Access: Protected, Virtual
// Description: Double-dispatch support: called by contains_other()
// when the type we're testing for intersection is known
// to be a plane.
////////////////////////////////////////////////////////////////////
int BoundingVolume::
contains_plane(const BoundingPlane *) const {
return IF_dont_understand;
}

View File

@ -27,6 +27,7 @@
class BoundingSphere;
class BoundingHexahedron;
class BoundingLine;
class BoundingPlane;
////////////////////////////////////////////////////////////////////
@ -118,6 +119,7 @@ protected:
virtual bool extend_by_sphere(const BoundingSphere *sphere);
virtual bool extend_by_hexahedron(const BoundingHexahedron *hexahedron);
virtual bool extend_by_line(const BoundingLine *line);
virtual bool extend_by_plane(const BoundingPlane *plane);
virtual bool around_spheres(const BoundingVolume **first,
const BoundingVolume **last);
@ -125,10 +127,13 @@ protected:
const BoundingVolume **last);
virtual bool around_lines(const BoundingVolume **first,
const BoundingVolume **last);
virtual bool around_planes(const BoundingVolume **first,
const BoundingVolume **last);
virtual int contains_sphere(const BoundingSphere *sphere) const;
virtual int contains_hexahedron(const BoundingHexahedron *hexahedron) const;
virtual int contains_line(const BoundingLine *line) const;
virtual int contains_plane(const BoundingPlane *plane) const;
public:
@ -151,6 +156,7 @@ private:
friend class BoundingSphere;
friend class BoundingHexahedron;
friend class BoundingLine;
friend class BoundingPlane;
};
INLINE_MATHUTIL ostream &operator << (ostream &out, const BoundingVolume &bound);

View File

@ -24,6 +24,7 @@
#include "boundingSphere.h"
#include "boundingHexahedron.h"
#include "boundingLine.h"
#include "boundingPlane.h"
#include "linmath_events.h"
#include "dconfig.h"
#include "pandaSystem.h"
@ -51,6 +52,7 @@ ConfigureFn(config_mathutil) {
GeometricBoundingVolume::init_type();
OmniBoundingVolume::init_type();
BoundingLine::init_type();
BoundingPlane::init_type();
EventStoreVec2::init_type("EventStoreVec2");
EventStoreVec3::init_type("EventStoreVec3");
EventStoreMat4::init_type("EventStoreMat4");

View File

@ -2,6 +2,7 @@
#include "boundingVolume.cxx"
#include "boundingHexahedron.cxx"
#include "boundingLine.cxx"
#include "boundingPlane.cxx"
#include "boundingSphere.cxx"
#include "finiteBoundingVolume.cxx"
#include "geometricBoundingVolume.cxx"

View File

@ -24,7 +24,7 @@
// It's not clear how useful a default plane is.
////////////////////////////////////////////////////////////////////
INLINE_MATHUTIL FLOATNAME(Plane)::
FLOATNAME(Plane)(void) {
FLOATNAME(Plane)() {
_v.v._0 = 0.0f;
_v.v._1 = 0.0f;
_v.v._2 = 1.0f;
@ -116,6 +116,26 @@ operator * (const FLOATNAME(LMatrix4) &mat) const {
return FLOATNAME(Plane)(new_normal, new_point);
}
////////////////////////////////////////////////////////////////////
// Function: Plane::Operator *= LMatrix4
// Access: Published
// Description: Transforms the plane by the indicated matrix.
////////////////////////////////////////////////////////////////////
INLINE_MATHUTIL void FLOATNAME(Plane)::
operator *= (const FLOATNAME(LMatrix4) &mat) {
(*this) = (*this) * mat;
}
////////////////////////////////////////////////////////////////////
// Function: Plane::xform
// Access: Published
// Description: Transforms the plane by the indicated matrix.
////////////////////////////////////////////////////////////////////
void FLOATNAME(Plane)::
xform(const FLOATNAME(LMatrix4) &mat) {
(*this) = (*this) * mat;
}
////////////////////////////////////////////////////////////////////
// Function: Plane::Unary -
// Access: Published

View File

@ -25,7 +25,7 @@
// mirror.
////////////////////////////////////////////////////////////////////
FLOATNAME(LMatrix4) FLOATNAME(Plane)::
get_reflection_mat(void) const {
get_reflection_mat() const {
FLOATTYPE aa = _v.v._0 * _v.v._0;
FLOATTYPE ab = _v.v._0 * _v.v._1;
FLOATTYPE ac = _v.v._0 * _v.v._2;

View File

@ -23,7 +23,7 @@
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA FLOATNAME(Plane) : public FLOATNAME(LVecBase4) {
PUBLISHED:
INLINE_MATHUTIL FLOATNAME(Plane)(void);
INLINE_MATHUTIL FLOATNAME(Plane)();
INLINE_MATHUTIL FLOATNAME(Plane)(const FLOATNAME(LVecBase4) &copy);
INLINE_MATHUTIL FLOATNAME(Plane)(const FLOATNAME(LPoint3) &a,
const FLOATNAME(LPoint3) &b,
@ -35,20 +35,22 @@ PUBLISHED:
INLINE_MATHUTIL FLOATNAME(Plane) operator * (const FLOATNAME(LMatrix3) &mat) const;
INLINE_MATHUTIL FLOATNAME(Plane) operator * (const FLOATNAME(LMatrix4) &mat) const;
INLINE_MATHUTIL void operator *= (const FLOATNAME(LMatrix4) &mat);
INLINE_MATHUTIL void xform(const FLOATNAME(LMatrix4) &mat);
INLINE_MATHUTIL FLOATNAME(Plane) operator - () const;
FLOATNAME(LMatrix4) get_reflection_mat(void) const;
FLOATNAME(LMatrix4) get_reflection_mat() const;
INLINE_MATHUTIL FLOATNAME(LVector3) get_normal() const;
FLOATNAME(LPoint3) get_point() const;
INLINE_MATHUTIL FLOATTYPE dist_to_plane(const FLOATNAME(LPoint3) &point) const;
INLINE_MATHUTIL bool intersects_line(FLOATNAME(LPoint3) &intersection_point,
const FLOATNAME(LPoint3) &p1,
const FLOATNAME(LPoint3) &p2) const;
const FLOATNAME(LPoint3) &p1,
const FLOATNAME(LPoint3) &p2) const;
INLINE_MATHUTIL bool intersects_line(FLOATTYPE &t,
const FLOATNAME(LPoint3) &from,
const FLOATNAME(LVector3) &delta) const;
const FLOATNAME(LPoint3) &from,
const FLOATNAME(LVector3) &delta) const;
bool intersects_plane(FLOATNAME(LPoint3) &from,
FLOATNAME(LVector3) &delta,

View File

@ -35,6 +35,7 @@
cullBinUnsorted.I cullBinUnsorted.h \
cullFaceAttrib.I cullFaceAttrib.h \
cullHandler.I cullHandler.h \
cullPlanes.I cullPlanes.h \
cullResult.I cullResult.h \
cullTraverser.I cullTraverser.h \
cullTraverserData.I cullTraverserData.h \
@ -134,6 +135,7 @@
cullBinUnsorted.cxx \
cullFaceAttrib.cxx \
cullHandler.cxx \
cullPlanes.cxx \
cullResult.cxx \
cullTraverser.cxx \
cullTraverserData.cxx \
@ -229,6 +231,7 @@
cullBinUnsorted.I cullBinUnsorted.h \
cullFaceAttrib.I cullFaceAttrib.h \
cullHandler.I cullHandler.h \
cullPlanes.I cullPlanes.h \
cullResult.I cullResult.h \
cullTraverser.I cullTraverser.h \
cullTraverserData.I cullTraverserData.h \

View File

@ -516,6 +516,83 @@ filter_to_max(int max_clip_planes) const {
return planeNode_attrib;
}
////////////////////////////////////////////////////////////////////
// Function: ClipPlaneAttrib::compose_off
// Access: Public
// Description: This is a special method which composes two
// ClipPlaneAttribs with regard only to their set of
// "off" clip planes, for the purposes of deriving
// PandaNode::get_off_clip_planes().
//
// The result will be a ClipPlaneAttrib that represents
// the union of all of the clip planes turned off in
// either attrib. The set of on planes in the result is
// undefined and should be ignored.
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) ClipPlaneAttrib::
compose_off(const RenderAttrib *other) const {
const ClipPlaneAttrib *ta;
DCAST_INTO_R(ta, other, 0);
if (_off_all_planes || (!ta->_off_all_planes && ta->_off_planes.empty())) {
// If we turn off all planes, or the other turns none off, the
// result is the same as this one.
return this;
}
if (ta->_off_all_planes || _off_planes.empty()) {
// And contrariwise.
return ta;
}
Planes::const_iterator ai = _off_planes.begin();
Planes::const_iterator bi = ta->_off_planes.begin();
// Create a new ClipPlaneAttrib that will hold the result.
ClipPlaneAttrib *new_attrib = new ClipPlaneAttrib;
back_insert_iterator<Planes> result =
back_inserter(new_attrib->_on_planes);
while (ai != _off_planes.end() &&
bi != ta->_off_planes.end()) {
if ((*ai) < (*bi)) {
// Here is a plane that we have in the original, which is not
// present in the secondary.
*result = *ai;
++ai;
++result;
} else if ((*bi) < (*ai)) {
// Here is a new plane we have in the secondary, that was not
// present in the original.
*result = *bi;
++bi;
++result;
} else { // (*bi) == (*ai)
// Here is a plane we have in both.
*result = *bi;
++ai;
++bi;
++result;
}
}
while (ai != _off_planes.end()) {
*result = *ai;
++ai;
++result;
}
while (bi != ta->_off_planes.end()) {
*result = *bi;
++bi;
++result;
}
return return_new(new_attrib);
}
////////////////////////////////////////////////////////////////////
// Function: ClipPlaneAttrib::issue
// Access: Public, Virtual

View File

@ -96,6 +96,7 @@ PUBLISHED:
CPT(ClipPlaneAttrib) filter_to_max(int max_clip_planes) const;
public:
CPT(RenderAttrib) compose_off(const RenderAttrib *other) const;
virtual void issue(GraphicsStateGuardianBase *gsg) const;
virtual void output(ostream &out) const;

View File

@ -0,0 +1,68 @@
// Filename: cullPlanes.I
// Created by: drose (23Aug05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: CullPlanes::Constructor
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
INLINE CullPlanes::
CullPlanes() {
}
////////////////////////////////////////////////////////////////////
// Function: CullPlanes::Copy Constructor
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
INLINE CullPlanes::
CullPlanes(const CullPlanes &copy) :
_planes(copy._planes)
{
}
////////////////////////////////////////////////////////////////////
// Function: CullPlanes::Copy Assignment Operator
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
INLINE void CullPlanes::
operator = (const CullPlanes &copy) {
_planes = copy._planes;
}
////////////////////////////////////////////////////////////////////
// Function: CullPlanes::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE CullPlanes::
~CullPlanes() {
}
////////////////////////////////////////////////////////////////////
// Function: CullPlanes::is_empty
// Access: Public
// Description: Returns true if there are no planes listed in the
// CullPlanes object.
////////////////////////////////////////////////////////////////////
INLINE bool CullPlanes::
is_empty() const {
return _planes.empty();
}

View File

@ -0,0 +1,218 @@
// Filename: cullPlanes.cxx
// Created by: drose (23Aug05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "cullPlanes.h"
////////////////////////////////////////////////////////////////////
// Function: CullPlanes::make_empty
// Access: Public, Static
// Description: Returns a pointer to an empty CullPlanes object.
////////////////////////////////////////////////////////////////////
CPT(CullPlanes) CullPlanes::
make_empty() {
static CPT(CullPlanes) empty;
if (empty == NULL) {
empty = new CullPlanes;
// Artificially tick the reference count, just to ensure we won't
// accidentally modify this object in any of the copy-on-write
// operations below.
empty->ref();
}
return empty;
}
////////////////////////////////////////////////////////////////////
// Function: CullPlanes::xform
// Access: Public
// Description: Returns a pointer to a new CullPlanes object that is
// the same as this one, but with the clip planes
// modified by the indicated transform.
////////////////////////////////////////////////////////////////////
CPT(CullPlanes) CullPlanes::
xform(const LMatrix4f &mat) const {
PT(CullPlanes) new_planes;
if (get_ref_count() == 1) {
new_planes = (CullPlanes *)this;
} else {
new_planes = new CullPlanes(*this);
}
for (Planes::iterator pi = new_planes->_planes.begin();
pi != new_planes->_planes.end();
++pi) {
if ((*pi).second->get_ref_count() != 1) {
(*pi).second = DCAST(BoundingPlane, (*pi).second->make_copy());
}
(*pi).second->xform(mat);
}
return new_planes;
}
////////////////////////////////////////////////////////////////////
// Function: CullPlanes::apply_state
// Access: Public
// Description: Returns a pointer to a new CullPlanes object that is
// the same as this one, but with the indicated
// attributes applied to the state.
//
// In particular, any new ClipPlanes given in
// net_attrib, if it is not NULL, will be added to the
// state, unless those ClipPlanes are also listed in
// off_attrib.
////////////////////////////////////////////////////////////////////
CPT(CullPlanes) CullPlanes::
apply_state(const CullTraverser *trav, const CullTraverserData *data,
const ClipPlaneAttrib *net_attrib,
const ClipPlaneAttrib *off_attrib) const {
if (net_attrib == (ClipPlaneAttrib *)NULL) {
return this;
}
PT(CullPlanes) new_planes;
if (get_ref_count() == 1) {
new_planes = (CullPlanes *)this;
} else {
new_planes = new CullPlanes(*this);
}
CPT(TransformState) net_transform = NULL;
int num_on_planes = net_attrib->get_num_on_planes();
for (int i = 0; i < num_on_planes; ++i) {
NodePath clip_plane = net_attrib->get_on_plane(i);
Planes::const_iterator pi = new_planes->_planes.find(clip_plane);
if (pi == new_planes->_planes.end()) {
if (!off_attrib->has_off_plane(clip_plane)) {
// Here's a new clip plane; add it to the list. For this we
// need the net transform to this node.
if (net_transform == (TransformState *)NULL) {
net_transform = data->get_net_transform(trav);
}
PlaneNode *plane_node = DCAST(PlaneNode, clip_plane.node());
CPT(TransformState) new_transform =
net_transform->invert_compose(clip_plane.get_net_transform());
Planef plane = plane_node->get_plane() * new_transform->get_mat();
new_planes->_planes[clip_plane] = new BoundingPlane(-plane);
}
}
}
return new_planes;
}
////////////////////////////////////////////////////////////////////
// Function: CullPlanes::do_cull
// Access: Public
// Description: Tests the indicated bounding volume against all of
// the clip planes in this object. Sets result to an
// appropriate union of
// BoundingVolume::IntersectionFlags, similar to the
// result of BoundingVolume::contains().
//
// Also, if the bounding volume is completely in front
// of any of the clip planes, removes those planes both
// from this object and from the indicated state,
// returning a new CullPlanes object in that case.
////////////////////////////////////////////////////////////////////
CPT(CullPlanes) CullPlanes::
do_cull(int &result, CPT(RenderState) &state,
const GeometricBoundingVolume *node_gbv) const {
// We should have a ClipPlaneAttrib in the state if we've called
// this method.
CPT(ClipPlaneAttrib) orig_cpa = state->get_clip_plane();
nassertr(orig_cpa != (ClipPlaneAttrib *)NULL, this);
result =
BoundingVolume::IF_all | BoundingVolume::IF_possible | BoundingVolume::IF_some;
CPT(CullPlanes) new_planes = this;
CPT(ClipPlaneAttrib) new_cpa = orig_cpa;
Planes::const_iterator pi;
for (pi = _planes.begin(); pi != _planes.end(); ++pi) {
int plane_result = (*pi).second->contains(node_gbv);
if (plane_result == BoundingVolume::IF_no_intersection) {
// The node is completely behind this clip plane. Short-circuit
// the rest of the logic; none of the other planes matter.
result = plane_result;
return new_planes;
}
if ((plane_result & BoundingVolume::IF_all) != 0) {
// The node is completely in front of this clip plane. We don't
// need to consider this plane ever again for any descendents of
// this node.
new_planes = new_planes->remove_plane((*pi).first);
nassertr(new_planes != this, new_planes);
new_cpa = DCAST(ClipPlaneAttrib, new_cpa->remove_on_plane((*pi).first));
}
result &= plane_result;
}
if (new_cpa != orig_cpa) {
if (new_cpa->is_identity()) {
state = state->remove_attrib(ClipPlaneAttrib::get_class_type());
} else {
state = state->add_attrib(new_cpa);
}
}
return new_planes;
}
////////////////////////////////////////////////////////////////////
// Function: CullPlanes::remove_plane
// Access: Public
// Description: Returns a pointer to a new CullPlanes object that is
// the same as this one, but with the indicated
// clip plane removed.
////////////////////////////////////////////////////////////////////
CPT(CullPlanes) CullPlanes::
remove_plane(const NodePath &clip_plane) const {
PT(CullPlanes) new_planes;
if (get_ref_count() == 1) {
new_planes = (CullPlanes *)this;
} else {
new_planes = new CullPlanes(*this);
}
Planes::iterator pi = new_planes->_planes.find(clip_plane);
nassertr(pi != new_planes->_planes.end(), new_planes);
new_planes->_planes.erase(pi);
return new_planes;
}
////////////////////////////////////////////////////////////////////
// Function: CullPlanes::write
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void CullPlanes::
write(ostream &out) const {
out << "CullPlanes (" << _planes.size() << " planes):\n";
Planes::const_iterator pi;
for (pi = _planes.begin(); pi != _planes.end(); ++pi) {
out << " " << (*pi).first << " : " << *(*pi).second << "\n";
}
}

View File

@ -0,0 +1,77 @@
// Filename: cullPlanes.h
// Created by: drose (23Aug05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef CULLPLANES_H
#define CULLPLANES_H
#include "pandabase.h"
#include "referenceCount.h"
#include "nodePath.h"
#include "clipPlaneAttrib.h"
#include "boundingPlane.h"
#include "pointerTo.h"
#include "luse.h"
////////////////////////////////////////////////////////////////////
// Class : CullPlanes
// Description : This represents the set of clip planes that are
// definitely in effect for the current node of the
// CullTraverserData, as well as on all child nodes.
// Any clip planes in this list may be safely culled
// against.
//
// This does not include the clip planes that are in
// effect now, but might later be turned off by a child
// node, since we can't safely cull against such clip
// planes.
//
// The bounding volumes in this object are transformed
// for each level of the scene graph.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA CullPlanes : public ReferenceCount {
protected:
INLINE CullPlanes();
INLINE CullPlanes(const CullPlanes &copy);
INLINE void operator = (const CullPlanes &copy);
public:
INLINE ~CullPlanes();
INLINE bool is_empty() const;
static CPT(CullPlanes) make_empty();
CPT(CullPlanes) xform(const LMatrix4f &mat) const;
CPT(CullPlanes) apply_state(const CullTraverser *trav,
const CullTraverserData *data,
const ClipPlaneAttrib *net_attrib,
const ClipPlaneAttrib *off_attrib) const;
CPT(CullPlanes) do_cull(int &result, CPT(RenderState) &state,
const GeometricBoundingVolume *node_gbv) const;
CPT(CullPlanes) remove_plane(const NodePath &clip_plane) const;
void write(ostream &out) const;
private:
typedef pmap<NodePath, PT(BoundingPlane) > Planes;
Planes _planes;
};
#include "cullPlanes.I"
#endif

View File

@ -166,8 +166,7 @@ traverse(CullTraverserData &data) {
data.apply_transform_and_state(this);
const RenderState *node_state = node->get_state();
const FogAttrib *fog = node_state->get_fog();
const FogAttrib *fog = node->get_state()->get_fog();
if (fog != (const FogAttrib *)NULL && fog->get_fog() != (Fog *)NULL) {
// If we just introduced a FogAttrib here, call adjust_to_camera()
// now. This maybe isn't the perfect time to call it, but it's

View File

@ -31,7 +31,8 @@ CullTraverserData(const NodePath &start,
_modelview_transform(modelview_transform),
_state(state),
_view_frustum(view_frustum),
_guard_band(guard_band)
_guard_band(guard_band),
_cull_planes(CullPlanes::make_empty())
{
}
@ -46,7 +47,8 @@ CullTraverserData(const CullTraverserData &copy) :
_modelview_transform(copy._modelview_transform),
_state(copy._state),
_view_frustum(copy._view_frustum),
_guard_band(copy._guard_band)
_guard_band(copy._guard_band),
_cull_planes(copy._cull_planes)
{
}
@ -62,6 +64,7 @@ operator = (const CullTraverserData &copy) {
_state = copy._state;
_view_frustum = copy._view_frustum;
_guard_band = copy._guard_band;
_cull_planes = copy._cull_planes;
}
////////////////////////////////////////////////////////////////////
@ -76,7 +79,8 @@ CullTraverserData(const CullTraverserData &parent, PandaNode *child) :
_modelview_transform(parent._modelview_transform),
_state(parent._state),
_view_frustum(parent._view_frustum),
_guard_band(parent._guard_band)
_guard_band(parent._guard_band),
_cull_planes(parent._cull_planes)
{
}
@ -132,9 +136,10 @@ is_in_view(const DrawMask &camera_mask) {
return false;
}
if (_view_frustum == (GeometricBoundingVolume *)NULL) {
// If the transform is valid, but we don't have a frustum, it's
// always in.
if (_view_frustum == (GeometricBoundingVolume *)NULL &&
_cull_planes->is_empty()) {
// If the transform is valid, but we don't have a frustum or any
// clip planes, it's always in.
return true;
}
@ -142,3 +147,28 @@ is_in_view(const DrawMask &camera_mask) {
return is_in_view_impl();
}
////////////////////////////////////////////////////////////////////
// Function: CullTraverserData::test_within_clip_planes
// Access: Public
// Description: Returns the BoundingVolume intersection bits
// appropriate for the intersection of the current clip
// planes with the current node.
//
// That is, if the current node is within all clip
// planes (and will not be clipped at all), returns
// BoundingVolume::IF_all. If it is completely behind
// at least one clip plane, returns
// BoundingVolume::IF_intersection. If it is partially
// behind one or more clip planes, returns
// BoundingVolume::IF_some.
////////////////////////////////////////////////////////////////////
INLINE int CullTraverserData::
test_within_clip_planes(const CullTraverser *trav) const {
const ClipPlaneAttrib *cpa = _state->get_clip_plane();
if (cpa != (ClipPlaneAttrib *)NULL) {
return test_within_clip_planes_impl(trav, cpa);
}
// No clip plane attrib; therefore, the node is not clipped at
// all.
return BoundingVolume::IF_all | BoundingVolume::IF_possible | BoundingVolume::IF_some;
}

View File

@ -23,6 +23,8 @@
#include "colorAttrib.h"
#include "textureAttrib.h"
#include "renderModeAttrib.h"
#include "clipPlaneAttrib.h"
#include "boundingPlane.h"
#include "billboardEffect.h"
#include "compassEffect.h"
#include "polylightEffect.h"
@ -62,7 +64,8 @@ apply_transform_and_state(CullTraverser *trav) {
}
apply_transform_and_state(trav, node->get_transform(),
node_state, node->get_effects());
node_state, node->get_effects(),
node->get_off_clip_planes());
}
////////////////////////////////////////////////////////////////////
@ -76,7 +79,8 @@ void CullTraverserData::
apply_transform_and_state(CullTraverser *trav,
CPT(TransformState) node_transform,
CPT(RenderState) node_state,
CPT(RenderEffects) node_effects) {
CPT(RenderEffects) node_effects,
const RenderAttrib *off_clip_planes) {
if (node_effects->has_cull_callback()) {
node_effects->cull_callback(trav, *this, node_transform, node_state);
}
@ -85,7 +89,8 @@ apply_transform_and_state(CullTraverser *trav,
_modelview_transform = _modelview_transform->compose(node_transform);
if ((_view_frustum != (GeometricBoundingVolume *)NULL) ||
(_guard_band != (GeometricBoundingVolume *)NULL)) {
(_guard_band != (GeometricBoundingVolume *)NULL) ||
(!_cull_planes->is_empty())) {
// We need to move the viewing frustums into the node's
// coordinate space by applying the node's inverse transform.
if (node_transform->is_singular()) {
@ -94,6 +99,7 @@ apply_transform_and_state(CullTraverser *trav,
// point down.
_view_frustum = (GeometricBoundingVolume *)NULL;
_guard_band = (GeometricBoundingVolume *)NULL;
_cull_planes = CullPlanes::make_empty();
} else {
CPT(TransformState) inv_transform =
@ -110,11 +116,16 @@ apply_transform_and_state(CullTraverser *trav,
_guard_band = DCAST(GeometricBoundingVolume, _guard_band->make_copy());
_guard_band->xform(inv_transform->get_mat());
}
_cull_planes = _cull_planes->xform(inv_transform->get_mat());
}
}
}
_state = _state->compose(node_state);
_cull_planes = _cull_planes->apply_state(trav, this,
_state->get_clip_plane(),
DCAST(ClipPlaneAttrib, off_clip_planes));
}
////////////////////////////////////////////////////////////////////
@ -124,55 +135,131 @@ apply_transform_and_state(CullTraverser *trav,
////////////////////////////////////////////////////////////////////
bool CullTraverserData::
is_in_view_impl() {
// By the time we get here, we know we have a viewing frustum.
nassertr(_view_frustum != (GeometricBoundingVolume *)NULL, true);
const BoundingVolume &node_volume = node()->get_bound();
nassertr(node_volume.is_of_type(GeometricBoundingVolume::get_class_type()), false);
const GeometricBoundingVolume *node_gbv =
DCAST(GeometricBoundingVolume, &node_volume);
int result = _view_frustum->contains(node_gbv);
if (pgraph_cat.is_spam()) {
pgraph_cat.spam()
<< _node_path << " cull result = " << hex << result << dec << "\n";
}
if (result == BoundingVolume::IF_no_intersection) {
// No intersection at all. Cull.
if (!fake_view_frustum_cull) {
return false;
}
// If we have fake view-frustum culling enabled, instead of
// actually culling an object we simply force it to be drawn in
// red wireframe.
_view_frustum = (GeometricBoundingVolume *)NULL;
CPT(RenderState) fake_state = get_fake_view_frustum_cull_state();
_state = _state->compose(fake_state);
if (_view_frustum != (GeometricBoundingVolume *)NULL) {
int result = _view_frustum->contains(node_gbv);
} else if ((result & BoundingVolume::IF_all) != 0) {
// The node and its descendants are completely enclosed within
// the frustum. No need to cull further.
_view_frustum = (GeometricBoundingVolume *)NULL;
_guard_band = (GeometricBoundingVolume *)NULL;
} else {
if (node()->is_final()) {
// The bounding volume is partially, but not completely,
// within the viewing frustum. Normally we'd keep testing
// child bounding volumes as we continue down. But this node
// has the "final" flag, so the user is claiming that there is
// some important reason we should consider everything visible
// at this point. So be it.
if (pgraph_cat.is_spam()) {
pgraph_cat.spam()
<< _node_path << " cull result = " << hex << result << dec << "\n";
}
if (result == BoundingVolume::IF_no_intersection) {
// No intersection at all. Cull.
if (!fake_view_frustum_cull) {
return false;
}
// If we have fake view-frustum culling enabled, instead of
// actually culling an object we simply force it to be drawn in
// red wireframe.
_view_frustum = (GeometricBoundingVolume *)NULL;
CPT(RenderState) fake_state = get_fake_view_frustum_cull_state();
_state = _state->compose(fake_state);
} else if ((result & BoundingVolume::IF_all) != 0) {
// The node and its descendents are completely enclosed within
// the frustum. No need to cull further.
_view_frustum = (GeometricBoundingVolume *)NULL;
_guard_band = (GeometricBoundingVolume *)NULL;
} else {
// The node is partially, but not completely, within the viewing
// frustum.
if (node()->is_final()) {
// Normally we'd keep testing child bounding volumes as we
// continue down. But this node has the "final" flag, so the
// user is claiming that there is some important reason we
// should consider everything visible at this point. So be it.
_view_frustum = (GeometricBoundingVolume *)NULL;
}
}
}
if (!_cull_planes->is_empty()) {
// Also cull against the current clip planes.
int result;
_cull_planes = _cull_planes->do_cull(result, _state, node_gbv);
if (pgraph_cat.is_spam()) {
pgraph_cat.spam()
<< _node_path << " cull planes cull result = " << hex
<< result << dec << "\n";
_cull_planes->write(pgraph_cat.spam(false));
}
if (result == BoundingVolume::IF_no_intersection) {
// No intersection at all. Cull.
return false;
} else if ((result & BoundingVolume::IF_all) != 0) {
// The node and its descendents are completely in front of all
// of the clip planes. The do_cull() call should therefore have
// removed all of the clip planes.
nassertr(_cull_planes->is_empty(), true);
} else {
// The node is partially within one or more clip planes.
if (node()->is_final()) {
// Even though the node is only partially within the clip
// planes, stop culling against them.
_cull_planes = CullPlanes::make_empty();
}
}
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: CullTraverserData::test_within_clip_planes_impl
// Access: Private
// Description: The private implementation of test_within_clip_planes().
////////////////////////////////////////////////////////////////////
int CullTraverserData::
test_within_clip_planes_impl(const CullTraverser *trav,
const ClipPlaneAttrib *cpa) const {
int result = BoundingVolume::IF_all | BoundingVolume::IF_possible | BoundingVolume::IF_some;
const BoundingVolume &node_volume = node()->get_bound();
nassertr(node_volume.is_of_type(GeometricBoundingVolume::get_class_type()), result);
const GeometricBoundingVolume *node_gbv =
DCAST(GeometricBoundingVolume, &node_volume);
CPT(TransformState) net_transform = get_net_transform(trav);
cerr << "considering " << _node_path << ", bv = " << *node_gbv << "\n";
cerr << " net_transform = " << *net_transform << "\n";
int num_planes = cpa->get_num_on_planes();
for (int i = 0;
i < num_planes && result != BoundingVolume::IF_no_intersection;
++i) {
NodePath plane_path = cpa->get_on_plane(i);
PlaneNode *plane_node = DCAST(PlaneNode, plane_path.node());
CPT(TransformState) new_transform =
net_transform->invert_compose(plane_path.get_net_transform());
Planef plane = plane_node->get_plane() * new_transform->get_mat();
BoundingPlane bplane(-plane);
cerr << " " << bplane << " -> " << bplane.contains(node_gbv) << "\n";
result &= bplane.contains(node_gbv);
}
if (pgraph_cat.is_spam()) {
pgraph_cat.spam()
<< _node_path << " test_within_clip_planes result = "
<< hex << result << dec << "\n";
}
return result;
}
////////////////////////////////////////////////////////////////////
// Function: CullTraverserData::get_fake_view_frustum_cull_state

View File

@ -20,7 +20,7 @@
#define CULLTRAVERSERDATA_H
#include "pandabase.h"
#include "cullPlanes.h"
#include "workingNodePath.h"
#include "renderState.h"
#include "transformState.h"
@ -64,20 +64,27 @@ public:
CPT(TransformState) get_net_transform(const CullTraverser *trav) const;
INLINE bool is_in_view(const DrawMask &camera_mask);
INLINE int test_within_clip_planes(const CullTraverser *trav) const;
void apply_transform_and_state(CullTraverser *trav);
void apply_transform_and_state(CullTraverser *trav,
CPT(TransformState) node_transform,
CPT(RenderState) node_state,
CPT(RenderEffects) node_effects);
CPT(RenderEffects) node_effects,
const RenderAttrib *off_clip_planes);
WorkingNodePath _node_path;
CPT(TransformState) _modelview_transform;
CPT(RenderState) _state;
PT(GeometricBoundingVolume) _view_frustum;
PT(GeometricBoundingVolume) _guard_band;
CPT(CullPlanes) _cull_planes;
private:
bool is_in_view_impl();
int test_within_clip_planes_impl(const CullTraverser *trav,
const ClipPlaneAttrib *cpa) const;
static CPT(RenderState) get_fake_view_frustum_cull_state();
};

View File

@ -27,6 +27,7 @@ ModelNode(const string &name) :
PandaNode(name)
{
_preserve_transform = PT_none;
_preserve_attributes = 0;
}
////////////////////////////////////////////////////////////////////
@ -66,6 +67,33 @@ get_preserve_transform() const {
return _preserve_transform;
}
////////////////////////////////////////////////////////////////////
// Function: ModelNode::set_preserve_attributes
// Access: Public
// Description: Sets the preserve_attributes flag. This restricts the
// ability of a flatten operation to affect the
// render attributes stored on this node.
//
// The value should be the union of bits from
// SceneGraphReducer::AttribTypes that represent the
// attributes that should *not* be changed.
////////////////////////////////////////////////////////////////////
INLINE void ModelNode::
set_preserve_attributes(int preserve_attributes) {
_preserve_attributes = preserve_attributes;
}
////////////////////////////////////////////////////////////////////
// Function: ModelNode::get_preserve_attributes
// Access: Public
// Description: Returns the current setting of the preserve_attributes
// flag. See set_preserve_attributes().
////////////////////////////////////////////////////////////////////
INLINE int ModelNode::
get_preserve_attributes() const {
return _preserve_attributes;
}
////////////////////////////////////////////////////////////////////
// Function: ModelNode::Copy Constructor
// Access: Protected
@ -74,6 +102,7 @@ get_preserve_transform() const {
INLINE ModelNode::
ModelNode(const ModelNode &copy) :
PandaNode(copy),
_preserve_transform(copy._preserve_transform)
_preserve_transform(copy._preserve_transform),
_preserve_attributes(copy._preserve_attributes)
{
}

View File

@ -109,6 +109,22 @@ preserve_name() const {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: ModelNode::get_unsafe_to_apply_attribs
// Access: Public, Virtual
// Description: Returns the union of all attributes from
// SceneGraphReducer::AttribTypes that may not safely be
// applied to the vertices of this node. If this is
// nonzero, these attributes must be dropped at this
// node as a state change.
//
// This is a generalization of safe_to_transform().
////////////////////////////////////////////////////////////////////
int ModelNode::
get_unsafe_to_apply_attribs() const {
return _preserve_attributes;
}
////////////////////////////////////////////////////////////////////
// Function: ModelNode::register_with_read_factory
// Access: Public, Static
@ -130,6 +146,7 @@ void ModelNode::
write_datagram(BamWriter *manager, Datagram &dg) {
PandaNode::write_datagram(manager, dg);
dg.add_uint8((int)_preserve_transform);
dg.add_uint16(_preserve_attributes);
}
////////////////////////////////////////////////////////////////////
@ -164,4 +181,8 @@ fillin(DatagramIterator &scan, BamReader *manager) {
PandaNode::fillin(scan, manager);
_preserve_transform = (PreserveTransform)scan.get_uint8();
_preserve_attributes = 0;
if (manager->get_file_minor_ver() >= 3) {
_preserve_attributes = scan.get_uint16();
}
}

View File

@ -50,6 +50,7 @@ public:
virtual bool safe_to_modify_transform() const;
virtual bool safe_to_combine() const;
virtual bool preserve_name() const;
virtual int get_unsafe_to_apply_attribs() const;
PUBLISHED:
enum PreserveTransform {
@ -61,8 +62,12 @@ PUBLISHED:
INLINE void set_preserve_transform(PreserveTransform preserve_transform);
INLINE PreserveTransform get_preserve_transform() const;
INLINE void set_preserve_attributes(int attrib_mask);
INLINE int get_preserve_attributes() const;
private:
PreserveTransform _preserve_transform;
int _preserve_attributes;
public:
static void register_with_read_factory();

View File

@ -436,7 +436,15 @@ get_stashed_sort(int n) const {
INLINE void PandaNode::
set_attrib(const RenderAttrib *attrib, int override) {
CDWriter cdata(_cycler);
cdata->_state = cdata->_state->add_attrib(attrib, override);
CPT(RenderState) new_state = cdata->_state->add_attrib(attrib, override);
if (cdata->_state != new_state) {
cdata->_state = new_state;
// We mark the bound stale when the state changes, in case we have
// changed a ClipPlaneAttrib.
mark_bound_stale();
state_changed();
}
}
////////////////////////////////////////////////////////////////////
@ -483,7 +491,15 @@ has_attrib(TypeHandle type) const {
INLINE void PandaNode::
clear_attrib(TypeHandle type) {
CDWriter cdata(_cycler);
cdata->_state = cdata->_state->remove_attrib(type);
CPT(RenderState) new_state = cdata->_state->remove_attrib(type);
if (cdata->_state != new_state) {
cdata->_state = new_state;
// We mark the bound stale when the state changes, in case we have
// changed a ClipPlaneAttrib.
mark_bound_stale();
state_changed();
}
}
////////////////////////////////////////////////////////////////////
@ -554,7 +570,14 @@ clear_effect(TypeHandle type) {
INLINE void PandaNode::
set_state(const RenderState *state) {
CDWriter cdata(_cycler);
cdata->_state = state;
if (cdata->_state != state) {
cdata->_state = state;
// We mark the bound stale when the state changes, in case we have
// changed a ClipPlaneAttrib.
mark_bound_stale();
state_changed();
}
}
////////////////////////////////////////////////////////////////////
@ -583,7 +606,14 @@ get_state() const {
INLINE void PandaNode::
clear_state() {
CDWriter cdata(_cycler);
cdata->_state = RenderState::make_empty();
if (!cdata->_state->is_empty()) {
cdata->_state = RenderState::make_empty();
// We mark the bound stale when the state changes, in case we have
// changed a ClipPlaneAttrib.
mark_bound_stale();
state_changed();
}
}
////////////////////////////////////////////////////////////////////
@ -633,9 +663,11 @@ clear_effects() {
INLINE void PandaNode::
set_transform(const TransformState *transform) {
CDWriter cdata(_cycler);
cdata->_transform = transform;
mark_bound_stale();
transform_changed();
if (cdata->_transform != transform) {
cdata->_transform = transform;
mark_bound_stale();
transform_changed();
}
}
////////////////////////////////////////////////////////////////////
@ -661,9 +693,11 @@ get_transform() const {
INLINE void PandaNode::
clear_transform() {
CDWriter cdata(_cycler);
cdata->_transform = TransformState::make_identity();
mark_bound_stale();
transform_changed();
if (!cdata->_transform->is_identity()) {
cdata->_transform = TransformState::make_identity();
mark_bound_stale();
transform_changed();
}
}
////////////////////////////////////////////////////////////////////
@ -819,7 +853,10 @@ ls(ostream &out, int indent_level) const {
INLINE void PandaNode::
set_draw_mask(DrawMask mask) {
CDWriter cdata(_cycler);
cdata->_draw_mask = mask;
if (cdata->_draw_mask != mask) {
cdata->_draw_mask = mask;
draw_mask_changed();
}
}
////////////////////////////////////////////////////////////////////
@ -887,6 +924,21 @@ get_net_collide_mask() const {
return cdata->_net_collide_mask;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::get_off_clip_planes
// Access: Published
// Description: Returns a ClipPlaneAttrib which represents the union
// of all of the clip planes that have been turned *off*
// at this level and below.
////////////////////////////////////////////////////////////////////
INLINE const RenderAttrib *PandaNode::
get_off_clip_planes() const {
// Call get_bound() first to ensure the attrib is recomputed.
BoundedObject::get_bound();
CDReader cdata(_cycler);
return cdata->_off_clip_planes;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::set_bound
// Access: Published

View File

@ -25,6 +25,7 @@
#include "geometricBoundingVolume.h"
#include "sceneGraphReducer.h"
#include "accumulatedAttribs.h"
#include "clipPlaneAttrib.h"
TypeHandle PandaNode::_type_handle;
@ -1581,6 +1582,12 @@ recompute_bound() {
CDWriter cdata(_cycler);
cdata->_net_collide_mask = cdata->_into_collide_mask;
// And compute the set of "off" clip planes.
cdata->_off_clip_planes = cdata->_state->get_clip_plane();
if (cdata->_off_clip_planes == (RenderAttrib *)NULL) {
cdata->_off_clip_planes = ClipPlaneAttrib::make();
}
// Now actually compute the bounding volume by putting it around all
// of our child bounding volumes.
pvector<const BoundingVolume *> child_volumes;
@ -1595,6 +1602,8 @@ recompute_bound() {
const BoundingVolume &child_bound = child->get_bound();
child_volumes.push_back(&child_bound);
cdata->_net_collide_mask |= child->get_net_collide_mask();
CPT(ClipPlaneAttrib) orig = DCAST(ClipPlaneAttrib, cdata->_off_clip_planes);
cdata->_off_clip_planes = orig->compose_off(child->get_off_clip_planes());
}
const BoundingVolume **child_begin = &child_volumes[0];
@ -1678,6 +1687,28 @@ void PandaNode::
transform_changed() {
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::state_changed
// Access: Protected, Virtual
// Description: Called after the node's RenderState has been changed
// for any reason, this just provides a hook so derived
// classes can do something special in this case.
////////////////////////////////////////////////////////////////////
void PandaNode::
state_changed() {
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::draw_mask_changed
// Access: Protected, Virtual
// Description: Called after the node's DrawMask has been changed
// for any reason, this just provides a hook so derived
// classes can do something special in this case.
////////////////////////////////////////////////////////////////////
void PandaNode::
draw_mask_changed() {
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::r_copy_subgraph
// Access: Protected, Virtual

View File

@ -187,6 +187,7 @@ PUBLISHED:
virtual CollideMask get_legal_collide_mask() const;
INLINE CollideMask get_net_collide_mask() const;
INLINE const RenderAttrib *get_off_clip_planes() const;
virtual void output(ostream &out) const;
virtual void write(ostream &out, int indent_level) const;
@ -225,6 +226,8 @@ protected:
virtual void parents_changed();
virtual void children_changed();
virtual void transform_changed();
virtual void state_changed();
virtual void draw_mask_changed();
INLINE void add_net_collide_mask(CollideMask mask);
typedef pmap<PandaNode *, PandaNode *> InstanceMap;
@ -363,6 +366,13 @@ private:
// we update them together.
CollideMask _net_collide_mask;
// This is a ClipPlaneAttrib that represents the union of all clip
// planes that have been turned *off* at and below this level. As
// above, it's similar to a bounding volume, and is updated at the
// same time. TODO: fix the circular reference counts involved
// here.
CPT(RenderAttrib) _off_clip_planes;
bool _fixed_internal_bound;
};

View File

@ -8,6 +8,7 @@
#include "cullBinUnsorted.cxx"
#include "cullFaceAttrib.cxx"
#include "cullHandler.cxx"
#include "cullPlanes.cxx"
#include "cullResult.cxx"
#include "cullTraverser.cxx"
#include "cullTraverserData.cxx"

View File

@ -66,10 +66,11 @@ fillin(DatagramIterator &scan, BamReader *) {
// Description:
////////////////////////////////////////////////////////////////////
PlaneNode::
PlaneNode(const string &name) :
PlaneNode(const string &name, const Planef &plane) :
PandaNode(name),
_priority(0)
{
set_plane(plane);
}
////////////////////////////////////////////////////////////////////

View File

@ -34,7 +34,7 @@
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA PlaneNode : public PandaNode {
PUBLISHED:
PlaneNode(const string &name);
PlaneNode(const string &name, const Planef &plane = Planef());
protected:
PlaneNode(const PlaneNode &copy);

View File

@ -336,6 +336,25 @@ get_render_mode() const {
return _render_mode;
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::get_clip_plane
// Access: Published
// Description: This function is provided as an optimization, to
// speed up the render-time checking for the existance
// of a ClipPlaneAttrib on this state. It returns a
// pointer to the ClipPlaneAttrib, if there is one, or
// NULL if there is not.
////////////////////////////////////////////////////////////////////
INLINE const ClipPlaneAttrib *RenderState::
get_clip_plane() const {
if ((_flags & F_checked_clip_plane) == 0) {
// We pretend this function is const, even though it transparently
// modifies the internal clip_plane cache.
((RenderState *)this)->determine_clip_plane();
}
return _clip_plane;
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::set_destructing
// Access: Private

View File

@ -21,6 +21,7 @@
#include "cullBinAttrib.h"
#include "cullBinManager.h"
#include "fogAttrib.h"
#include "clipPlaneAttrib.h"
#include "transparencyAttrib.h"
#include "colorAttrib.h"
#include "colorScaleAttrib.h"
@ -1647,6 +1648,21 @@ determine_render_mode() {
_flags |= F_checked_render_mode;
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::determine_clip_plane
// Access: Private
// Description: This is the private implementation of get_clip_plane().
////////////////////////////////////////////////////////////////////
void RenderState::
determine_clip_plane() {
const RenderAttrib *attrib = get_attrib(ClipPlaneAttrib::get_class_type());
_clip_plane = (const ClipPlaneAttrib *)NULL;
if (attrib != (const RenderAttrib *)NULL) {
_clip_plane = DCAST(ClipPlaneAttrib, attrib);
}
_flags |= F_checked_clip_plane;
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::update_pstats
// Access: Private

View File

@ -40,6 +40,7 @@ class ColorAttrib;
class ColorScaleAttrib;
class TextureAttrib;
class TexGenAttrib;
class ClipPlaneAttrib;
class FactoryParams;
////////////////////////////////////////////////////////////////////
@ -133,6 +134,7 @@ PUBLISHED:
INLINE const TexGenAttrib *get_tex_gen() const;
INLINE const TexMatrixAttrib *get_tex_matrix() const;
INLINE const RenderModeAttrib *get_render_mode() const;
INLINE const ClipPlaneAttrib *get_clip_plane() const;
int get_geom_rendering(int geom_rendering) const;
@ -177,6 +179,7 @@ private:
void determine_tex_gen();
void determine_tex_matrix();
void determine_render_mode();
void determine_clip_plane();
INLINE void set_destructing();
INLINE bool is_destructing() const;
@ -274,6 +277,7 @@ private:
const TexGenAttrib *_tex_gen;
const TexMatrixAttrib *_tex_matrix;
const RenderModeAttrib *_render_mode;
const ClipPlaneAttrib *_clip_plane;
enum Flags {
F_checked_bin_index = 0x0001,
@ -286,6 +290,7 @@ private:
F_checked_tex_gen = 0x0080,
F_checked_tex_matrix = 0x0100,
F_checked_render_mode = 0x0200,
F_checked_clip_plane = 0x0400,
F_is_destructing = 0x8000,
};
unsigned short _flags;

View File

@ -12,49 +12,61 @@
#define SOURCES \
config_pgui.h \
pgButton.I pgButton.h \
pgSliderButton.I pgSliderButton.h \
pgButtonNotify.I pgButtonNotify.h \
pgCullTraverser.I pgCullTraverser.h \
pgEntry.I pgEntry.h \
pgMouseWatcherGroup.I pgMouseWatcherGroup.h \
pgMouseWatcherParameter.I pgMouseWatcherParameter.h \
pgFrameStyle.I pgFrameStyle.h \
pgItem.I pgItem.h \
pgItemNotify.I pgItemNotify.h \
pgMouseWatcherBackground.h \
pgMouseWatcherRegion.I pgMouseWatcherRegion.h \
pgScrollFrame.I pgScrollFrame.h \
pgSliderBar.I pgSliderBar.h \
pgSliderBarNotify.I pgSliderBarNotify.h \
pgTop.I pgTop.h \
pgWaitBar.I pgWaitBar.h \
pgSliderBar.I pgSliderBar.h
pgVirtualFrame.I pgVirtualFrame.h \
pgWaitBar.I pgWaitBar.h
#define INCLUDED_SOURCES \
config_pgui.cxx \
pgButton.cxx \
pgSliderButton.cxx \
pgButtonNotify.cxx \
pgCullTraverser.cxx \
pgEntry.cxx \
pgMouseWatcherGroup.cxx \
pgMouseWatcherParameter.cxx \
pgFrameStyle.cxx \
pgItem.cxx \
pgItemNotify.cxx \
pgMouseWatcherBackground.cxx \
pgMouseWatcherRegion.cxx \
pgScrollFrame.cxx \
pgSliderBar.cxx \
pgSliderBarNotify.cxx \
pgTop.cxx \
pgWaitBar.cxx \
pgSliderBar.cxx
pgVirtualFrame.cxx \
pgWaitBar.cxx
#define INSTALL_HEADERS \
pgButton.I pgButton.h \
pgSliderButton.I pgSliderButton.h \
pgButtonNotify.I pgButtonNotify.h \
pgCullTraverser.I pgCullTraverser.h \
pgEntry.I pgEntry.h \
pgMouseWatcherGroup.I pgMouseWatcherGroup.h \
pgMouseWatcherParameter.I pgMouseWatcherParameter.h \
pgFrameStyle.I pgFrameStyle.h \
pgItem.I pgItem.h \
pgItemNotify.I pgItemNotify.h \
pgMouseWatcherBackground.h \
pgMouseWatcherRegion.I pgMouseWatcherRegion.h \
pgScrollFrame.I pgScrollFrame.h \
pgSliderBar.I pgSliderBar.h \
pgSliderBarNotify.I pgSliderBarNotify.h \
pgTop.I pgTop.h \
pgWaitBar.I pgWaitBar.h \
pgSliderBar.I pgSliderBar.h
pgVirtualFrame.I pgVirtualFrame.h \
pgWaitBar.I pgWaitBar.h
#define IGATESCAN all

View File

@ -18,7 +18,6 @@
#include "config_pgui.h"
#include "pgButton.h"
#include "pgSliderButton.h"
#include "pgCullTraverser.h"
#include "pgEntry.h"
#include "pgMouseWatcherParameter.h"
@ -26,9 +25,11 @@
#include "pgItem.h"
#include "pgMouseWatcherBackground.h"
#include "pgMouseWatcherRegion.h"
#include "pgTop.h"
#include "pgWaitBar.h"
#include "pgScrollFrame.h"
#include "pgSliderBar.h"
#include "pgTop.h"
#include "pgVirtualFrame.h"
#include "pgWaitBar.h"
#include "dconfig.h"
@ -39,6 +40,18 @@ ConfigureFn(config_pgui) {
init_libpgui();
}
ConfigVariableDouble scroll_initial_delay
("scroll-initial-delay", 0.3,
PRC_DESC("This is the amount of time, in seconds, to delay after the user "
"first clicks and holds on a scrollbar button before the scrolling "
"continues automatically."));
ConfigVariableDouble scroll_continued_delay
("scroll-continued-delay", 0.1,
PRC_DESC("This is the amount of time, in seconds, to delay between lines "
"scrolled while the user is continuing to hold down the scrollbar "
"button."));
////////////////////////////////////////////////////////////////////
// Function: init_libpgui
// Description: Initializes the library. This must be called at
@ -56,7 +69,6 @@ init_libpgui() {
initialized = true;
PGButton::init_type();
PGSliderButton::init_type();
PGCullTraverser::init_type();
PGEntry::init_type();
PGMouseWatcherParameter::init_type();
@ -64,7 +76,9 @@ init_libpgui() {
PGItem::init_type();
PGMouseWatcherBackground::init_type();
PGMouseWatcherRegion::init_type();
PGTop::init_type();
PGWaitBar::init_type();
PGScrollFrame::init_type();
PGSliderBar::init_type();
PGTop::init_type();
PGVirtualFrame::init_type();
PGWaitBar::init_type();
}

View File

@ -21,10 +21,13 @@
#include "pandabase.h"
#include "notifyCategoryProxy.h"
#include "configVariableDouble.h"
NotifyCategoryDecl(pgui, EXPCL_PANDA, EXPTP_PANDA);
// Configure variables for pgui package.
extern ConfigVariableDouble scroll_initial_delay;
extern ConfigVariableDouble scroll_continued_delay;
extern EXPCL_PANDA void init_libpgui();

View File

@ -17,6 +17,32 @@
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: PGButton::set_notify
// Access: Published
// Description: Sets the object which will be notified when the
// PGButton changes. Set this to NULL to disable
// this effect. The PGButton does not retain
// ownership of the pointer; it is your responsibility
// to ensure that the notify object does not destruct.
////////////////////////////////////////////////////////////////////
INLINE void PGButton::
set_notify(PGButtonNotify *notify) {
PGItem::set_notify(notify);
}
////////////////////////////////////////////////////////////////////
// Function: PGButton::get_notify
// Access: Published
// Description: Returns the object which will be notified when the
// PGButton changes, if any. Returns NULL if there
// is no such object configured.
////////////////////////////////////////////////////////////////////
INLINE PGButtonNotify *PGButton::
get_notify() const {
return (PGButtonNotify *)PGItem::get_notify();
}
////////////////////////////////////////////////////////////////////
// Function: PGButton::setup
// Access: Published

View File

@ -156,18 +156,10 @@ click(const MouseWatcherParameter &param) {
string event = get_click_event(param.get_button());
play_sound(event);
throw_event(event, EventParameter(ep));
}
////////////////////////////////////////////////////////////////////
// Function: PGButton::move
// Access: Public, Virtual
// Description: This is a callback hook function, called whenever a
// mouse is moved while the mouse
// is within the region.
////////////////////////////////////////////////////////////////////
void PGButton::
move(const MouseWatcherParameter &param) {
PGItem::move(param);
if (has_notify()) {
get_notify()->button_click(this, param);
}
}
////////////////////////////////////////////////////////////////////
@ -180,7 +172,7 @@ move(const MouseWatcherParameter &param) {
// according to the size of the text.
////////////////////////////////////////////////////////////////////
void PGButton::
setup(const string &label) {
setup(const string &label, float bevel) {
clear_state_def(S_ready);
clear_state_def(S_depressed);
clear_state_def(S_rollover);
@ -200,7 +192,7 @@ setup(const string &label) {
PGFrameStyle style;
style.set_color(0.8f, 0.8f, 0.8f, 1.0f);
style.set_width(0.1f, 0.1f);
style.set_width(bevel, bevel);
style.set_type(PGFrameStyle::T_bevel_out);
set_frame_style(S_ready, style);

View File

@ -22,6 +22,7 @@
#include "pandabase.h"
#include "pgItem.h"
#include "pgButtonNotify.h"
#include "nodePath.h"
#include "pset.h"
@ -37,6 +38,9 @@ PUBLISHED:
PGButton(const string &name);
virtual ~PGButton();
protected:
PGButton(const PGButton &copy);
public:
virtual PandaNode *make_copy() const;
@ -46,10 +50,9 @@ public:
virtual void release(const MouseWatcherParameter &param, bool background);
virtual void click(const MouseWatcherParameter &param);
virtual void move(const MouseWatcherParameter &param);
PGButton(const PGButton &copy);
INLINE void set_notify(PGButtonNotify *notify);
INLINE PGButtonNotify *get_notify() const;
PUBLISHED:
enum State {
@ -59,7 +62,7 @@ PUBLISHED:
S_inactive
};
void setup(const string &label);
void setup(const string &label, float bevel = 0.1f);
INLINE void setup(const NodePath &ready);
INLINE void setup(const NodePath &ready, const NodePath &depressed);
INLINE void setup(const NodePath &ready, const NodePath &depressed,

View File

@ -0,0 +1,27 @@
// Filename: pgButtonNotify.I
// Created by: drose (18Aug05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: PGButtonNotify::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE PGButtonNotify::
PGButtonNotify() {
}

View File

@ -0,0 +1,29 @@
// Filename: pgButtonNotify.cxx
// Created by: drose (18Aug05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "pgButtonNotify.h"
#include "pgButton.h"
////////////////////////////////////////////////////////////////////
// Function: PGButtonNotify::button_click
// Access: Protected, Virtual
// Description: Called whenever a watched PGButton has been clicked.
////////////////////////////////////////////////////////////////////
void PGButtonNotify::
button_click(PGButton *, const MouseWatcherParameter &) {
}

View File

@ -0,0 +1,45 @@
// Filename: pgButtonNotify.h
// Created by: drose (18Aug05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef PGBUTTONNOTIFY_H
#define PGBUTTONNOTIFY_H
#include "pandabase.h"
#include "pgItemNotify.h"
class PGButton;
////////////////////////////////////////////////////////////////////
// Class : PGButtonNotify
// Description : Objects that inherit from this class can receive
// notify messages when a slider bar moves or otherwise
// is reconfigured.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA PGButtonNotify : public PGItemNotify {
public:
INLINE PGButtonNotify();
protected:
virtual void button_click(PGButton *button, const MouseWatcherParameter &param);
friend class PGButton;
};
#include "pgButtonNotify.I"
#endif

View File

@ -27,6 +27,7 @@ PGFrameStyle() {
_type = T_none;
_color.set(1.0f, 1.0f, 1.0f, 1.0f);
_width.set(0.1f, 0.1f);
_visible_scale.set(1.0f, 1.0f);
}
////////////////////////////////////////////////////////////////////
@ -39,7 +40,8 @@ PGFrameStyle(const PGFrameStyle &copy) :
_type(copy._type),
_color(copy._color),
_texture(copy._texture),
_width(copy._width)
_width(copy._width),
_visible_scale(copy._visible_scale)
{
}
@ -54,6 +56,7 @@ operator = (const PGFrameStyle &copy) {
_color = copy._color;
_texture = copy._texture;
_width = copy._width;
_visible_scale = copy._visible_scale;
}
////////////////////////////////////////////////////////////////////
@ -197,6 +200,48 @@ get_width() const {
return _width;
}
////////////////////////////////////////////////////////////////////
// Function: PGFrameStyle::set_visible_scale
// Access: Published
// Description: Sets a scale factor on the visible representation of
// the frame, in the X and Y directions. If this scale
// factor is other than 1, it will affect the size of
// the visible frame representation within the actual
// frame border.
////////////////////////////////////////////////////////////////////
INLINE void PGFrameStyle::
set_visible_scale(float x, float y) {
set_visible_scale(LVecBase2f(x, y));
}
////////////////////////////////////////////////////////////////////
// Function: PGFrameStyle::set_visible_scale
// Access: Published
// Description: Sets a scale factor on the visible representation of
// the frame, in the X and Y directions. If this scale
// factor is other than 1, it will affect the size of
// the visible frame representation within the actual
// frame border.
////////////////////////////////////////////////////////////////////
INLINE void PGFrameStyle::
set_visible_scale(const LVecBase2f &visible_scale) {
_visible_scale = visible_scale;
}
////////////////////////////////////////////////////////////////////
// Function: PGFrameStyle::get_visible_scale
// Access: Published
// Description: Returns the scale factor on the visible
// representation of the frame, in the X and Y
// directions. If this scale factor is other than 1, it
// will affect the size of the visible frame
// representation within the actual frame border.
////////////////////////////////////////////////////////////////////
INLINE const LVecBase2f &PGFrameStyle::
get_visible_scale() const {
return _visible_scale;
}
////////////////////////////////////////////////////////////////////
// Function: PGFrameStyle ostream output
// Description:

View File

@ -60,6 +60,39 @@ operator << (ostream &out, PGFrameStyle::Type type) {
return out << "**unknown(" << (int)type << ")**";
}
////////////////////////////////////////////////////////////////////
// Function: PGFrameStyle::get_internal_frame
// Access: Published
// Description: Computes the size of the internal frame, given the
// indicated external frame, appropriate for this kind
// of frame style. This simply subtracts the border
// width for those frame styles that include a border.
////////////////////////////////////////////////////////////////////
LVecBase4f PGFrameStyle::
get_internal_frame(const LVecBase4f &frame) const {
LPoint2f center((frame[0] + frame[1]) / 2.0f,
(frame[2] + frame[3]) / 2.0f);
LVecBase4f scaled_frame
((frame[0] - center[0]) * _visible_scale[0] + center[0],
(frame[1] - center[0]) * _visible_scale[0] + center[0],
(frame[2] - center[1]) * _visible_scale[1] + center[1],
(frame[3] - center[1]) * _visible_scale[1] + center[1]);
switch (_type) {
case T_none:
case T_flat:
return scaled_frame;
default:
break;
}
return LVecBase4f(scaled_frame[0] + _width[0],
scaled_frame[1] - _width[0],
scaled_frame[2] + _width[1],
scaled_frame[3] - _width[1]);
}
////////////////////////////////////////////////////////////////////
// Function: PGFrameStyle::output
// Access: Published
@ -68,6 +101,9 @@ operator << (ostream &out, PGFrameStyle::Type type) {
void PGFrameStyle::
output(ostream &out) const {
out << _type << " color = " << _color << " width = " << _width;
if (_visible_scale != LVecBase2f(1.0f, 1.0f)) {
out << "visible_scale = " << get_visible_scale();
}
if (has_texture()) {
out << " texture = " << *get_texture();
}
@ -126,28 +162,36 @@ NodePath PGFrameStyle::
generate_into(const NodePath &parent, const LVecBase4f &frame) {
PT(PandaNode) new_node;
LPoint2f center((frame[0] + frame[1]) / 2.0f,
(frame[2] + frame[3]) / 2.0f);
LVecBase4f scaled_frame
((frame[0] - center[0]) * _visible_scale[0] + center[0],
(frame[1] - center[0]) * _visible_scale[0] + center[0],
(frame[2] - center[1]) * _visible_scale[1] + center[1],
(frame[3] - center[1]) * _visible_scale[1] + center[1]);
switch (_type) {
case T_none:
return NodePath();
case T_flat:
new_node = generate_flat_geom(frame);
new_node = generate_flat_geom(scaled_frame);
break;
case T_bevel_out:
new_node = generate_bevel_geom(frame, false);
new_node = generate_bevel_geom(scaled_frame, false);
break;
case T_bevel_in:
new_node = generate_bevel_geom(frame, true);
new_node = generate_bevel_geom(scaled_frame, true);
break;
case T_groove:
new_node = generate_groove_geom(frame, true);
new_node = generate_groove_geom(scaled_frame, true);
break;
case T_ridge:
new_node = generate_groove_geom(frame, false);
new_node = generate_groove_geom(scaled_frame, false);
break;
default:

View File

@ -65,6 +65,12 @@ PUBLISHED:
INLINE void set_width(const LVecBase2f &width);
INLINE const LVecBase2f &get_width() const;
INLINE void set_visible_scale(float x, float y);
INLINE void set_visible_scale(const LVecBase2f &visible_scale);
INLINE const LVecBase2f &get_visible_scale() const;
LVecBase4f get_internal_frame(const LVecBase4f &frame) const;
void output(ostream &out) const;
public:
@ -81,6 +87,7 @@ private:
Colorf _color;
PT(Texture) _texture;
LVecBase2f _width;
LVecBase2f _visible_scale;
};
INLINE ostream &operator << (ostream &out, const PGFrameStyle &pfs);

View File

@ -33,6 +33,49 @@ get_region() const {
return _region;
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::set_notify
// Access: Published
// Description: Sets the object which will be notified when the
// PGItem changes. Set this to NULL to disable
// this effect. The PGItem does not retain
// ownership of the pointer; it is your responsibility
// to ensure that the notify object does not destruct.
////////////////////////////////////////////////////////////////////
INLINE void PGItem::
set_notify(PGItemNotify *notify) {
if (_notify != (PGItemNotify *)NULL) {
_notify->remove_item(this);
}
_notify = notify;
if (_notify != (PGItemNotify *)NULL) {
_notify->add_item(this);
}
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::has_notify
// Access: Published
// Description: Returns true if there is an object configured to be
// notified when the PGItem changes, false otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool PGItem::
has_notify() const {
return (_notify != (PGItemNotify *)NULL);
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::get_notify
// Access: Published
// Description: Returns the object which will be notified when the
// PGItem changes, if any. Returns NULL if there
// is no such object configured.
////////////////////////////////////////////////////////////////////
INLINE PGItemNotify *PGItem::
get_notify() const {
return _notify;
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::set_frame
// Access: Published
@ -58,9 +101,11 @@ set_frame(float left, float right, float bottom, float top) {
////////////////////////////////////////////////////////////////////
INLINE void PGItem::
set_frame(const LVecBase4f &frame) {
_has_frame = true;
_frame = frame;
mark_frames_stale();
if (!_has_frame || _frame != frame) {
_has_frame = true;
_frame = frame;
frame_changed();
}
}
////////////////////////////////////////////////////////////////////
@ -96,8 +141,10 @@ has_frame() const {
////////////////////////////////////////////////////////////////////
INLINE void PGItem::
clear_frame() {
_has_frame = false;
mark_frames_stale();
if (_has_frame) {
_has_frame = false;
frame_changed();
}
}
////////////////////////////////////////////////////////////////////
@ -476,3 +523,32 @@ INLINE LMatrix4f PGItem::
get_frame_inv_xform() const {
return _frame_inv_xform;
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::compute_area
// Access: Private, Static
// Description: Computes the area of the indicated frame.
////////////////////////////////////////////////////////////////////
INLINE float PGItem::
compute_area(const LVecBase4f &frame) {
return (frame[1] - frame[0]) * (frame[3] - frame[2]);
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::compare_largest
// Access: Private, Static
// Description: Given that largest is the pointer to the largest
// frame so far, and largest_area is its area, compare
// that to the area of the new frame; if the new frame
// is larger, adjust largest and largest_area
// appropriately.
////////////////////////////////////////////////////////////////////
INLINE void PGItem::
compare_largest(const LVecBase4f *&largest, float &largest_area,
const LVecBase4f *new_frame) {
float new_area = compute_area(*new_frame);
if (new_area > largest_area) {
largest = new_frame;
largest_area = new_area;
}
}

View File

@ -20,7 +20,7 @@
#include "pgMouseWatcherParameter.h"
#include "pgCullTraverser.h"
#include "config_pgui.h"
#include "boundingVolume.h"
#include "pandaNode.h"
#include "sceneGraphReducer.h"
#include "throw_event.h"
@ -29,6 +29,7 @@
#include "cullTraverser.h"
#include "cullTraverserData.h"
#include "cullBinManager.h"
#include "clipPlaneAttrib.h"
#include "dcast.h"
#ifdef HAVE_AUDIO
@ -40,6 +41,16 @@ PT(TextNode) PGItem::_text_node;
PGItem *PGItem::_focus_item = (PGItem *)NULL;
PGItem::BackgroundFocus PGItem::_background_focus;
////////////////////////////////////////////////////////////////////
// Function: is_right
// Description: Returns true if the 2-d v1 is to the right of v2.
////////////////////////////////////////////////////////////////////
INLINE bool
is_right(const LVector2f &v1, const LVector2f &v2) {
return (-v1[0] * v2[1] + v1[1] * v2[0]) > 0;
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::Constructor
// Access: Published
@ -49,6 +60,7 @@ PGItem::
PGItem(const string &name) :
PandaNode(name)
{
_notify = NULL;
_has_frame = false;
_frame.set(0, 0, 0, 0);
_region = new PGMouseWatcherRegion(this);
@ -63,6 +75,11 @@ PGItem(const string &name) :
////////////////////////////////////////////////////////////////////
PGItem::
~PGItem() {
if (_notify != (PGItemNotify *)NULL) {
_notify->remove_item(this);
_notify = NULL;
}
nassertv(_region->_item == this);
_region->_item = (PGItem *)NULL;
@ -85,6 +102,7 @@ PGItem(const PGItem &copy) :
_state(copy._state),
_flags(copy._flags)
{
_notify = NULL;
_region = new PGMouseWatcherRegion(this);
}
@ -102,38 +120,33 @@ make_copy() const {
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::xform
// Function: PGItem::transform_changed
// Access: Protected, Virtual
// Description: Transforms the contents of this node by the indicated
// matrix, if it means anything to do so. For most
// kinds of nodes, this does nothing.
// Description: Called after the node's transform has been changed
// for any reason, this just provides a hook so derived
// classes can do something special in this case.
////////////////////////////////////////////////////////////////////
void PGItem::
xform(const LMatrix4f &mat) {
// Transform the frame.
LPoint3f ll(_frame[0], 0.0f, _frame[2]);
LPoint3f ur(_frame[1], 0.0f, _frame[3]);
ll = ll * mat;
ur = ur * mat;
_frame.set(ll[0], ur[0], ll[2], ur[2]);
// Transform the individual states and their frame styles.
StateDefs::iterator di;
for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
NodePath &root = (*di)._root;
// Apply the matrix to the previous transform.
root.set_transform(root.get_transform()->compose(TransformState::make_mat(mat)));
// Now flatten the transform into the subgraph.
SceneGraphReducer gr;
gr.apply_attribs(root.node());
// Transform the frame style too.
if ((*di)._frame_style.xform(mat)) {
(*di)._frame_stale = true;
}
transform_changed() {
PandaNode::transform_changed();
if (has_notify()) {
get_notify()->item_transform_changed(this);
}
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::draw_mask_changed
// Access: Protected, Virtual
// Description: Called after the node's draw_mask has been changed
// for any reason, this just provides a hook so derived
// classes can do something special in this case.
////////////////////////////////////////////////////////////////////
void PGItem::
draw_mask_changed() {
PandaNode::draw_mask_changed();
if (has_notify()) {
get_notify()->item_draw_mask_changed(this);
}
mark_bound_stale();
}
////////////////////////////////////////////////////////////////////
@ -221,8 +234,9 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
// which only provides one.
sort = (bin_sort << 16) | ((sort + 0x8000) & 0xffff);
activate_region(transform, sort);
pg_trav->_top->add_region(get_region());
if (activate_region(transform, sort, data._state->get_clip_plane())) {
pg_trav->_top->add_region(get_region());
}
}
}
@ -273,14 +287,53 @@ recompute_internal_bound() {
return bound;
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::xform
// Access: Public, Virtual
// Description: Transforms the contents of this node by the indicated
// matrix, if it means anything to do so. For most
// kinds of nodes, this does nothing.
////////////////////////////////////////////////////////////////////
void PGItem::
xform(const LMatrix4f &mat) {
// Transform the frame.
LPoint3f ll(_frame[0], 0.0f, _frame[2]);
LPoint3f ur(_frame[1], 0.0f, _frame[3]);
ll = ll * mat;
ur = ur * mat;
_frame.set(ll[0], ur[0], ll[2], ur[2]);
// Transform the individual states and their frame styles.
StateDefs::iterator di;
for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
NodePath &root = (*di)._root;
// Apply the matrix to the previous transform.
root.set_transform(root.get_transform()->compose(TransformState::make_mat(mat)));
// Now flatten the transform into the subgraph.
SceneGraphReducer gr;
gr.apply_attribs(root.node());
// Transform the frame style too.
if ((*di)._frame_style.xform(mat)) {
(*di)._frame_stale = true;
}
}
mark_bound_stale();
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::activate_region
// Access: Public
// Description: Applies the indicated scene graph transform and order
// as determined by the traversal from PGTop.
//
// The return value is true if the region is valid, or
// false if it is empty or completely clipped.
////////////////////////////////////////////////////////////////////
void PGItem::
activate_region(const LMatrix4f &transform, int sort) {
bool PGItem::
activate_region(const LMatrix4f &transform, int sort,
const ClipPlaneAttrib *cpa) {
// Transform all four vertices, and get the new bounding box. This
// way the region works (mostly) even if has been rotated.
LPoint3f ll(_frame[0], 0.0f, _frame[2]);
@ -291,10 +344,54 @@ activate_region(const LMatrix4f &transform, int sort) {
lr = lr * transform;
ul = ul * transform;
ur = ur * transform;
_region->set_frame(min(min(ll[0], lr[0]), min(ul[0], ur[0])),
max(max(ll[0], lr[0]), max(ul[0], ur[0])),
min(min(ll[2], lr[2]), min(ul[2], ur[2])),
max(max(ll[2], lr[2]), max(ul[2], ur[2])));
if (cpa != (ClipPlaneAttrib *)NULL && cpa->get_num_on_planes() != 0) {
// Apply the clip plane(s) now that we are here in world space.
pvector<LPoint2f> points;
points.reserve(4);
points.push_back(LPoint2f(ll[0], ll[2]));
points.push_back(LPoint2f(lr[0], lr[2]));
points.push_back(LPoint2f(ur[0], ur[2]));
points.push_back(LPoint2f(ul[0], ul[2]));
int num_on_planes = cpa->get_num_on_planes();
for (int i = 0; i < num_on_planes; ++i) {
NodePath plane_path = cpa->get_on_plane(i);
Planef plane = DCAST(PlaneNode, plane_path.node())->get_plane();
plane.xform(plane_path.get_net_transform()->get_mat());
// We ignore the y coordinate, assuming the frame is still in
// the X-Z plane after being transformed. Not sure if we really
// need to support general 3-D transforms on 2-D objects.
clip_frame(points, plane);
}
if (points.empty()) {
// Turns out it's completely clipped after all.
return false;
}
pvector<LPoint2f>::iterator pi;
pi = points.begin();
LVecBase4f frame((*pi)[0], (*pi)[0], (*pi)[1], (*pi)[1]);
++pi;
while (pi != points.end()) {
frame[0] = min(frame[0], (*pi)[0]);
frame[1] = max(frame[1], (*pi)[0]);
frame[2] = min(frame[2], (*pi)[1]);
frame[3] = max(frame[3], (*pi)[1]);
++pi;
}
_region->set_frame(frame);
} else {
// Since there are no clip planes involved, just set the frame.
_region->set_frame(min(min(ll[0], lr[0]), min(ul[0], ur[0])),
max(max(ll[0], lr[0]), max(ul[0], ur[0])),
min(min(ll[2], lr[2]), min(ul[2], ur[2])),
max(max(ll[2], lr[2]), max(ul[2], ur[2])));
}
_region->set_sort(sort);
_region->set_active(true);
@ -302,6 +399,8 @@ activate_region(const LMatrix4f &transform, int sort) {
// calculate the inverse of this transform, which is needed to
// go back to the frame space.
_frame_inv_xform.invert_from(transform);
return true;
}
////////////////////////////////////////////////////////////////////
@ -319,6 +418,11 @@ enter(const MouseWatcherParameter &param) {
string event = get_enter_event();
play_sound(event);
throw_event(event, EventParameter(ep));
if (has_notify()) {
get_notify()->item_enter(this, param);
}
//pgui_cat.info() << get_name() << "::enter()" << endl;
}
@ -337,6 +441,11 @@ exit(const MouseWatcherParameter &param) {
string event = get_exit_event();
play_sound(event);
throw_event(event, EventParameter(ep));
if (has_notify()) {
get_notify()->item_exit(this, param);
}
//pgui_cat.info() << get_name() << "::exit()" << endl;
}
@ -356,6 +465,11 @@ within(const MouseWatcherParameter &param) {
string event = get_within_event();
play_sound(event);
throw_event(event, EventParameter(ep));
if (has_notify()) {
get_notify()->item_within(this, param);
}
//pgui_cat.info() << get_name() << "::within()" << endl;
}
@ -372,6 +486,11 @@ without(const MouseWatcherParameter &param) {
string event = get_without_event();
play_sound(event);
throw_event(event, EventParameter(ep));
if (has_notify()) {
get_notify()->item_without(this, param);
}
//pgui_cat.info() << get_name() << "::without()" << endl;
}
@ -386,6 +505,11 @@ focus_in() {
string event = get_focus_in_event();
play_sound(event);
throw_event(event);
if (has_notify()) {
get_notify()->item_focus_in(this);
}
//pgui_cat.info() << get_name() << "::focus_in()" << endl;
}
@ -400,6 +524,11 @@ focus_out() {
string event = get_focus_out_event();
play_sound(event);
throw_event(event);
if (has_notify()) {
get_notify()->item_focus_out(this);
}
//pgui_cat.info() << get_name() << "::focus_out()" << endl;
}
@ -418,6 +547,11 @@ press(const MouseWatcherParameter &param, bool background) {
play_sound(event);
throw_event(event, EventParameter(ep));
}
if (has_notify()) {
get_notify()->item_press(this, param);
}
//pgui_cat.info() << get_name() << "::press()" << endl;
}
@ -436,6 +570,11 @@ release(const MouseWatcherParameter &param, bool background) {
play_sound(event);
throw_event(event, EventParameter(ep));
}
if (has_notify()) {
get_notify()->item_release(this, param);
}
//pgui_cat.info() << get_name() << "::release()" << endl;
}
@ -452,6 +591,10 @@ keystroke(const MouseWatcherParameter &param, bool background) {
string event = get_keystroke_event();
play_sound(event);
throw_event(event, EventParameter(ep));
if (has_notify()) {
get_notify()->item_keystroke(this, param);
}
}
}
@ -464,6 +607,11 @@ keystroke(const MouseWatcherParameter &param, bool background) {
void PGItem::
candidate(const MouseWatcherParameter &param, bool background) {
// We don't throw sound events for candidate selections for now.
if (!background) {
if (has_notify()) {
get_notify()->item_candidate(this, param);
}
}
}
////////////////////////////////////////////////////////////////////
@ -474,11 +622,9 @@ candidate(const MouseWatcherParameter &param, bool background) {
////////////////////////////////////////////////////////////////////
void PGItem::
move(const MouseWatcherParameter &param) {
PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
string event = get_press_event(param.get_button());
play_sound(event);
throw_event(event, EventParameter(ep));
//pgui_cat.info() << get_name() << "::move()" << endl;
if (has_notify()) {
get_notify()->item_move(this, param);
}
}
////////////////////////////////////////////////////////////////////
@ -861,6 +1007,109 @@ play_sound(const string &event) {
#endif // HAVE_AUDIO
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::reduce_region
// Access: Protected
// Description: The frame parameter is an in/out parameter. This
// function adjusts frame so that it represents the
// largest part of the rectangular region passed in,
// that does not overlap with the rectangular region of
// the indicated obscurer. If the obscurer is NULL, or
// is a hidden node, it is not considered and the frame
// is left unchanged.
//
// This is used by slider bars and scroll frames, which
// have to automatically figure out how much space they
// have to work with after allowing space for scroll
// bars and buttons.
////////////////////////////////////////////////////////////////////
void PGItem::
reduce_region(LVecBase4f &frame, PGItem *obscurer) const {
if (obscurer != (PGItem *)NULL && !obscurer->get_draw_mask().is_zero()) {
LVecBase4f oframe = get_relative_frame(obscurer);
// Determine the four rectangular regions on the four sides of the
// obscuring region.
LVecBase4f right(max(frame[0], oframe[1]), frame[1], frame[2], frame[3]);
LVecBase4f left(frame[0], min(frame[1], oframe[0]), frame[2], frame[3]);
LVecBase4f above(frame[0], frame[1], max(frame[2], oframe[3]), frame[3]);
LVecBase4f below(frame[0], frame[1], frame[2], min(frame[3], oframe[2]));
// Now choose the largest of those four.
const LVecBase4f *largest = &right;
float largest_area = compute_area(*largest);
compare_largest(largest, largest_area, &left);
compare_largest(largest, largest_area, &above);
compare_largest(largest, largest_area, &below);
frame = *largest;
}
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::get_relative_frame
// Access: Protected
// Description: Returns the LVecBase4f frame of the indicated item,
// converted into this item's coordinate space.
// Presumably, item is a child of this node.
////////////////////////////////////////////////////////////////////
LVecBase4f PGItem::
get_relative_frame(PGItem *item) const {
NodePath this_np = NodePath::any_path((PGItem *)this);
NodePath item_np = this_np.find_path_to(item);
if (item_np.is_empty()) {
item_np = NodePath::any_path(item);
}
const LVecBase4f &orig_frame = item->get_frame();
LMatrix4f transform = item_np.get_mat(this_np);
// Transform the item's frame into the PGScrollFrame's
// coordinate space. Transform all four vertices, and get the
// new bounding box. This way the region works (mostly) even if
// has been rotated.
LPoint3f ll(orig_frame[0], 0.0f, orig_frame[2]);
LPoint3f lr(orig_frame[1], 0.0f, orig_frame[2]);
LPoint3f ul(orig_frame[0], 0.0f, orig_frame[3]);
LPoint3f ur(orig_frame[1], 0.0f, orig_frame[3]);
ll = ll * transform;
lr = lr * transform;
ul = ul * transform;
ur = ur * transform;
return LVecBase4f(min(min(ll[0], lr[0]), min(ul[0], ur[0])),
max(max(ll[0], lr[0]), max(ul[0], ur[0])),
min(min(ll[2], lr[2]), min(ul[2], ur[2])),
max(max(ll[2], lr[2]), max(ul[2], ur[2])));
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::mouse_to_local
// Access: Protected
// Description: Converts from the 2-d mouse coordinates into the
// coordinate space of the item.
////////////////////////////////////////////////////////////////////
LPoint3f PGItem::
mouse_to_local(const LPoint2f &mouse_point) const {
// This is ambiguous if the PGItem has multiple instances. Why
// would you do that, anyway?
NodePath this_np((PGItem *)this);
CPT(TransformState) inv_transform = NodePath().get_transform(this_np);
return inv_transform->get_mat().xform_point(LVector3f::rfu(mouse_point[0], 0, mouse_point[1]));
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::frame_changed
// Access: Protected, Virtual
// Description: Called when the user changes the frame size.
////////////////////////////////////////////////////////////////////
void PGItem::
frame_changed() {
mark_frames_stale();
if (has_notify()) {
get_notify()->item_frame_changed(this);
}
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::slot_state_def
// Access: Private
@ -919,3 +1168,91 @@ mark_frames_stale() {
}
mark_bound_stale();
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::clip_frame
// Access: Private
// Description: Clips the four corners of the item's frame by the
// indicated clipping plane, and modifies the points to
// reflect the new set of clipped points.
//
// The return value is true if the set of points is
// unmodified (all points are behind the clip plane), or
// false otherwise.
////////////////////////////////////////////////////////////////////
bool PGItem::
clip_frame(pvector<LPoint2f> &source_points, const Planef &plane) const {
if (source_points.empty()) {
return true;
}
LPoint3f from3d;
LVector3f delta3d;
if (!plane.intersects_plane(from3d, delta3d, Planef(LVector3f(0, 1, 0), LPoint3f::zero()))) {
// The clipping plane is parallel to the polygon. The polygon is
// either all in or all out.
if (plane.dist_to_plane(LPoint3f::zero()) < 0.0) {
// A point within the polygon is behind the clipping plane: the
// polygon is all in.
return true;
}
return false;
}
// Project the line of intersection into the X-Z plane. Now we have
// a 2-d clipping line.
LPoint2f from2d(from3d[0], from3d[2]);
LVector2f delta2d(delta3d[0], delta3d[2]);
float a = -delta2d[1];
float b = delta2d[0];
float c = from2d[0] * delta2d[1] - from2d[1] * delta2d[0];
// Now walk through the points. Any point on the left of our line
// gets removed, and the line segment clipped at the point of
// intersection.
// We might increase the number of vertices by as many as 1, if the
// plane clips off exactly one corner. (We might also decrease the
// number of vertices, or keep them the same number.)
pvector<LPoint2f> new_points;
new_points.reserve(source_points.size() + 1);
LPoint2f last_point = source_points.back();
bool last_is_in = !is_right(last_point - from2d, delta2d);
bool all_in = last_is_in;
pvector<LPoint2f>::const_iterator pi;
for (pi = source_points.begin(); pi != source_points.end(); ++pi) {
const LPoint2f &this_point = (*pi);
bool this_is_in = !is_right(this_point - from2d, delta2d);
// There appears to be a compiler bug in gcc 4.0: we need to
// extract this comparison outside of the if statement.
bool crossed_over = (this_is_in != last_is_in);
if (crossed_over) {
// We have just crossed over the clipping line. Find the point
// of intersection.
LVector2f d = this_point - last_point;
float denom = (a * d[0] + b * d[1]);
if (denom != 0.0) {
float t = -(a * last_point[0] + b * last_point[1] + c) / denom;
LPoint2f p = last_point + t * d;
new_points.push_back(p);
last_is_in = this_is_in;
}
}
if (this_is_in) {
// We are behind the clipping line. Keep the point.
new_points.push_back(this_point);
} else {
all_in = false;
}
last_point = this_point;
}
source_points.swap(new_points);
return all_in;
}

View File

@ -23,6 +23,7 @@
#include "pgMouseWatcherRegion.h"
#include "pgFrameStyle.h"
#include "pgItemNotify.h"
#include "pandaNode.h"
#include "nodePath.h"
@ -30,12 +31,13 @@
#include "pointerTo.h"
#include "audioSound.h"
#include "textNode.h"
#include "plane.h"
#include "pmap.h"
class PGTop;
class MouseWatcherParameter;
class AudioSound;
class ClipPlaneAttrib;
////////////////////////////////////////////////////////////////////
// Class : PGItem
@ -60,14 +62,18 @@ protected:
PGItem(const PGItem &copy);
virtual PandaNode *make_copy() const;
virtual void xform(const LMatrix4f &mat);
virtual void transform_changed();
virtual void draw_mask_changed();
virtual bool has_cull_callback() const;
virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
virtual BoundingVolume *recompute_internal_bound();
public:
void activate_region(const LMatrix4f &transform, int sort);
virtual void xform(const LMatrix4f &mat);
bool activate_region(const LMatrix4f &transform, int sort,
const ClipPlaneAttrib *cpa);
INLINE PGMouseWatcherRegion *get_region() const;
virtual void enter(const MouseWatcherParameter &param);
@ -87,6 +93,10 @@ public:
static void background_keystroke(const MouseWatcherParameter &param);
static void background_candidate(const MouseWatcherParameter &param);
INLINE void set_notify(PGItemNotify *notify);
INLINE bool has_notify() const;
INLINE PGItemNotify *get_notify() const;
PUBLISHED:
INLINE void set_frame(float left, float right, float bottom, float top);
INLINE void set_frame(const LVecBase4f &frame);
@ -158,11 +168,28 @@ PUBLISHED:
protected:
void play_sound(const string &event);
void reduce_region(LVecBase4f &clip, PGItem *obscurer) const;
void reduce_region(LVecBase4f &frame, float px, float py) const;
LVecBase4f get_relative_frame(PGItem *item) const;
LPoint3f mouse_to_local(const LPoint2f &mouse_point) const;
virtual void frame_changed();
private:
void slot_state_def(int state);
void update_frame(int state);
void mark_frames_stale();
INLINE static float compute_area(const LVecBase4f &frame);
INLINE static void compare_largest(const LVecBase4f *&largest,
float &largest_area,
const LVecBase4f *new_frame);
bool clip_frame(pvector<LPoint2f> &source_points, const Planef &plane) const;
private:
PGItemNotify *_notify;
bool _has_frame;
LVecBase4f _frame;
int _state;

View File

@ -0,0 +1,27 @@
// Filename: pgItemNotify.I
// Created by: drose (18Aug05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: PGItemNotify::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE PGItemNotify::
PGItemNotify() {
}

View File

@ -0,0 +1,202 @@
// Filename: pgItemNotify.cxx
// Created by: drose (18Aug05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "pgItemNotify.h"
#include "pgItem.h"
////////////////////////////////////////////////////////////////////
// Function: PGItemNotify::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
PGItemNotify::
~PGItemNotify() {
while (!_items.empty()) {
// Disconnect all of the items that are connected to this
// object.
PGItem *item = (*_items.begin());
nassertv(item->get_notify() == this);
(*_items.begin())->set_notify(NULL);
}
}
////////////////////////////////////////////////////////////////////
// Function: PGItemNotify::item_transform_changed
// Access: Protected, Virtual
// Description: Called whenever a watched PGItem's local transform
// has been changed.
////////////////////////////////////////////////////////////////////
void PGItemNotify::
item_transform_changed(PGItem *) {
}
////////////////////////////////////////////////////////////////////
// Function: PGItemNotify::item_frame_changed
// Access: Protected, Virtual
// Description: Called whenever a watched PGItem's frame
// has been changed.
////////////////////////////////////////////////////////////////////
void PGItemNotify::
item_frame_changed(PGItem *) {
}
////////////////////////////////////////////////////////////////////
// Function: PGItemNotify::item_draw_mask_changed
// Access: Protected, Virtual
// Description: Called whenever a watched PGItem's draw_mask
// has been changed.
////////////////////////////////////////////////////////////////////
void PGItemNotify::
item_draw_mask_changed(PGItem *) {
}
////////////////////////////////////////////////////////////////////
// Function: PGItemNotify::item_enter
// Access: Protected, Virtual
// Description: Called whenever the "enter" event is triggered on a
// watched PGItem. See PGItem::enter().
////////////////////////////////////////////////////////////////////
void PGItemNotify::
item_enter(PGItem *, const MouseWatcherParameter &) {
}
////////////////////////////////////////////////////////////////////
// Function: PGItemNotify::item_exit
// Access: Protected, Virtual
// Description: Called whenever the "exit" event is triggered on a
// watched PGItem. See PGItem::exit().
////////////////////////////////////////////////////////////////////
void PGItemNotify::
item_exit(PGItem *, const MouseWatcherParameter &) {
}
////////////////////////////////////////////////////////////////////
// Function: PGItemNotify::item_within
// Access: Protected, Virtual
// Description: Called whenever the "within" event is triggered on a
// watched PGItem. See PGItem::within().
////////////////////////////////////////////////////////////////////
void PGItemNotify::
item_within(PGItem *, const MouseWatcherParameter &) {
}
////////////////////////////////////////////////////////////////////
// Function: PGItemNotify::item_without
// Access: Protected, Virtual
// Description: Called whenever the "without" event is triggered on a
// watched PGItem. See PGItem::without().
////////////////////////////////////////////////////////////////////
void PGItemNotify::
item_without(PGItem *, const MouseWatcherParameter &) {
}
////////////////////////////////////////////////////////////////////
// Function: PGItemNotify::item_focus_in
// Access: Protected, Virtual
// Description: Called whenever the "focus_in" event is triggered on a
// watched PGItem. See PGItem::focus_in().
////////////////////////////////////////////////////////////////////
void PGItemNotify::
item_focus_in(PGItem *) {
}
////////////////////////////////////////////////////////////////////
// Function: PGItemNotify::item_focus_out
// Access: Protected, Virtual
// Description: Called whenever the "focus_out" event is triggered on a
// watched PGItem. See PGItem::focus_out().
////////////////////////////////////////////////////////////////////
void PGItemNotify::
item_focus_out(PGItem *) {
}
////////////////////////////////////////////////////////////////////
// Function: PGItemNotify::item_press
// Access: Protected, Virtual
// Description: Called whenever the "press" event is triggered on a
// watched PGItem. See PGItem::press().
////////////////////////////////////////////////////////////////////
void PGItemNotify::
item_press(PGItem *, const MouseWatcherParameter &) {
}
////////////////////////////////////////////////////////////////////
// Function: PGItemNotify::item_release
// Access: Protected, Virtual
// Description: Called whenever the "release" event is triggered on a
// watched PGItem. See PGItem::release().
////////////////////////////////////////////////////////////////////
void PGItemNotify::
item_release(PGItem *, const MouseWatcherParameter &) {
}
////////////////////////////////////////////////////////////////////
// Function: PGItemNotify::item_keystroke
// Access: Protected, Virtual
// Description: Called whenever the "keystroke" event is triggered on a
// watched PGItem. See PGItem::keystroke().
////////////////////////////////////////////////////////////////////
void PGItemNotify::
item_keystroke(PGItem *, const MouseWatcherParameter &) {
}
////////////////////////////////////////////////////////////////////
// Function: PGItemNotify::item_candidate
// Access: Protected, Virtual
// Description: Called whenever the "candidate" event is triggered on a
// watched PGItem. See PGItem::candidate().
////////////////////////////////////////////////////////////////////
void PGItemNotify::
item_candidate(PGItem *, const MouseWatcherParameter &) {
}
////////////////////////////////////////////////////////////////////
// Function: PGItemNotify::item_move
// Access: Protected, Virtual
// Description: Called whenever the "move" event is triggered on a
// watched PGItem. See PGItem::move().
////////////////////////////////////////////////////////////////////
void PGItemNotify::
item_move(PGItem *, const MouseWatcherParameter &) {
}
////////////////////////////////////////////////////////////////////
// Function: PGItemNotify::add_item
// Access: Protected, Virtual
// Description: Called by PGItem when a new item is set up to
// notify this object.
////////////////////////////////////////////////////////////////////
void PGItemNotify::
add_item(PGItem *item) {
bool inserted = _items.insert(item).second;
nassertv(inserted);
}
////////////////////////////////////////////////////////////////////
// Function: PGItemNotify::remove_item
// Access: Protected, Virtual
// Description: Called by PGItem when an item is no longer set up
// to notify this object.
////////////////////////////////////////////////////////////////////
void PGItemNotify::
remove_item(PGItem *item) {
Items::iterator bi;
bi = _items.find(item);
nassertv(bi != _items.end());
_items.erase(bi);
}

View File

@ -0,0 +1,67 @@
// Filename: pgItemNotify.h
// Created by: drose (18Aug05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef PGITEMNOTIFY_H
#define PGITEMNOTIFY_H
#include "pandabase.h"
class PGItem;
////////////////////////////////////////////////////////////////////
// Class : PGItemNotify
// Description : Objects that inherit from this class can receive
// specialized messages when PGItems change in certain
// ways.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA PGItemNotify {
public:
INLINE PGItemNotify();
virtual ~PGItemNotify();
protected:
virtual void item_transform_changed(PGItem *item);
virtual void item_frame_changed(PGItem *item);
virtual void item_draw_mask_changed(PGItem *item);
virtual void item_enter(PGItem *item, const MouseWatcherParameter &param);
virtual void item_exit(PGItem *item, const MouseWatcherParameter &param);
virtual void item_within(PGItem *item, const MouseWatcherParameter &param);
virtual void item_without(PGItem *item, const MouseWatcherParameter &param);
virtual void item_focus_in(PGItem *item);
virtual void item_focus_out(PGItem *item);
virtual void item_press(PGItem *item, const MouseWatcherParameter &param);
virtual void item_release(PGItem *item, const MouseWatcherParameter &param);
virtual void item_keystroke(PGItem *item, const MouseWatcherParameter &param);
virtual void item_candidate(PGItem *item, const MouseWatcherParameter &param);
virtual void item_move(PGItem *item, const MouseWatcherParameter &param);
protected:
void add_item(PGItem *item);
void remove_item(PGItem *item);
private:
typedef pset<PGItem *> Items;
Items _items;
friend class PGItem;
};
#include "pgItemNotify.I"
#endif

View File

@ -0,0 +1,245 @@
// Filename: pgScrollFrame.I
// Created by: drose (17Aug05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::set_virtual_frame
// Access: Published
// Description: Sets the bounding rectangle of the virtual frame.
// This is the size of the large, virtual canvas which
// we can see only a portion of at any given time.
////////////////////////////////////////////////////////////////////
INLINE void PGScrollFrame::
set_virtual_frame(float left, float right, float bottom, float top) {
set_virtual_frame(LVecBase4f(left, right, bottom, top));
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::set_virtual_frame
// Access: Published
// Description: Sets the bounding rectangle of the virtual frame.
// This is the size of the large, virtual canvas which
// we can see only a portion of at any given time.
////////////////////////////////////////////////////////////////////
INLINE void PGScrollFrame::
set_virtual_frame(const LVecBase4f &frame) {
_has_virtual_frame = true;
_virtual_frame = frame;
if (_auto_hide) {
_needs_remanage = true;
}
_needs_recompute_clip = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::get_virtual_frame
// Access: Published
// Description: Returns the bounding rectangle of the virtual frame.
// See set_virtual_frame(). If has_virtual_frame() is
// false, this returns the item's clip frame.
////////////////////////////////////////////////////////////////////
INLINE const LVecBase4f &PGScrollFrame::
get_virtual_frame() const {
return _has_virtual_frame ? _virtual_frame : get_clip_frame();
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::has_virtual_frame
// Access: Published
// Description: Returns true if the virtual frame has a bounding
// rectangle; see set_virtual_frame(). Most
// PGScrollFrame objects will have a virtual frame.
////////////////////////////////////////////////////////////////////
INLINE bool PGScrollFrame::
has_virtual_frame() const {
return _has_virtual_frame;
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::clear_virtual_frame
// Access: Published
// Description: Removes the virtual frame from the item. This
// effectively sets the virtual frame to the same size
// as the clip frame. Scrolling will no longer be
// possible.
////////////////////////////////////////////////////////////////////
INLINE void PGScrollFrame::
clear_virtual_frame() {
_has_virtual_frame = false;
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::set_manage_pieces
// Access: Published
// Description: Sets the manage_pieces flag. When this is true, the
// sub-pieces of the scroll frame--that is, the two
// scroll bars--are automatically positioned and/or
// resized when the scroll frame's overall frame is
// changed. They are also automatically resized to fill
// in the gap when one or the other is hidden.
////////////////////////////////////////////////////////////////////
INLINE void PGScrollFrame::
set_manage_pieces(bool manage_pieces) {
_manage_pieces = manage_pieces;
_needs_remanage = true;
_needs_recompute_clip = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::get_manage_pieces
// Access: Published
// Description: Returns the manage_pieces flag. See
// set_manage_pieces().
////////////////////////////////////////////////////////////////////
INLINE bool PGScrollFrame::
get_manage_pieces() const {
return _manage_pieces;
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::set_auto_hide
// Access: Published
// Description: Sets the auto_hide flag. When this is true, the
// two scroll bars are automatically hidden if they are
// not needed (that is, if the virtual frame would fit
// within the clip frame without them), and they are
// automatically shown when they are needed.
//
// Setting this flag true forces the manage_pieces flag
// to also be set true.
////////////////////////////////////////////////////////////////////
INLINE void PGScrollFrame::
set_auto_hide(bool auto_hide) {
_auto_hide = auto_hide;
if (_auto_hide) {
set_manage_pieces(true);
_needs_remanage = true;
}
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::get_auto_hide
// Access: Published
// Description: Returns the auto_hide flag. See
// set_auto_hide().
////////////////////////////////////////////////////////////////////
INLINE bool PGScrollFrame::
get_auto_hide() const {
return _auto_hide;
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::set_horizontal_slider
// Access: Published
// Description: Sets the PGSliderBar object that will serve as the
// horizontal scroll bar for this frame. It is your
// responsibility to parent this slider bar to the frame
// and move it to the appropriate place.
////////////////////////////////////////////////////////////////////
INLINE void PGScrollFrame::
set_horizontal_slider(PGSliderBar *horizontal_slider) {
if (_horizontal_slider != (PGSliderBar *)NULL) {
_horizontal_slider->set_notify(NULL);
}
_horizontal_slider = horizontal_slider;
if (_horizontal_slider != (PGSliderBar *)NULL) {
_horizontal_slider->set_notify(this);
}
_needs_recompute_clip = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::clear_horizontal_slider
// Access: Published
// Description: Removes the horizontal scroll bar from control of the
// frame. It is your responsibility to actually remove
// or hide the object itself.
////////////////////////////////////////////////////////////////////
INLINE void PGScrollFrame::
clear_horizontal_slider() {
set_horizontal_slider(NULL);
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::get_horizontal_slider
// Access: Published
// Description: Returns the PGSliderBar that serves as the horizontal
// scroll bar for this frame, if any, or NULL if it is
// not set.
////////////////////////////////////////////////////////////////////
INLINE PGSliderBar *PGScrollFrame::
get_horizontal_slider() const {
return _horizontal_slider;
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::set_vertical_slider
// Access: Published
// Description: Sets the PGSliderBar object that will serve as the
// vertical scroll bar for this frame. It is your
// responsibility to parent this slider bar to the frame
// and move it to the appropriate place.
////////////////////////////////////////////////////////////////////
INLINE void PGScrollFrame::
set_vertical_slider(PGSliderBar *vertical_slider) {
if (_vertical_slider != (PGSliderBar *)NULL) {
_vertical_slider->set_notify(NULL);
}
_vertical_slider = vertical_slider;
if (_vertical_slider != (PGSliderBar *)NULL) {
_vertical_slider->set_notify(this);
}
_needs_recompute_clip = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::clear_vertical_slider
// Access: Published
// Description: Removes the vertical scroll bar from control of the
// frame. It is your responsibility to actually remove
// or hide the object itself.
////////////////////////////////////////////////////////////////////
INLINE void PGScrollFrame::
clear_vertical_slider() {
set_vertical_slider(NULL);
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::get_vertical_slider
// Access: Published
// Description: Returns the PGSliderBar that serves as the vertical
// scroll bar for this frame, if any, or NULL if it is
// not set.
////////////////////////////////////////////////////////////////////
INLINE PGSliderBar *PGScrollFrame::
get_vertical_slider() const {
return _vertical_slider;
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::recompute
// Access: Published
// Description: Forces the PGScrollFrame to recompute itself right
// now. Normally this should not be required.
////////////////////////////////////////////////////////////////////
INLINE void PGScrollFrame::
recompute() {
recompute_clip();
recompute_canvas();
}

View File

@ -0,0 +1,445 @@
// Filename: pgScrollFrame.cxx
// Created by: drose (17Aug05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "pgScrollFrame.h"
TypeHandle PGScrollFrame::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::Constructor
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
PGScrollFrame::
PGScrollFrame(const string &name) : PGVirtualFrame(name)
{
_needs_remanage = false;
_needs_recompute_canvas = false;
_needs_recompute_clip = false;
_has_virtual_frame = false;
_virtual_frame.set(0.0f, 0.0f, 0.0f, 0.0f);
_manage_pieces = false;
_auto_hide = false;
_horizontal_slider = NULL;
_vertical_slider = NULL;
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
PGScrollFrame::
~PGScrollFrame() {
set_horizontal_slider(NULL);
set_vertical_slider(NULL);
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::Copy Constructor
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
PGScrollFrame::
PGScrollFrame(const PGScrollFrame &copy) :
PGVirtualFrame(copy),
_has_virtual_frame(copy._has_virtual_frame),
_virtual_frame(copy._virtual_frame),
_manage_pieces(copy._manage_pieces),
_auto_hide(copy._auto_hide)
{
_needs_remanage = false;
_needs_recompute_canvas = true;
_needs_recompute_clip = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::make_copy
// Access: Public, Virtual
// Description: Returns a newly-allocated Node that is a shallow copy
// of this one. It will be a different Node pointer,
// but its internal data may or may not be shared with
// that of the original Node.
////////////////////////////////////////////////////////////////////
PandaNode *PGScrollFrame::
make_copy() const {
return new PGScrollFrame(*this);
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::has_cull_callback
// Access: Protected, Virtual
// Description: Should be overridden by derived classes to return
// true if cull_callback() has been defined. Otherwise,
// returns false to indicate cull_callback() does not
// need to be called for this node during the cull
// traversal.
////////////////////////////////////////////////////////////////////
bool PGScrollFrame::
has_cull_callback() const {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::cull_callback
// Access: Protected, Virtual
// Description: If has_cull_callback() returns true, this function
// will be called during the cull traversal to perform
// any additional operations that should be performed at
// cull time. This may include additional manipulation
// of render state or additional visible/invisible
// decisions, or any other arbitrary operation.
//
// By the time this function is called, the node has
// already passed the bounding-volume test for the
// viewing frustum, and the node's transform and state
// have already been applied to the indicated
// CullTraverserData object.
//
// The return value is true if this node should be
// visible, or false if it should be culled.
////////////////////////////////////////////////////////////////////
bool PGScrollFrame::
cull_callback(CullTraverser *trav, CullTraverserData &data) {
if (_manage_pieces && _needs_remanage) {
remanage();
}
if (_needs_recompute_clip) {
recompute_clip();
}
if (_needs_recompute_canvas) {
recompute_canvas();
}
return PGVirtualFrame::cull_callback(trav, data);
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::xform
// Access: Public, Virtual
// Description: Transforms the contents of this node by the indicated
// matrix, if it means anything to do so. For most
// kinds of nodes, this does nothing.
////////////////////////////////////////////////////////////////////
void PGScrollFrame::
xform(const LMatrix4f &mat) {
PGVirtualFrame::xform(mat);
_needs_remanage = true;
_needs_recompute_clip = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::setup
// Access: Published
// Description: Creates a PGScrollFrame with the indicated
// dimensions, and the indicated virtual frame.
////////////////////////////////////////////////////////////////////
void PGScrollFrame::
setup(float width, float height,
float left, float right, float bottom, float top,
float slider_width, float bevel) {
set_state(0);
clear_state_def(0);
set_frame(0, width, 0, height);
PGFrameStyle style;
style.set_width(bevel, bevel);
style.set_color(0.8f, 0.8f, 0.8f, 1.0f);
style.set_type(PGFrameStyle::T_ridge);
set_frame_style(0, style);
set_clip_frame(bevel, width - bevel,
bevel, height - bevel);
set_virtual_frame(left, right, bottom, top);
// Remove the slider nodes created by a previous call to setup(), if
// any.
if (_horizontal_slider != (PGSliderBar *)NULL) {
remove_child(_horizontal_slider);
set_horizontal_slider(NULL);
}
if (_vertical_slider != (PGSliderBar *)NULL) {
remove_child(_vertical_slider);
set_vertical_slider(NULL);
}
// Create new slider bars.
PT(PGSliderBar) horizontal_slider = new PGSliderBar("horizontal");
horizontal_slider->setup_scroll_bar(false, width - slider_width - bevel * 2, slider_width, bevel);
horizontal_slider->set_transform(TransformState::make_pos(LVector3f::rfu(width / 2.0f - slider_width / 2.0f, 0, slider_width / 2.0f + bevel)));
add_child(horizontal_slider);
set_horizontal_slider(horizontal_slider);
PT(PGSliderBar) vertical_slider = new PGSliderBar("vertical");
vertical_slider->setup_scroll_bar(true, width - slider_width - bevel * 2, slider_width, bevel);
add_child(vertical_slider);
vertical_slider->set_transform(TransformState::make_pos(LVector3f::rfu(width - slider_width / 2.0f - bevel, 0, width / 2.0f + slider_width / 2.0f)));
set_vertical_slider(vertical_slider);
set_manage_pieces(true);
set_auto_hide(true);
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::remanage
// Access: Published
// Description: Manages the position and size of the scroll bars.
// Normally this should not need to be called directly.
////////////////////////////////////////////////////////////////////
void PGScrollFrame::
remanage() {
_needs_remanage = false;
const LVecBase4f &frame = get_frame();
LVecBase4f clip = get_frame_style(get_state()).get_internal_frame(frame);
// Determine which scroll bars we have in the frame.
bool got_horizontal = false;
float horizontal_width = 0.0f;
if (_horizontal_slider != (PGSliderBar *)NULL) {
got_horizontal = true;
const LVecBase4f &slider_frame = _horizontal_slider->get_frame();
horizontal_width = slider_frame[3] - slider_frame[2];
}
bool got_vertical = false;
float vertical_width = 0.0f;
if (_vertical_slider != (PGSliderBar *)NULL) {
got_vertical = true;
const LVecBase4f &slider_frame = _vertical_slider->get_frame();
vertical_width = slider_frame[1] - slider_frame[0];
}
if (_auto_hide) {
// Should we hide or show either of the scroll bars? TODO.
// Start by figuring out what our maximum clipping area will be.
float clip_width = clip[1] - clip[0];
float clip_height = clip[3] - clip[2];
// And our virtual frame too.
const LVecBase4f &virtual_frame = get_virtual_frame();
float virtual_width = virtual_frame[1] - virtual_frame[0];
float virtual_height = virtual_frame[3] - virtual_frame[2];
if (virtual_width <= clip_width &&
virtual_height <= clip_height) {
// No need for either slider.
got_horizontal = false;
got_vertical = false;
} else {
if (virtual_width <= clip_width - vertical_width) {
// No need for the horizontal slider.
got_horizontal = false;
}
if (virtual_height <= clip_height - horizontal_width) {
// No need for the vertical slider.
got_vertical = false;
// Now reconsider the need for the horizontal slider.
if (virtual_width <= clip_width) {
got_horizontal = false;
}
}
}
// Now show or hide the sliders appropriately.
if (_horizontal_slider != (PGSliderBar *)NULL) {
if (got_horizontal) {
_horizontal_slider->set_draw_mask(DrawMask::all_on());
} else {
_horizontal_slider->set_draw_mask(DrawMask::all_off());
_horizontal_slider->set_ratio(0.0f);
horizontal_width = 0.0f;
}
}
if (_vertical_slider != (PGSliderBar *)NULL) {
if (got_vertical) {
_vertical_slider->set_draw_mask(DrawMask::all_on());
} else {
_vertical_slider->set_draw_mask(DrawMask::all_off());
_vertical_slider->set_ratio(0.0f);
vertical_width = 0.0f;
}
}
// Showing or hiding one of the scroll bars might have set this
// flag again indirectly; we clear it again to avoid a feedback
// loop.
_needs_remanage = false;
}
// Are either or both of the scroll bars hidden?
if (got_horizontal && _horizontal_slider->get_draw_mask().is_zero()) {
got_horizontal = false;
horizontal_width = 0.0f;
}
if (got_vertical && _vertical_slider->get_draw_mask().is_zero()) {
got_vertical = false;
vertical_width = 0.0f;
}
if (got_horizontal) {
_horizontal_slider->set_frame(clip[0], clip[1] - vertical_width,
clip[2], clip[2] + horizontal_width);
_horizontal_slider->clear_transform();
}
if (got_vertical) {
_vertical_slider->set_frame(clip[1] - vertical_width, clip[1],
clip[2] + horizontal_width, clip[3]);
_vertical_slider->clear_transform();
}
recompute();
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::frame_changed
// Access: Protected, Virtual
// Description: Called when the user changes the frame size.
////////////////////////////////////////////////////////////////////
void PGScrollFrame::
frame_changed() {
PGVirtualFrame::frame_changed();
_needs_remanage = true;
_needs_recompute_clip = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::item_transform_changed
// Access: Protected, Virtual
// Description: Called whenever a watched PGItem's local transform
// has been changed.
////////////////////////////////////////////////////////////////////
void PGScrollFrame::
item_transform_changed(PGItem *) {
_needs_recompute_clip = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::item_frame_changed
// Access: Protected, Virtual
// Description: Called whenever a watched PGItem's frame
// has been changed.
////////////////////////////////////////////////////////////////////
void PGScrollFrame::
item_frame_changed(PGItem *) {
_needs_recompute_clip = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::item_draw_mask_changed
// Access: Protected, Virtual
// Description: Called whenever a watched PGItem's draw_mask
// has been changed.
////////////////////////////////////////////////////////////////////
void PGScrollFrame::
item_draw_mask_changed(PGItem *) {
_needs_remanage = true;
_needs_recompute_clip = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::slider_bar_adjust
// Access: Protected, Virtual
// Description: Called whenever a watched PGSliderBar's value
// has been changed by the user or programmatically.
////////////////////////////////////////////////////////////////////
void PGScrollFrame::
slider_bar_adjust(PGSliderBar *) {
_needs_recompute_canvas = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::recompute_clip
// Access: Private
// Description: Recomputes the clipping window of the PGScrollFrame,
// based on the position of the slider bars.
////////////////////////////////////////////////////////////////////
void PGScrollFrame::
recompute_clip() {
_needs_recompute_clip = false;
_needs_recompute_canvas = true;
// Figure out how to remove the scroll bars from the clip region.
LVecBase4f clip = get_frame_style(get_state()).get_internal_frame(get_frame());
reduce_region(clip, _horizontal_slider);
reduce_region(clip, _vertical_slider);
set_clip_frame(clip);
if (_horizontal_slider != (PGSliderBar *)NULL) {
_horizontal_slider->set_page_size((clip[1] - clip[0]) / (_virtual_frame[1] - _virtual_frame[0]));
}
if (_vertical_slider != (PGSliderBar *)NULL) {
_vertical_slider->set_page_size((clip[3] - clip[2]) / (_virtual_frame[3] - _virtual_frame[2]));
}
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::recompute_canvas
// Access: Private
// Description: Recomputes the portion of the virtual canvas that is
// visible within the PGScrollFrame, based on the values
// of the slider bars.
////////////////////////////////////////////////////////////////////
void PGScrollFrame::
recompute_canvas() {
_needs_recompute_canvas = false;
const LVecBase4f &clip = get_clip_frame();
float x = interpolate_canvas(clip[0], clip[1],
_virtual_frame[0], _virtual_frame[1],
_horizontal_slider);
float y = interpolate_canvas(clip[3], clip[2],
_virtual_frame[3], _virtual_frame[2],
_vertical_slider);
get_canvas_node()->set_transform(TransformState::make_pos(LVector3f::rfu(x, 0, y)));
}
////////////////////////////////////////////////////////////////////
// Function: PGScrollFrame::interpolate_canvas
// Access: Private
// Description: Computes the linear translation that should be
// applied to the virtual canvas node, based on the
// corresponding slider bar's position.
////////////////////////////////////////////////////////////////////
float PGScrollFrame::
interpolate_canvas(float clip_min, float clip_max,
float canvas_min, float canvas_max,
PGSliderBar *slider_bar) {
float t = 0.0f;
if (slider_bar != (PGSliderBar *)NULL) {
t = slider_bar->get_ratio();
}
float min = clip_min - canvas_min;
float max = clip_max - canvas_max;
return min + t * (max - min);
}

View File

@ -0,0 +1,137 @@
// Filename: pgScrollFrame.h
// Created by: drose (17Aug05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef PGSCROLLFRAME_H
#define PGSCROLLFRAME_H
#include "pandabase.h"
#include "pgVirtualFrame.h"
#include "pgSliderBarNotify.h"
#include "pgSliderBar.h"
////////////////////////////////////////////////////////////////////
// Class : PGScrollFrame
// Description : This is a special kind of frame that pretends to be
// much larger than it actually is. You can scroll
// through the frame, as if you're looking through a
// window at the larger frame beneath. All children of
// this frame node are scrolled and clipped as if they
// were children of the larger, virtual frame.
//
// This is implemented as a specialization of
// PGVirtualFrame, which handles the meat of the virtual
// canvas. This class adds automatic support for scroll
// bars, and restricts the virtual transform to
// translate only (no scale or rotate).
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA PGScrollFrame : public PGVirtualFrame, public PGSliderBarNotify {
PUBLISHED:
PGScrollFrame(const string &name = "");
virtual ~PGScrollFrame();
protected:
PGScrollFrame(const PGScrollFrame &copy);
public:
virtual PandaNode *make_copy() const;
virtual bool has_cull_callback() const;
virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
virtual void xform(const LMatrix4f &mat);
PUBLISHED:
void setup(float width, float height,
float left, float right, float bottom, float top,
float slider_width, float bevel);
INLINE void set_virtual_frame(float left, float right, float bottom, float top);
INLINE void set_virtual_frame(const LVecBase4f &virtual_frame);
INLINE const LVecBase4f &get_virtual_frame() const;
INLINE bool has_virtual_frame() const;
INLINE void clear_virtual_frame();
INLINE void set_manage_pieces(bool manage_pieces);
INLINE bool get_manage_pieces() const;
INLINE void set_auto_hide(bool auto_hide);
INLINE bool get_auto_hide() const;
INLINE void set_horizontal_slider(PGSliderBar *horizontal_slider);
INLINE void clear_horizontal_slider();
INLINE PGSliderBar *get_horizontal_slider() const;
INLINE void set_vertical_slider(PGSliderBar *vertical_slider);
INLINE void clear_vertical_slider();
INLINE PGSliderBar *get_vertical_slider() const;
void remanage();
INLINE void recompute();
protected:
virtual void frame_changed();
virtual void item_transform_changed(PGItem *item);
virtual void item_frame_changed(PGItem *item);
virtual void item_draw_mask_changed(PGItem *item);
virtual void slider_bar_adjust(PGSliderBar *slider_bar);
private:
void recompute_clip();
void recompute_canvas();
float interpolate_canvas(float clip_min, float clip_max,
float canvas_min, float canvas_max,
PGSliderBar *slider_bar);
private:
bool _needs_remanage;
bool _needs_recompute_clip;
bool _needs_recompute_canvas;
LVecBase4f _orig_clip_frame;
bool _has_virtual_frame;
LVecBase4f _virtual_frame;
bool _manage_pieces;
bool _auto_hide;
PT(PGSliderBar) _horizontal_slider;
PT(PGSliderBar) _vertical_slider;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
PGVirtualFrame::init_type();
register_type(_type_handle, "PGScrollFrame",
PGVirtualFrame::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
};
#include "pgScrollFrame.I"
#endif

View File

@ -17,265 +17,419 @@
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::set_notify
// Access: Published
// Description: Sets the object which will be notified when the
// PGSliderBar changes. Set this to NULL to disable
// this effect. The PGSliderBar does not retain
// ownership of the pointer; it is your responsibility
// to ensure that the notify object does not destruct.
////////////////////////////////////////////////////////////////////
INLINE void PGSliderBar::
set_notify(PGSliderBarNotify *notify) {
PGItem::set_notify(notify);
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::get_notify
// Access: Published
// Description: Returns the object which will be notified when the
// PGSliderBar changes, if any. Returns NULL if there
// is no such object configured.
////////////////////////////////////////////////////////////////////
INLINE PGSliderBarNotify *PGSliderBar::
get_notify() const {
return (PGSliderBarNotify *)PGItem::get_notify();
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::set_axis
// Access: Published
// Description: Specifies the axis of the slider bar's motion. This
// should be only one of four vectors: (1, 0, 0), (0, 0,
// 1), (-1, 0, 0), or (0, 0, -1).
//
// This specifies the vector in which the thumb moves
// when it is moving from the minimum to the maximum
// value.
//
// The axis must be parallel to one of the screen axes,
// and it must be normalized. Hence, it may only be one
// of the above four possibilities; anything else is an
// error and will result in indeterminate behavior.
//
// Normally, you should not try to set the axis
// directly.
////////////////////////////////////////////////////////////////////
INLINE void PGSliderBar::
set_axis(const LVector3f &axis) {
_axis = axis;
_needs_remanage = true;
_needs_recompute = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::get_axis
// Access: Published
// Description: Returns the axis of the slider bar's motion. See
// set_axis().
////////////////////////////////////////////////////////////////////
INLINE const LVector3f &PGSliderBar::
get_axis() const {
return _axis;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::set_range
// Access: Published
// Description: Sets the value at which the SliderBar indicates 100%.
// Description: Sets the minimum and maxmimum value for the slider.
////////////////////////////////////////////////////////////////////
INLINE void PGSliderBar::
set_range(float range) {
_range = range;
_bar_state = -1;
set_range(float min_value, float max_value) {
nassertv(min_value != max_value);
_min_value = min_value;
_max_value = max_value;
_needs_recompute = true;
if (has_notify()) {
get_notify()->slider_bar_set_range(this);
}
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::get_range
// Function: PGSliderBar::get_min_value
// Access: Published
// Description: Returns the value at which the SliderBar indicates 100%.
// Description: Returns the value when the slider is all the way to
// the left.
////////////////////////////////////////////////////////////////////
INLINE float PGSliderBar::
get_range() const {
return _range;
get_min_value() const {
return _min_value;
}
/*
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::set_magnifier
// Function: PGSliderBar::get_max_value
// Access: Published
// Description: Sets a magnifier value for the mouseWatcherRegion in
// the Y direction.
////////////////////////////////////////////////////////////////////
INLINE void PGSliderBar::
set_magnifier(float value) {
PGItem::_mouseWatcherMagnifier = value;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::get_value
// Access: Published
// Description: Returns the current value of the bar.
// Description: Returns the value when the slider is all the way to
// the right.
////////////////////////////////////////////////////////////////////
INLINE float PGSliderBar::
get_magnifier() const {
return PGItem::_mouseWatcherMagnifier;
get_max_value() const {
return _max_value;
}
*/
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::set_scroll_size
// Access: Published
// Description: Specifies the amount the slider will move when the
// user clicks on the left or right buttons.
////////////////////////////////////////////////////////////////////
INLINE void PGSliderBar::
set_scroll_size(float value) {
_scroll_value = value;
_needs_recompute = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::get_scroll_size
// Access: Published
// Description: Returns the value last set by set_scroll_size().
////////////////////////////////////////////////////////////////////
INLINE float PGSliderBar::
get_scroll_size() const {
return _scroll_value;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::set_page_size
// Access: Published
// Description: Specifies the amount of data contained in a single
// page. This indicates how much the thumb will jump
// when the trough is directly clicked; and if
// resize_thumb is true, it also controls the visible
// size of the thumb button.
////////////////////////////////////////////////////////////////////
INLINE void PGSliderBar::
set_page_size(float value) {
_page_value = value;
_needs_recompute = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::get_page_size
// Access: Published
// Description: Returns the value last set by set_page_size().
////////////////////////////////////////////////////////////////////
INLINE float PGSliderBar::
get_page_size() const {
return _page_value;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::set_value
// Access: Published
// Description: Sets the current value of the bar. This should range
// between 0 and get_range().
// Description: Sets the current value of the slider
// programmatically. This should range between
// get_min_value() and get_max_value().
////////////////////////////////////////////////////////////////////
INLINE void PGSliderBar::
set_value(float value) {
_value = value;
_mapped_value = _negative_mapping ? (_value*0.5 + 0.5) : _value;
_update_x = _width * (2*_mapped_value - 1);
_bar_state = -1;
_update_slider = true;
set_ratio((value - _min_value) / (_max_value - _min_value));
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::get_value
// Access: Published
// Description: Returns the current value of the bar.
// Description: Returns the current value of the slider.
////////////////////////////////////////////////////////////////////
INLINE float PGSliderBar::
get_value() const {
return _value;
return get_ratio() * (_max_value - _min_value) + _min_value;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar:get_mapped_value
// Function: PGSliderBar::set_ratio
// Access: Published
// Description: Returns the _mapped_value which is caluclated from
// _value with negative_mapping bool
////////////////////////////////////////////////////////////////////
INLINE float PGSliderBar::
get_mapped_value() const {
return _mapped_value;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::get_update_x
// Access: Published
// Description: Returns the _update_x which is caluclated from
// _mapped_value with width formula
////////////////////////////////////////////////////////////////////
INLINE float PGSliderBar::
get_update_x() const {
return _update_x;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::get_update_y
// Access: Published
// Description: Returns the _update_y which is caluclated from
// _mapped_value with width formula
////////////////////////////////////////////////////////////////////
INLINE float PGSliderBar::
get_update_y() const {
return _update_y;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::set_speed
// Access: Published
// Description: Sets the current speed at which the slider moves
// Description: Sets the current value of the slider, expressed in
// the range 0 .. 1.
////////////////////////////////////////////////////////////////////
INLINE void PGSliderBar::
set_speed(float speed) {
_speed = speed;
set_ratio(float ratio) {
_ratio = max(min(ratio, 1.0f), 0.0f);
_needs_reposition = true;
adjust();
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::get_speed
// Function: PGSliderBar::get_ratio
// Access: Published
// Description: Returns the current speed at which the slider moves.
// Description: Returns the current value of the slider, expressed in
// the range 0 .. 1.
////////////////////////////////////////////////////////////////////
INLINE float PGSliderBar::
get_speed() const {
return _speed;
get_ratio() const {
return _ratio;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::set_scale
// Function: PGSliderBar::set_resize_thumb
// Access: Published
// Description: Sets the scale of the traugh image
// Description: Sets the resize_thumb flag. When this is true, the
// thumb button's frame will be adjusted so that its
// width visually represents the page size. When this
// is false, the thumb button will be left alone.
////////////////////////////////////////////////////////////////////
INLINE void PGSliderBar::
set_scale(float scale) {
_scale = scale/10;
set_resize_thumb(bool resize_thumb) {
_resize_thumb = resize_thumb;
_needs_recompute = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::get_scale
// Function: PGSliderBar::get_resize_thumb
// Access: Published
// Description: Returns the scale set on the traugh image
////////////////////////////////////////////////////////////////////
INLINE float PGSliderBar::
get_scale() const {
return _scale;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::set_slider_only
// Access: Published
// Description: Sets _slider_only. True:only slider button desired.
// No left or right button to control slider needed.
// Else, the left and right slider buttons are shown.
////////////////////////////////////////////////////////////////////
INLINE void PGSliderBar::
set_slider_only(bool value) {
_slider_only = value;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::get_slider_only
// Access: Published
// Description: Returns what type of slider option set
// Description: Returns the resize_thumb flag. See
// set_resize_thumb().
////////////////////////////////////////////////////////////////////
INLINE bool PGSliderBar::
get_slider_only() const {
return _slider_only;
get_resize_thumb() const {
return _resize_thumb;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::set_negative_mapping
// Function: PGSliderBar::set_manage_pieces
// Access: Published
// Description: Sets _negative_mapping. True: slider will receive
// _value from -1 to +1 else, _value from 0 to + 1
// Description: Sets the manage_pieces flag. When this is true, the
// sub-pieces of the slider bar--that is, the thumb, and
// the left and right scroll buttons--are automatically
// positioned and/or resized when the slider bar's
// overall frame is changed.
////////////////////////////////////////////////////////////////////
INLINE void PGSliderBar::
set_negative_mapping(bool value) {
_negative_mapping = value;
set_manage_pieces(bool manage_pieces) {
_manage_pieces = manage_pieces;
_needs_remanage = true;
_needs_recompute = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::get_negative_mapping
// Function: PGSliderBar::get_manage_pieces
// Access: Published
// Description: Returns what type of value range option set
// Description: Returns the manage_pieces flag. See
// set_manage_pieces().
////////////////////////////////////////////////////////////////////
INLINE bool PGSliderBar::
get_negative_mapping() const {
return _negative_mapping;
get_manage_pieces() const {
return _manage_pieces;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::set_bar_style
// Function: PGSliderBar::set_thumb_button
// Access: Published
// Description: Sets the kind of frame that is drawn on top of the
// SliderBar to represent the amount completed.
////////////////////////////////////////////////////////////////////
INLINE void PGSliderBar::
set_bar_style(const PGFrameStyle &style) {
_bar_style = style;
_bar_state = -1;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::get_bar_style
// Access: Published
// Description: Returns the kind of frame that is drawn on top of the
// SliderBar to represent the amount completed.
////////////////////////////////////////////////////////////////////
INLINE PGFrameStyle PGSliderBar::
get_bar_style() const {
return _bar_style;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::get_click_event
// Access: Published
// Description: Returns the event name that will be thrown when the
// slider button is moved
////////////////////////////////////////////////////////////////////
INLINE string PGSliderBar::
get_click_event() const {
return "updated-slider-" + get_id();
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::get_slider_button
// Access: Published
// Description: Returns the slider button that is drawn on the
// SliderBar to move the slider left or right
////////////////////////////////////////////////////////////////////
INLINE NodePath PGSliderBar::
get_slider_button() const {
return _slider_button;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::set_slider_button
// Access: Published
// Description: sets slider button that is drawn on the
// SliderBar to move the slider left or right
// Description: Sets the PGButton object that will serve as the thumb
// for this slider. This button visually represents the
// position of the slider, and can be dragged left and
// right by the user.
//
// It is the responsibility of the caller to ensure that
// the button object is parented to the PGSliderBar
// node.
////////////////////////////////////////////////////////////////////
INLINE void PGSliderBar::
set_slider_button(NodePath &np, PGSliderButton *button) {
_slider_button = np;
_slider = button;
set_thumb_button(PGButton *thumb_button) {
if (_thumb_button != (PGButton *)NULL) {
_thumb_button->set_notify(NULL);
}
_thumb_button = thumb_button;
if (_thumb_button != (PGButton *)NULL) {
_thumb_button->set_notify(this);
}
_needs_remanage = true;
_needs_recompute = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::clear_thumb_button
// Access: Published
// Description: Removes the thumb button object from control of the
// frame. It is your responsibility to actually remove
// or hide the button itself.
////////////////////////////////////////////////////////////////////
INLINE void PGSliderBar::
clear_thumb_button() {
set_thumb_button(NULL);
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::get_thumb_button
// Access: Published
// Description: Returns the PGButton that serves as the thumb for
// this slider, or NULL if it is not set.
////////////////////////////////////////////////////////////////////
INLINE PGButton *PGSliderBar::
get_thumb_button() const {
return _thumb_button;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::set_left_button
// Access: Published
// Description: Sets the PGButton object that will serve as the left
// scroll button for this slider. This button is
// optional; if present, the user can click on it to
// move scroll_size units at a time to the left.
//
// It is the responsibility of the caller to ensure that
// the button object is parented to the PGSliderBar
// node.
////////////////////////////////////////////////////////////////////
INLINE void PGSliderBar::
set_left_button(PGButton *left_button) {
if (_left_button != (PGButton *)NULL) {
_left_button->set_notify(NULL);
}
_left_button = left_button;
if (_left_button != (PGButton *)NULL) {
_left_button->set_notify(this);
}
_needs_remanage = true;
_needs_recompute = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::clear_left_button
// Access: Published
// Description: Removes the left button object from control of the
// frame. It is your responsibility to actually remove
// or hide the button itself.
////////////////////////////////////////////////////////////////////
INLINE void PGSliderBar::
clear_left_button() {
set_left_button(NULL);
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::get_left_button
// Access: Published
// Description: Returns the left button that is drawn on left of the
// SliderBar to move the slider left
// Description: Returns the PGButton that serves as the left scroll
// button for this slider, if any, or NULL if it is not
// set.
////////////////////////////////////////////////////////////////////
INLINE NodePath PGSliderBar::
INLINE PGButton *PGSliderBar::
get_left_button() const {
return _left_button;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::set_right_button
// Access: Published
// Description: Sets the PGButton object that will serve as the right
// scroll button for this slider. This button is
// optional; if present, the user can click on it to
// move scroll_size units at a time to the right.
//
// It is the responsibility of the caller to ensure that
// the button object is parented to the PGSliderBar
// node.
////////////////////////////////////////////////////////////////////
INLINE void PGSliderBar::
set_right_button(PGButton *right_button) {
if (_right_button != (PGButton *)NULL) {
_right_button->set_notify(NULL);
}
_right_button = right_button;
if (_right_button != (PGButton *)NULL) {
_right_button->set_notify(this);
}
_needs_remanage = true;
_needs_recompute = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::clear_right_button
// Access: Published
// Description: Removes the right button object from control of the
// frame. It is your responsibility to actually remove
// or hide the button itself.
////////////////////////////////////////////////////////////////////
INLINE void PGSliderBar::
clear_right_button() {
set_right_button(NULL);
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::get_right_button
// Access: Published
// Description: Returns the right button that is drawn on right of the
// SliderBar to move the slider right
// Description: Returns the PGButton that serves as the right scroll
// button for this slider, if any, or NULL if it is not
// set.
////////////////////////////////////////////////////////////////////
INLINE NodePath PGSliderBar::
INLINE PGButton *PGSliderBar::
get_right_button() const {
return _right_button;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::get_adjust_prefix
// Access: Published, Static
// Description: Returns the prefix that is used to define the adjust
// event for all PGSliderBars. The adjust event is the
// concatenation of this string followed by get_id().
////////////////////////////////////////////////////////////////////
INLINE string PGSliderBar::
get_adjust_prefix() {
return "adjust-";
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::get_adjust_event
// Access: Published
// Description: Returns the event name that will be thrown when the
// slider bar value is adjusted by the user or
// programmatically.
////////////////////////////////////////////////////////////////////
INLINE string PGSliderBar::
get_adjust_event() const {
return get_adjust_prefix() + get_id();
}

View File

@ -18,8 +18,12 @@
#include "pgSliderBar.h"
#include "pgMouseWatcherParameter.h"
#include "clockObject.h"
#include "throw_event.h"
#include "config_pgui.h"
#include "throw_event.h"
#include "transformState.h"
#include "mouseButton.h"
TypeHandle PGSliderBar::_type_handle;
@ -29,26 +33,26 @@ TypeHandle PGSliderBar::_type_handle;
// Description:
////////////////////////////////////////////////////////////////////
PGSliderBar::
PGSliderBar(const string &name) :
PGItem(name),
// _slider("slider"),
_left("left"),
_right("right")
PGSliderBar(const string &name)
: PGItem(name)
{
_slider = NULL;
_range = 100.0;
// _value is a range from (-1 to +1) that represents slider
_value = 0.0;
// _mapped_value is a mapping of (-1<->1) into (0<->1)
_mapped_value = 0.5*_value + 0.5;
// _update_x is mapping of _mapped_value wrt slider width
_update_x = 0.0; //will be set when _width is defined
_speed = 0.05;
_scale = 0.05;
_bar_state = -1;
_update_slider = false;
_slider_only = true;
_negative_mapping = false;
_min_value = 0.0f;
_max_value = 1.0f;
set_scroll_size(0.01f);
set_page_size(0.1f);
_ratio = 0.0f;
_resize_thumb = false;
_manage_pieces = false;
_axis.set(1.0f, 0.0f, 0.0f);
_needs_remanage = false;
_needs_recompute = true;
_needs_reposition = false;
_scroll_button_held = NULL;
_mouse_button_page = false;
_dragging = false;
_thumb_width = 0.1f;
set_active(true);
}
////////////////////////////////////////////////////////////////////
@ -68,14 +72,22 @@ PGSliderBar::
PGSliderBar::
PGSliderBar(const PGSliderBar &copy) :
PGItem(copy),
_value(copy._value),
_range(copy._range),
// _slider(copy._slider),
_left(copy._left),
_right(copy._right)
_min_value(copy._min_value),
_max_value(copy._max_value),
_scroll_value(copy._scroll_value),
_scroll_ratio(copy._scroll_ratio),
_page_value(copy._page_value),
_page_ratio(copy._page_ratio),
_ratio(copy._ratio),
_resize_thumb(copy._resize_thumb),
_manage_pieces(copy._manage_pieces),
_axis(copy._axis)
{
_bar_state = -1;
_slider = NULL;
_needs_remanage = false;
_needs_recompute = true;
_scroll_button_held = NULL;
_mouse_button_page = false;
_dragging = false;
}
////////////////////////////////////////////////////////////////////
@ -91,6 +103,71 @@ make_copy() const {
return new PGSliderBar(*this);
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::press
// Access: Public, Virtual
// Description: This is a callback hook function, called whenever a
// mouse or keyboard button is depressed while the mouse
// is within the region.
////////////////////////////////////////////////////////////////////
void PGSliderBar::
press(const MouseWatcherParameter &param, bool background) {
if (param.has_mouse()) {
_mouse_pos = param.get_mouse();
}
if (get_active() && param.get_button() == MouseButton::one()) {
if (_needs_recompute) {
recompute();
}
if (_range_x != 0.0f) {
_mouse_button_page = true;
_scroll_button_held = NULL;
advance_page();
_next_advance_time =
ClockObject::get_global_clock()->get_frame_time() + scroll_initial_delay;
}
}
PGItem::press(param, background);
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::release
// Access: Public, Virtual
// Description: This is a callback hook function, called whenever a
// mouse or keyboard button previously depressed with
// press() is released.
////////////////////////////////////////////////////////////////////
void PGSliderBar::
release(const MouseWatcherParameter &param, bool background) {
if (MouseButton::is_mouse_button(param.get_button())) {
_mouse_button_page = false;
}
if (_dragging) {
end_drag();
}
PGItem::release(param, background);
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::move
// Access: Protected, Virtual
// Description: This is a callback hook function, called whenever a
// mouse is moved while within the region.
////////////////////////////////////////////////////////////////////
void PGSliderBar::
move(const MouseWatcherParameter &param) {
_mouse_pos = param.get_mouse();
if (_dragging) {
// We only get here if we the user originally clicked on the
// track, which caused the slider to move all the way to the mouse
// position, and then started dragging the mouse along the track.
// In this case, we start moving the thumb as if the user had
// started by dragging the thumb directly.
continue_drag();
}
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::has_cull_callback
// Access: Protected, Virtual
@ -126,161 +203,599 @@ has_cull_callback() const {
////////////////////////////////////////////////////////////////////
bool PGSliderBar::
cull_callback(CullTraverser *trav, CullTraverserData &data) {
update();
if (_manage_pieces && _needs_remanage) {
remanage();
}
if (_needs_recompute) {
recompute();
}
if (_scroll_button_held != (PGItem *)NULL &&
_next_advance_time <= ClockObject::get_global_clock()->get_frame_time()) {
advance_scroll();
}
if (_mouse_button_page &&
_next_advance_time <= ClockObject::get_global_clock()->get_frame_time()) {
advance_page();
}
if (_needs_reposition) {
reposition();
}
return PGItem::cull_callback(trav, data);
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::setup
// Access: Public
// Description: Creates a PGSliderBar with the indicated dimensions,
// with the indicated maximum range.
// Function: PGSliderBar::xform
// Access: Public, Virtual
// Description: Transforms the contents of this node by the indicated
// matrix, if it means anything to do so. For most
// kinds of nodes, this does nothing.
////////////////////////////////////////////////////////////////////
void PGSliderBar::
setup(float width, float height, float range) {
//set_state(0);
//clear_state_def(0);
xform(const LMatrix4f &mat) {
PGItem::xform(mat);
_axis = _axis * mat;
_width = 0.5 * width; // quick reference to find the left and right max points
//set_frame(-0.5f * width, 0.5f * width, -0.5f * height, 0.5f * height);
set_range(range);
NodePath current = NodePath(this);
if (!_slider) {
_slider = new PGSliderButton("slider");
_slider_button = current.attach_new_node(_slider);
// Make sure we set the thumb to identity position first, so it
// won't be accidentally flattened.
if (_thumb_button != (PGButton *)NULL) {
_thumb_button->clear_transform();
}
_slider->set_slider_bar(this);
//_slider->setup(_slider->get_name());
_slider->setup("");
_slider->set_drag_n_drop(true);
_slider->set_frame(-1,1,-1.5,1.5);
_slider_button.set_scale(_scale);
_slider_button.set_pos(0, 0, 0); // center it
// if left or right button to control slider desired, create them
if (!_slider_only) {
_left_button = current.attach_new_node(&_left);
_left.set_slider_bar(this);
_right_button = current.attach_new_node(&_right);
_right.set_slider_bar(this);
_left.setup(_left.get_name());
_right.setup(_right.get_name());
_left_button.set_scale(0.5);
_left_button.set_pos(-(_width+1), 0, -0.25);
_right_button.set_scale(0.5);
_right_button.set_pos((_width+0.5), 0, -0.25);
_needs_remanage = true;
_needs_recompute = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::adjust
// Access: Public, Virtual
// Description: This is a callback hook function, called whenever the
// slider value is adjusted by the user or
// programmatically.
////////////////////////////////////////////////////////////////////
void PGSliderBar::
adjust() {
string event = get_adjust_event();
play_sound(event);
throw_event(event);
if (has_notify()) {
get_notify()->slider_bar_adjust(this);
}
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::setup_scroll_bar
// Access: Published
// Description: Creates PGSliderBar that represents a vertical or
// horizontal scroll bar (if vertical is true or false,
// respectively), with additional buttons for scrolling,
// and a range of 0 .. 1.
//
// length here is the measurement along the scroll bar,
// and width is the measurement across the scroll bar,
// whether it is vertical or horizontal (so for a
// horizontal scroll bar, the length is actually the x
// dimension, and the width is the y dimension).
////////////////////////////////////////////////////////////////////
void PGSliderBar::
setup_scroll_bar(bool vertical, float length, float width, float bevel) {
set_state(0);
clear_state_def(0);
if (vertical) {
set_frame(-width / 2.0f, width / 2.0f, -length / 2.0f, length / 2.0f);
_axis = LVector3f::rfu(0.0f, 0.0f, -1.0f);
} else {
set_frame(-length / 2.0f, length / 2.0f, -width / 2.0f, width / 2.0f);
_axis = LVector3f::rfu(1.0f, 0.0f, 0.0f);
}
PGFrameStyle style;
style.set_width(0.3f, 0.3f);
/*
style.set_color(0.6f, 0.6f, 0.6f, 1.0f);
style.set_type(PGFrameStyle::T_bevel_in);
//set_frame_style(0, style);
*/
style.set_type(PGFrameStyle::T_flat);
set_frame_style(0, style);
style.set_color(0.8f, 0.8f, 0.8f, 1.0f);
style.set_type(PGFrameStyle::T_bevel_out);
_slider->set_frame_style(PGButton::S_ready, style);
style.set_type(PGFrameStyle::T_bevel_in);
_slider->set_frame_style(PGButton::S_depressed, style);
style.set_width(bevel, bevel);
// Remove the button nodes created by a previous call to setup(), if
// any.
if (_thumb_button != (PGButton *)NULL) {
remove_child(_thumb_button);
set_thumb_button(NULL);
}
if (_left_button != (PGButton *)NULL) {
remove_child(_left_button);
set_left_button(NULL);
}
if (_right_button != (PGButton *)NULL) {
remove_child(_right_button);
set_right_button(NULL);
}
PT(PGButton) thumb = new PGButton("thumb");
thumb->setup("", bevel);
thumb->set_frame(-width / 2.0f, width / 2.0f,
-width / 2.0f, width / 2.0f);
add_child(thumb);
set_thumb_button(thumb);
PT(PGButton) left = new PGButton("left");
left->setup("", bevel);
left->set_frame(-width / 2.0f, width / 2.0f,
-width / 2.0f, width / 2.0f);
left->set_transform(TransformState::make_pos(((width - length) / 2.0f) * _axis));
add_child(left);
set_left_button(left);
PT(PGButton) right = new PGButton("right");
right->setup("", bevel);
right->set_frame(-width / 2.0f, width / 2.0f,
-width / 2.0f, width / 2.0f);
right->set_transform(TransformState::make_pos(((length - width) / 2.0f) * _axis));
add_child(right);
set_right_button(right);
set_resize_thumb(true);
set_manage_pieces(true);
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::press
// Access: Public, Virtual
// Description: This is a callback hook function, called whenever a
// mouse or keyboard button is depressed while the mouse
// is within the region.
// Function: PGSliderBar::setup_slider
// Access: Published
// Description: Creates PGSliderBar that represents a slider that the
// user can use to control an analog quantity.
//
// This is functionally the same as a scroll bar, but it
// has a distinctive look.
////////////////////////////////////////////////////////////////////
void PGSliderBar::
press(const MouseWatcherParameter &param, bool background) {
PGItem::press(param, background);
//pgui_cat.info() << get_name() << "::" << param << endl;
//pgui_cat.info() << _slider->get_name() << "::press:x" << _slider_button.get_x()
// << " press:y" << _slider_button.get_y() << endl;
setup_slider(bool vertical, float length, float width, float bevel) {
set_state(0);
clear_state_def(0);
// translate the mouse param position into frame space
LPoint2f mouse_point = param.get_mouse();
LVector3f result(mouse_point[0], mouse_point[1], 0);
result = get_frame_inv_xform().xform_point(result);
_update_slider = true;
_update_x = result[0];
_update_y = result[1];
//_slider_button.set_x(result[0]);
if (vertical) {
set_frame(-width / 2.0f, width / 2.0f, -length / 2.0f, length / 2.0f);
_axis = LVector3f::rfu(0.0f, 0.0f, -1.0f);
} else {
set_frame(-length / 2.0f, length / 2.0f, -width / 2.0f, width / 2.0f);
_axis = LVector3f::rfu(1.0f, 0.0f, 0.0f);
}
PGFrameStyle style;
style.set_color(0.6f, 0.6f, 0.6f, 1.0f);
style.set_type(PGFrameStyle::T_flat);
style.set_visible_scale(1.0f, 0.25f);
style.set_width(bevel, bevel);
set_frame_style(0, style);
// Remove the button nodes created by a previous call to setup(), if
// any.
if (_thumb_button != (PGButton *)NULL) {
remove_child(_thumb_button);
set_thumb_button(NULL);
}
if (_left_button != (PGButton *)NULL) {
remove_child(_left_button);
set_left_button(NULL);
}
if (_right_button != (PGButton *)NULL) {
remove_child(_right_button);
set_right_button(NULL);
}
PT(PGButton) thumb = new PGButton("thumb");
thumb->setup(" ", bevel);
thumb->set_frame(-width / 4.0f, width / 4.0f,
-width / 2.0f, width / 2.0f);
add_child(thumb);
set_thumb_button(thumb);
set_resize_thumb(false);
set_manage_pieces(true);
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::drag
// Access: Public, Virtual
// Description: This is a hook function, called when the user
// is trying to drag the slider button
// Function: PGSliderBar::set_active
// Access: Published, Virtual
// Description: Sets whether the PGItem is active for mouse watching.
// This is not necessarily related to the
// active/inactive appearance of the item, which is
// controlled by set_state(), but it does affect whether
// it responds to mouse events.
////////////////////////////////////////////////////////////////////
void PGSliderBar::
drag(const MouseWatcherParameter &param) {
//pgui_cat.info() << get_name() << "::" << param << endl;
//pgui_cat.info() << _slider->get_name() << "::drag:x" << _slider_button.get_x()
// <<" drag:y" << _slider_button.get_y() << endl;
set_active(bool active) {
PGItem::set_active(active);
// translate the mouse param position into frame space
LPoint2f mouse_point = param.get_mouse();
LVector3f result(mouse_point[0], mouse_point[1], 0);
result = get_frame_inv_xform().xform_point(result);
// keep the slider button within slider bar
if (result[0] < -_width)
result[0] = -_width;
if (result[0] > _width)
result[0] = _width;
_update_slider = true;
_update_x = result[0];
_update_y = result[1];
//_slider_button.set_x(result[0]);
// This also implicitly sets the managed pieces.
if (_thumb_button != (PGButton *)NULL) {
_thumb_button->set_active(active);
}
if (_left_button != (PGButton *)NULL) {
_left_button->set_active(active);
}
if (_right_button != (PGButton *)NULL) {
_right_button->set_active(active);
}
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::update
// Access: Private
// Description: Computes the appropriate size of the bar frame
// according to the percentage completed.
// Function: PGSliderBar::remanage
// Access: Published
// Description: Manages the position and size of the scroll bars and
// the thumb. Normally this should not need to be
// called directly.
////////////////////////////////////////////////////////////////////
void PGSliderBar::
update() {
// int state = get_state();
void PGSliderBar::
remanage() {
_needs_remanage = false;
// need left and right button input if they exist
if (!_slider_only) {
// Handle left and right button presses
if (_left.is_button_down()) {
// move the slider to the left
float x = _slider_button.get_x() - _speed;
_update_slider = true;
_update_x = max(x, -_width);
//_slider_button.set_x(max(x, -_width));
}
const LVecBase4f &frame = get_frame();
float width, length;
if (fabs(_axis[0]) > fabs(_axis[1] + _axis[2])) {
// The slider is X-dominant.
width = frame[3] - frame[2];
length = frame[1] - frame[0];
if (_right.is_button_down()) {
// move the slider to the right
float x = _slider_button.get_x() + _speed;
_update_slider = true;
_update_x = min(x, _width);
//_slider_button.set_x(min(x, _width));
}
} else {
// The slider is Y-dominant.
width = frame[1] - frame[0];
length = frame[3] - frame[2];
}
// press() and drag() update schedules this values that need to be
// applied here so that value of current slider position as a ratio
// of range can be updated
if (_update_slider) {
//pgui_cat.info() << "mouse_point:x " << _update_x
// << " mouse_point:y " << _update_y << endl;
if (!_slider_button.is_empty())
_slider_button.set_x(_update_x);
_mapped_value = (_update_x + _width)/(2*_width);
_value = _negative_mapping ? ((_mapped_value-0.5)*2) : _mapped_value;
_update_slider = false;
throw_event(get_click_event());
LVector3f center = LVector3f::rfu((frame[0] + frame[1]) / 2.0f,
0.0f,
(frame[2] + frame[3]) / 2.0f);
if (_left_button != (PGButton *)NULL) {
_left_button->set_frame(-width / 2.0f, width / 2.0f,
-width / 2.0f, width / 2.0f);
_left_button->xform(LMatrix4f::translate_mat(center + ((width - length) / 2.0f) * _axis));
_left_button->clear_transform();
}
if (_right_button != (PGButton *)NULL) {
_right_button->set_frame(-width / 2.0f, width / 2.0f,
-width / 2.0f, width / 2.0f);
_right_button->xform(LMatrix4f::translate_mat(center + ((length - width) / 2.0f) * _axis));
_right_button->clear_transform();
}
if (_thumb_button != (PGButton *)NULL) {
_thumb_button->set_frame(-width / 2.0f, width / 2.0f,
-width / 2.0f, width / 2.0f);
_thumb_button->set_transform(TransformState::make_pos(center));
}
recompute();
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::recompute
// Access: Published
// Description: Recomputes the position and size of the thumb.
// Normally this should not need to be called directly.
////////////////////////////////////////////////////////////////////
void PGSliderBar::
recompute() {
_needs_recompute = false;
if (_min_value != _max_value) {
_scroll_ratio = fabs(_scroll_value / (_max_value - _min_value));
_page_ratio = fabs(_page_value / (_max_value - _min_value));
} else {
_scroll_ratio = 0.0f;
_page_ratio = 0.0f;
}
LVecBase4f frame = get_frame();
reduce_region(frame, _left_button);
reduce_region(frame, _right_button);
if (fabs(_axis[0]) > fabs(_axis[1] + _axis[2])) {
// The slider is X-dominant.
_min_x = frame[0];
_max_x = frame[1];
float trough_width = _max_x - _min_x;
if (_thumb_button != (PGButton *)NULL) {
const LVecBase4f &thumb_frame = _thumb_button->get_frame();
if (_resize_thumb) {
// If we're allowed to adjust the thumb's size, we don't need to
// find out how wide it is.
_thumb_width = trough_width * min(1.0f, _page_ratio);
_thumb_button->set_frame(-_thumb_width / 2.0f, _thumb_width / 2.0f,
thumb_frame[2], thumb_frame[3]);
} else {
// If we're not adjusting the thumb's size, we do need to know
// its current width.
_thumb_width = thumb_frame[1] - thumb_frame[0];
}
}
_range_x = trough_width - _thumb_width;
const LVecBase4f &thumb_frame = _thumb_button->get_frame();
if (_axis[0] >= 0.0f) {
// The slider runs forwards, left to right.
_thumb_start = (_min_x - thumb_frame[0]) * _axis;
} else {
// The slider runs backwards: right to left.
_thumb_start = (thumb_frame[1] - _max_x) * _axis;
}
_thumb_start += LVector3f::rfu(0.0f, 0.0f, (frame[2] + frame[3]) / 2.0f);
} else {
// The slider is Y-dominant. We call it X in the variable names,
// but it's really Y (or even Z).
_min_x = frame[2];
_max_x = frame[3];
float trough_width = _max_x - _min_x;
if (_thumb_button == (PGButton *)NULL) {
_thumb_width = 0.0f;
_range_x = 0.0f;
_thumb_start.set(0.0f, 0.0f, 0.0f);
} else {
const LVecBase4f &thumb_frame = _thumb_button->get_frame();
if (_resize_thumb) {
// If we're allowed to adjust the thumb's size, we don't need to
// find out how wide it is.
_thumb_width = trough_width * min(1.0f, _page_ratio);
_thumb_button->set_frame(thumb_frame[0], thumb_frame[1],
-_thumb_width / 2.0f, _thumb_width / 2.0f);
} else {
// If we're not adjusting the thumb's size, we do need to know
// its current width.
_thumb_width = thumb_frame[3] - thumb_frame[2];
}
_range_x = trough_width - _thumb_width;
if (_axis[1] >= 0.0f && _axis[2] >= 0.0f) {
// The slider runs forwards, bottom to top.
_thumb_start = (_min_x - thumb_frame[2]) * _axis;
} else {
// The slider runs backwards: top to bottom.
_thumb_start = (thumb_frame[3] - _max_x) * _axis;
}
_thumb_start += LVector3f::rfu((frame[0] + frame[1]) / 2.0f, 0.0f, 0.0f);
}
}
reposition();
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::frame_changed
// Access: Protected, Virtual
// Description: Called when the user changes the frame size.
////////////////////////////////////////////////////////////////////
void PGSliderBar::
frame_changed() {
PGItem::frame_changed();
_needs_remanage = true;
_needs_recompute = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::item_transform_changed
// Access: Protected, Virtual
// Description: Called whenever a watched PGItem's local transform
// has been changed.
////////////////////////////////////////////////////////////////////
void PGSliderBar::
item_transform_changed(PGItem *) {
_needs_recompute = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::item_frame_changed
// Access: Protected, Virtual
// Description: Called whenever a watched PGItem's frame
// has been changed.
////////////////////////////////////////////////////////////////////
void PGSliderBar::
item_frame_changed(PGItem *) {
_needs_recompute = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::item_draw_mask_changed
// Access: Protected, Virtual
// Description: Called whenever a watched PGItem's draw_mask
// has been changed.
////////////////////////////////////////////////////////////////////
void PGSliderBar::
item_draw_mask_changed(PGItem *) {
_needs_recompute = true;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::item_press
// Access: Protected, Virtual
// Description: Called whenever the "press" event is triggered on a
// watched PGItem. See PGItem::press().
////////////////////////////////////////////////////////////////////
void PGSliderBar::
item_press(PGItem *item, const MouseWatcherParameter &param) {
if (param.has_mouse()) {
_mouse_pos = param.get_mouse();
}
if (item == _left_button || item == _right_button) {
_scroll_button_held = item;
_mouse_button_page = false;
advance_scroll();
_next_advance_time =
ClockObject::get_global_clock()->get_frame_time() + scroll_initial_delay;
} else if (item == _thumb_button) {
_scroll_button_held = NULL;
begin_drag();
}
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::item_release
// Access: Protected, Virtual
// Description: Called whenever the "release" event is triggered on a
// watched PGItem. See PGItem::release().
////////////////////////////////////////////////////////////////////
void PGSliderBar::
item_release(PGItem *item, const MouseWatcherParameter &) {
if (item == _scroll_button_held) {
_scroll_button_held = NULL;
} else if (item == _thumb_button) {
_scroll_button_held = NULL;
if (_dragging) {
end_drag();
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::item_move
// Access: Protected, Virtual
// Description: Called whenever the "move" event is triggered on a
// watched PGItem. See PGItem::move().
////////////////////////////////////////////////////////////////////
void PGSliderBar::
item_move(PGItem *item, const MouseWatcherParameter &param) {
_mouse_pos = param.get_mouse();
if (item == _thumb_button) {
if (_dragging) {
continue_drag();
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::reposition
// Access: Private
// Description: A lighter-weight version of recompute(), this just
// moves the thumb, assuming all other properties are
// unchanged.
////////////////////////////////////////////////////////////////////
void PGSliderBar::
reposition() {
_needs_reposition = false;
float t = get_ratio();
if (_thumb_button != (PGButton *)NULL) {
_thumb_button->set_transform(TransformState::make_pos((t * _range_x) * _axis + _thumb_start));
}
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::advance_scroll
// Access: Private
// Description: Advances the scroll bar by one unit in the left or
// right direction while the user is holding down the
// left or right scroll button.
////////////////////////////////////////////////////////////////////
void PGSliderBar::
advance_scroll() {
if (_scroll_button_held == _left_button) {
set_ratio(max(_ratio - _scroll_ratio, 0.0f));
} else if (_scroll_button_held == _right_button) {
set_ratio(min(_ratio + _scroll_ratio, 1.0f));
}
_next_advance_time =
ClockObject::get_global_clock()->get_frame_time() + scroll_continued_delay;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::advance_page
// Access: Private
// Description: Advances the scroll bar by one page in the left or
// right direction while the user is holding down the
// mouse button on the track.
////////////////////////////////////////////////////////////////////
void PGSliderBar::
advance_page() {
// Is the mouse position left or right of the current thumb
// position?
LPoint3f mouse = mouse_to_local(_mouse_pos) - _thumb_start;
float target_ratio = mouse.dot(_axis) / _range_x;
float t;
if (target_ratio < _ratio) {
t = max(_ratio - _page_ratio + _scroll_ratio, target_ratio);
} else {
t = min(_ratio + _page_ratio - _scroll_ratio, target_ratio);
}
set_ratio(t);
if (t == target_ratio) {
// We made it; begin dragging from now on until the user releases
// the mouse.
begin_drag();
}
_next_advance_time =
ClockObject::get_global_clock()->get_frame_time() + scroll_continued_delay;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::begin_drag
// Access: Private
// Description: Called when the user clicks down on the thumb button,
// possibly to begin dragging.
////////////////////////////////////////////////////////////////////
void PGSliderBar::
begin_drag() {
if (_needs_recompute) {
recompute();
}
if (_range_x != 0.0f) {
float current_x = mouse_to_local(_mouse_pos).dot(_axis);
_drag_start_x = current_x - get_ratio() * _range_x;
_dragging = true;
}
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::continue_drag
// Access: Private
// Description: Called as the user moves the mouse while still
// dragging on the thumb button.
////////////////////////////////////////////////////////////////////
void PGSliderBar::
continue_drag() {
if (_needs_recompute) {
recompute();
}
if (_range_x != 0.0f) {
float current_x = mouse_to_local(_mouse_pos).dot(_axis);
set_ratio((current_x - _drag_start_x) / _range_x);
}
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBar::end_drag
// Access: Private
// Description: Called as the user releases the mouse after dragging.
////////////////////////////////////////////////////////////////////
void PGSliderBar::
end_drag() {
_dragging = false;
}

View File

@ -22,15 +22,20 @@
#include "pandabase.h"
#include "pgItem.h"
#include "pgSliderButton.h"
#include "pgSliderBarNotify.h"
#include "pgButtonNotify.h"
#include "pgButton.h"
////////////////////////////////////////////////////////////////////
// Class : PGSliderBar
// Description : This is a particular kind of PGItem that draws a
// little bar with a slider that moves from left to
// right indicating a value between the ranges
// right indicating a value between the ranges.
//
// This is used as an implementation for both
// DirectSlider and for DirectScrollBar.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA PGSliderBar : public PGItem {
class EXPCL_PANDA PGSliderBar : public PGItem, public PGButtonNotify {
PUBLISHED:
PGSliderBar(const string &name = "");
virtual ~PGSliderBar();
@ -42,71 +47,110 @@ public:
virtual PandaNode *make_copy() const;
virtual bool has_cull_callback() const;
virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
virtual void xform(const LMatrix4f &mat);
virtual void press(const MouseWatcherParameter &param, bool background);
virtual void drag(const MouseWatcherParameter &param);
virtual void release(const MouseWatcherParameter &param, bool background);
virtual void move(const MouseWatcherParameter &param);
virtual void adjust();
INLINE void set_notify(PGSliderBarNotify *notify);
INLINE PGSliderBarNotify *get_notify() const;
PUBLISHED:
void setup(float width, float height, float range);
void setup_scroll_bar(bool vertical, float length, float width, float bevel);
void setup_slider(bool vertical, float length, float width, float bevel);
INLINE void set_range(float range);
INLINE float get_range() const;
INLINE void set_axis(const LVector3f &axis);
INLINE const LVector3f &get_axis() const;
//INLINE void set_magnifier(float value);
//INLINE float get_magnifier() const;
INLINE void set_range(float min_value, float max_value);
INLINE float get_min_value() const;
INLINE float get_max_value() const;
INLINE void set_scroll_size(float scroll_size);
INLINE float get_scroll_size() const;
INLINE void set_page_size(float page_size);
INLINE float get_page_size() const;
INLINE void set_value(float value);
INLINE float get_value() const;
INLINE float get_mapped_value() const;
INLINE float get_update_x() const;
INLINE float get_update_y() const;
INLINE void set_speed(float speed);
INLINE float get_speed() const;
INLINE void set_ratio(float ratio);
INLINE float get_ratio() const;
INLINE void set_scale(float speed);
INLINE float get_scale() const;
INLINE void set_resize_thumb(bool resize_thumb);
INLINE bool get_resize_thumb() const;
INLINE void set_slider_only(bool value);
INLINE bool get_slider_only() const ;
INLINE void set_negative_mapping(bool value);
INLINE bool get_negative_mapping() const ;
INLINE void set_manage_pieces(bool manage_pieces);
INLINE bool get_manage_pieces() const;
INLINE void set_bar_style(const PGFrameStyle &style);
INLINE PGFrameStyle get_bar_style() const;
INLINE string get_click_event() const;
INLINE void set_thumb_button(PGButton *thumb_button);
INLINE void clear_thumb_button();
INLINE PGButton *get_thumb_button() const;
INLINE void set_slider_button(NodePath &np, PGSliderButton *button);
INLINE void set_left_button(PGButton *left_button);
INLINE void clear_left_button();
INLINE PGButton *get_left_button() const;
INLINE NodePath get_slider_button() const;
INLINE NodePath get_left_button() const;
INLINE NodePath get_right_button() const;
INLINE void set_right_button(PGButton *right_button);
INLINE void clear_right_button();
INLINE PGButton *get_right_button() const;
INLINE static string get_adjust_prefix();
INLINE string get_adjust_event() const;
virtual void set_active(bool active);
void remanage();
void recompute();
protected:
virtual void frame_changed();
virtual void item_transform_changed(PGItem *item);
virtual void item_frame_changed(PGItem *item);
virtual void item_draw_mask_changed(PGItem *item);
virtual void item_press(PGItem *item, const MouseWatcherParameter &param);
virtual void item_release(PGItem *item, const MouseWatcherParameter &param);
virtual void item_move(PGItem *item, const MouseWatcherParameter &param);
private:
void update();
void reposition();
void advance_scroll();
void advance_page();
void begin_drag();
void continue_drag();
void end_drag();
bool _slider_only;
bool _negative_mapping;
bool _update_slider;
private:
bool _needs_remanage;
bool _needs_recompute;
bool _needs_reposition;
// These 3 variables control slider range
float _value;
float _mapped_value;
float _update_x;
float _update_y;
float _min_value, _max_value;
float _scroll_value, _scroll_ratio;
float _page_value, _page_ratio;
float _ratio;
bool _resize_thumb;
bool _manage_pieces;
float _range;
float _speed, _width;
float _scale;
int _bar_state;
PGFrameStyle _bar_style;
PGSliderButton *_slider;
PGSliderButton _left;
PGSliderButton _right;
NodePath _bar;
NodePath _slider_button;
NodePath _left_button;
NodePath _right_button;
LVector3f _axis;
PT(PGButton) _thumb_button;
PT(PGButton) _left_button;
PT(PGButton) _right_button;
float _min_x, _max_x, _thumb_width, _range_x;
LPoint3f _thumb_start;
PGItem *_scroll_button_held;
bool _mouse_button_page;
LPoint2f _mouse_pos;
double _next_advance_time;
bool _dragging;
float _drag_start_x;
public:
static TypeHandle get_class_type() {

View File

@ -0,0 +1,27 @@
// Filename: pgSliderBarNotify.I
// Created by: drose (18Aug05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: PGSliderBarNotify::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE PGSliderBarNotify::
PGSliderBarNotify() {
}

View File

@ -0,0 +1,40 @@
// Filename: pgSliderBarNotify.cxx
// Created by: drose (18Aug05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "pgSliderBarNotify.h"
#include "pgSliderBar.h"
////////////////////////////////////////////////////////////////////
// Function: PGSliderBarNotify::slider_bar_adjust
// Access: Protected, Virtual
// Description: Called whenever a watched PGSliderBar's value
// has been changed by the user or programmatically.
////////////////////////////////////////////////////////////////////
void PGSliderBarNotify::
slider_bar_adjust(PGSliderBar *) {
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderBarNotify::slider_bar_set_range
// Access: Protected, Virtual
// Description: Called whenever a watched PGSliderBar's overall range
// has been changed.
////////////////////////////////////////////////////////////////////
void PGSliderBarNotify::
slider_bar_set_range(PGSliderBar *) {
}

View File

@ -0,0 +1,46 @@
// Filename: pgSliderBarNotify.h
// Created by: drose (18Aug05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef PGSLIDERBARNOTIFY_H
#define PGSLIDERBARNOTIFY_H
#include "pandabase.h"
#include "pgItemNotify.h"
class PGSliderBar;
////////////////////////////////////////////////////////////////////
// Class : PGSliderBarNotify
// Description : Objects that inherit from this class can receive
// notify messages when a slider bar moves or otherwise
// is reconfigured.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA PGSliderBarNotify : public PGItemNotify {
public:
INLINE PGSliderBarNotify();
protected:
virtual void slider_bar_adjust(PGSliderBar *slider_bar);
virtual void slider_bar_set_range(PGSliderBar *slider_bar);
friend class PGSliderBar;
};
#include "pgSliderBarNotify.I"
#endif

View File

@ -1,94 +0,0 @@
// Filename: pgSliderButton.cxx
// Created by: masad (21Oct04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "pgSliderButton.h"
#include "pgSliderBar.h"
#include "dcast.h"
#include "pgMouseWatcherParameter.h"
#include "throw_event.h"
#include "mouseButton.h"
#include "mouseWatcherParameter.h"
#include "colorAttrib.h"
#include "transformState.h"
TypeHandle PGSliderButton::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: PGSliderButton::Constructor
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
PGSliderButton::
PGSliderButton(const string &name) : PGButton(name)
{
_drag_n_drop = false;
_slider_bar = NULL;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderButton::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
PGSliderButton::
~PGSliderButton() {
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderButton::Copy Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
PGSliderButton::
PGSliderButton(const PGSliderButton &copy) :
PGButton(copy),
_drag_n_drop(copy._drag_n_drop)
// _slider_bar(copy._slider_bar)
{
_slider_bar = NULL;
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderButton::make_copy
// Access: Public, Virtual
// Description: Returns a newly-allocated Node that is a shallow copy
// of this one. It will be a different Node pointer,
// but its internal data may or may not be shared with
// that of the original Node.
////////////////////////////////////////////////////////////////////
PandaNode *PGSliderButton::
make_copy() const {
return new PGSliderButton(*this);
}
////////////////////////////////////////////////////////////////////
// Function: PGSliderButton::move
// Access: Public, Virtual
// Description: This is a callback hook function, called whenever the
// button is dragged left or right by the user normally.
////////////////////////////////////////////////////////////////////
void PGSliderButton::
move(const MouseWatcherParameter &param) {
PGButton::move(param);
if (_drag_n_drop && is_button_down()) {
PGSliderBar *slider = DCAST(PGSliderBar, _slider_bar);
slider->drag(param);
//pgui_cat.info() << get_name() << "::move()" << endl;
}
}

View File

@ -1,77 +0,0 @@
// Filename: pgSliderButton.h
// Created by: masad (21Oct04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef PGSLIDERBUTTON_H
#define PGSLIDERBUTTON_H
#include "pandabase.h"
#include "pgButton.h"
#include "nodePath.h"
#include "pset.h"
////////////////////////////////////////////////////////////////////
// Class : PGSliderButton
// Description : This is a particular kind of PGItem that is
// specialized to behave like a normal button object.
// It keeps track of its own state, and handles mouse
// events sensibly.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA PGSliderButton : public PGButton {
PUBLISHED:
PGSliderButton(const string &name);
virtual ~PGSliderButton();
public:
virtual void move(const MouseWatcherParameter &param);
PGSliderButton(const PGSliderButton &copy);
PUBLISHED:
INLINE bool is_drag_n_drop();
INLINE void set_drag_n_drop(bool value);
INLINE void set_slider_bar(PGItem *item);
public:
virtual PandaNode *make_copy() const;
bool _drag_n_drop;
PGItem *_slider_bar;
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
PGButton::init_type();
register_type(_type_handle, "PGSliderButton",
PGButton::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
};
#include "pgSliderButton.I"
#endif

View File

@ -42,6 +42,18 @@ get_mouse_watcher() const {
return _watcher;
}
////////////////////////////////////////////////////////////////////
// Function: PGTop::get_group
// Access: Published
// Description: Returns the MouseWatcherGroup pointer that the PGTop
// object registers its PG items with, or NULL if the
// MouseWatcher has not yet been set.
////////////////////////////////////////////////////////////////////
INLINE MouseWatcherGroup *PGTop::
get_group() const {
return _watcher_group;
}
////////////////////////////////////////////////////////////////////
// Function: PGTop::set_start_sort
// Access: Published
@ -83,15 +95,12 @@ get_start_sort() const {
// Function: PGTop::add_region
// Access: Public
// Description: Adds the indicated region to the set of regions in
// the group. Returns true if it was successfully
// added, or false if it was already on the list.
// the group.
////////////////////////////////////////////////////////////////////
INLINE bool PGTop::
INLINE void PGTop::
add_region(MouseWatcherRegion *region) {
if (_watcher_group == (PGMouseWatcherGroup *)NULL) {
return false;
}
return _watcher_group->add_region(region);
nassertv(_watcher_group != (PGMouseWatcherGroup *)NULL);
_watcher_group->add_region(region);
}
////////////////////////////////////////////////////////////////////

View File

@ -60,13 +60,14 @@ public:
PUBLISHED:
void set_mouse_watcher(MouseWatcher *watcher);
INLINE MouseWatcher *get_mouse_watcher() const;
INLINE MouseWatcherGroup *get_group() const;
INLINE void set_start_sort(int start_sort);
INLINE int get_start_sort() const;
public:
// These methods duplicate the functionality of MouseWatcherGroup.
INLINE bool add_region(MouseWatcherRegion *region);
INLINE void add_region(MouseWatcherRegion *region);
INLINE void clear_regions();
private:

View File

@ -0,0 +1,104 @@
// Filename: pgVirtualFrame.I
// Created by: drose (17Aug05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: PGVirtualFrame::set_clip_frame
// Access: Published
// Description: Sets the bounding rectangle of the clip frame.
// This is the size of the small window through which we
// can see the virtual canvas. Normally, this is the
// same size as the actual frame or smaller (typically
// it is smaller by the size of the bevel, or to make
// room for scroll bars).
////////////////////////////////////////////////////////////////////
INLINE void PGVirtualFrame::
set_clip_frame(float left, float right, float bottom, float top) {
set_clip_frame(LVecBase4f(left, right, bottom, top));
}
////////////////////////////////////////////////////////////////////
// Function: PGVirtualFrame::get_clip_frame
// Access: Published
// Description: Returns the bounding rectangle of the clip frame.
// See set_clip_frame(). If has_clip_frame() is
// false, this returns the item's actual frame.
////////////////////////////////////////////////////////////////////
INLINE const LVecBase4f &PGVirtualFrame::
get_clip_frame() const {
return _has_clip_frame ? _clip_frame : get_frame();
}
////////////////////////////////////////////////////////////////////
// Function: PGVirtualFrame::has_clip_frame
// Access: Published
// Description: Returns true if the clip frame has been set; see
// set_clip_frame(). If it has not been set, objects in
// the virtual frame will not be clipped.
////////////////////////////////////////////////////////////////////
INLINE bool PGVirtualFrame::
has_clip_frame() const {
return _has_clip_frame;
}
////////////////////////////////////////////////////////////////////
// Function: PGVirtualFrame::set_canvas_transform
// Access: Published
// Description: Changes the transform of the virtual canvas. This
// transform is applied to all child nodes of the
// canvas_node.
////////////////////////////////////////////////////////////////////
INLINE void PGVirtualFrame::
set_canvas_transform(const TransformState *transform) {
_canvas_node->set_transform(transform);
}
////////////////////////////////////////////////////////////////////
// Function: PGVirtualFrame::get_canvas_transform
// Access: Published
// Description: Returns the transform of the virtual canvas. This
// transform is applied to all child nodes of the
// canvas_node.
////////////////////////////////////////////////////////////////////
INLINE const TransformState *PGVirtualFrame::
get_canvas_transform() const {
return _canvas_node->get_transform();
}
////////////////////////////////////////////////////////////////////
// Function: PGVirtualFrame::get_canvas_node
// Access: Published
// Description: Returns the special node that holds all of the
// children that appear in the virtual canvas.
////////////////////////////////////////////////////////////////////
INLINE PandaNode *PGVirtualFrame::
get_canvas_node() const {
return _canvas_node;
}
////////////////////////////////////////////////////////////////////
// Function: PGVirtualFrame::get_clip_plane_node
// Access: Published
// Description: Returns the special node that holds all of PlaneNodes
// that are used for applying the clip planes to the
// canvas_node.
////////////////////////////////////////////////////////////////////
INLINE PandaNode *PGVirtualFrame::
get_clip_plane_node() const {
return _clip_plane_node;
}

View File

@ -0,0 +1,241 @@
// Filename: pgVirtualFrame.cxx
// Created by: drose (17Aug05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "pgVirtualFrame.h"
#include "clipPlaneAttrib.h"
#include "planeNode.h"
#include "sceneGraphReducer.h"
TypeHandle PGVirtualFrame::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: PGVirtualFrame::Constructor
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
PGVirtualFrame::
PGVirtualFrame(const string &name) : PGItem(name)
{
_has_clip_frame = false;
_clip_frame.set(0.0f, 0.0f, 0.0f, 0.0f);
setup_child_nodes();
}
////////////////////////////////////////////////////////////////////
// Function: PGVirtualFrame::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
PGVirtualFrame::
~PGVirtualFrame() {
}
////////////////////////////////////////////////////////////////////
// Function: PGVirtualFrame::Copy Constructor
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
PGVirtualFrame::
PGVirtualFrame(const PGVirtualFrame &copy) :
PGItem(copy),
_has_clip_frame(copy._has_clip_frame),
_clip_frame(copy._clip_frame)
{
setup_child_nodes();
// Reassign the clip planes according to the clip frame.
if (_has_clip_frame) {
set_clip_frame(_clip_frame);
} else {
clear_clip_frame();
}
}
////////////////////////////////////////////////////////////////////
// Function: PGVirtualFrame::make_copy
// Access: Public, Virtual
// Description: Returns a newly-allocated Node that is a shallow copy
// of this one. It will be a different Node pointer,
// but its internal data may or may not be shared with
// that of the original Node.
////////////////////////////////////////////////////////////////////
PandaNode *PGVirtualFrame::
make_copy() const {
return new PGVirtualFrame(*this);
}
////////////////////////////////////////////////////////////////////
// Function: PGVirtualFrame::r_copy_children
// Access: Protected, Virtual
// Description: This is called by r_copy_subgraph(); the copy has
// already been made of this particular node (and this
// is the copy); this function's job is to copy all of
// the children from the original.
//
// Note that it includes the parameter inst_map, which
// is a map type, and is not (and cannot be) exported
// from PANDA.DLL. Thus, any derivative of PandaNode
// that is not also a member of PANDA.DLL *cannot*
// access this map, and probably should not even
// override this function.
////////////////////////////////////////////////////////////////////
void PGVirtualFrame::
r_copy_children(const PandaNode *from, PandaNode::InstanceMap &inst_map) {
PandaNode::r_copy_children(from, inst_map);
// Reassign the canvas_node to point to the new copy, if it's there.
const PGVirtualFrame *from_frame = DCAST(PGVirtualFrame, from);
PandaNode *from_canvas_node = from_frame->get_canvas_node();
PandaNode *from_clip_plane_node = from_frame->get_clip_plane_node();
InstanceMap::const_iterator ci;
ci = inst_map.find(from_canvas_node);
if (ci != inst_map.end()) {
remove_child(_canvas_node);
_canvas_node = DCAST(ModelNode, (*ci).second);
}
ci = inst_map.find(from_clip_plane_node);
if (ci != inst_map.end()) {
remove_child(_clip_plane_node);
_clip_plane_node = (*ci).second;
}
// Reassign the clip planes according to the clip frame.
if (_has_clip_frame) {
set_clip_frame(_clip_frame);
} else {
clear_clip_frame();
}
}
////////////////////////////////////////////////////////////////////
// Function: PGVirtualFrame::setup
// Access: Published
// Description: Creates a PGVirtualFrame with the indicated
// dimensions.
////////////////////////////////////////////////////////////////////
void PGVirtualFrame::
setup(float width, float height) {
set_state(0);
clear_state_def(0);
set_frame(0, width, 0, height);
float bevel = 0.05f;
PGFrameStyle style;
style.set_width(bevel, bevel);
style.set_color(0.8f, 0.8f, 0.8f, 1.0f);
style.set_type(PGFrameStyle::T_bevel_out);
set_frame_style(0, style);
set_clip_frame(bevel, width - 2 * bevel,
bevel, height - 2 * bevel);
}
////////////////////////////////////////////////////////////////////
// Function: PGVirtualFrame::set_clip_frame
// Access: Published
// Description: Sets the bounding rectangle of the clip frame.
// This is the size of the small window through which we
// can see the virtual canvas. Normally, this is the
// same size as the actual frame or smaller (typically
// it is smaller by the size of the bevel, or to make
// room for scroll bars).
////////////////////////////////////////////////////////////////////
void PGVirtualFrame::
set_clip_frame(const LVecBase4f &frame) {
if (!_has_clip_frame || _clip_frame != frame) {
_has_clip_frame = true;
_clip_frame = frame;
const LVector3f r = LVector3f::right();
const LVector3f u = LVector3f::up();
PT(PlaneNode) left_clip =
new PlaneNode("left_clip", Planef(r, r * _clip_frame[0]));
PT(PlaneNode) right_clip =
new PlaneNode("right_clip", Planef(-r, r * _clip_frame[1]));
PT(PlaneNode) bottom_clip =
new PlaneNode("bottom_clip", Planef(u, u * _clip_frame[2]));
PT(PlaneNode) top_clip =
new PlaneNode("top_clip", Planef(-u, u * _clip_frame[3]));
_clip_plane_node->remove_all_children();
_clip_plane_node->add_child(left_clip);
_clip_plane_node->add_child(right_clip);
_clip_plane_node->add_child(bottom_clip);
_clip_plane_node->add_child(top_clip);
CPT(ClipPlaneAttrib) plane_attrib = DCAST(ClipPlaneAttrib, ClipPlaneAttrib::make());
plane_attrib = DCAST(ClipPlaneAttrib, plane_attrib->add_on_plane(NodePath::any_path(left_clip)));
plane_attrib = DCAST(ClipPlaneAttrib, plane_attrib->add_on_plane(NodePath::any_path(right_clip)));
plane_attrib = DCAST(ClipPlaneAttrib, plane_attrib->add_on_plane(NodePath::any_path(bottom_clip)));
plane_attrib = DCAST(ClipPlaneAttrib, plane_attrib->add_on_plane(NodePath::any_path(top_clip)));
_canvas_node->set_attrib(plane_attrib);
clip_frame_changed();
}
}
////////////////////////////////////////////////////////////////////
// Function: PGVirtualFrame::clear_clip_frame
// Access: Published
// Description: Removes the clip frame from the item. This
// disables clipping.
////////////////////////////////////////////////////////////////////
void PGVirtualFrame::
clear_clip_frame() {
if (_has_clip_frame) {
_has_clip_frame = false;
_canvas_node->clear_attrib(ClipPlaneAttrib::get_class_type());
clip_frame_changed();
}
}
////////////////////////////////////////////////////////////////////
// Function: PGVirtualFrame::clip_frame_changed
// Access: Protected, Virtual
// Description: Called when the user changes the clip_frame size.
////////////////////////////////////////////////////////////////////
void PGVirtualFrame::
clip_frame_changed() {
}
////////////////////////////////////////////////////////////////////
// Function: PGVirtualFrame::setup_child_nodes
// Access: Private
// Description: Creates the special canvas_node and clip_plane_node
// for this object.
////////////////////////////////////////////////////////////////////
void PGVirtualFrame::
setup_child_nodes() {
_canvas_node = new ModelNode("canvas");
_canvas_node->set_preserve_transform(ModelNode::PT_local);
// We have to preserve the clip plane attribute.
_canvas_node->set_preserve_attributes(SceneGraphReducer::TT_other);
add_child(_canvas_node);
_clip_plane_node = new PandaNode("clip_planes");
add_child(_clip_plane_node);
}

View File

@ -0,0 +1,112 @@
// Filename: pgVirtualFrame.h
// Created by: drose (17Aug05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef PGVIRTUALFRAME_H
#define PGVIRTUALFRAME_H
#include "pandabase.h"
#include "pgItem.h"
#include "modelNode.h"
class TransformState;
////////////////////////////////////////////////////////////////////
// Class : PGVirtualFrame
// Description : This represents a frame that is rendered as a window
// onto another (possibly much larger) canvas. You can
// only see the portion of the canvas that is below the
// window at any given time.
//
// This works simply by automatically defining clipping
// planes to be applied to a special child node, called
// the canvas_node, of the PGVirtualFrame node. Every
// object that is parented to the canvas_node will be
// clipped by the clip_frame. Also, you can modify the
// canvas_transform through convenience methods here,
// which actually modifies the transform on the
// canvas_node.
//
// The net effect is that the virtual canvas is
// arbitrarily large, and we can peek at it through the
// clip_frame, and scroll through different parts of it
// by modifying the canvas_transform.
//
// See PGScrollFrame for a specialization of this class
// that handles the traditional scrolling canvas, with
// scroll bars.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA PGVirtualFrame : public PGItem {
PUBLISHED:
PGVirtualFrame(const string &name = "");
virtual ~PGVirtualFrame();
protected:
PGVirtualFrame(const PGVirtualFrame &copy);
virtual PandaNode *make_copy() const;
virtual void r_copy_children(const PandaNode *from, InstanceMap &inst_map);
PUBLISHED:
void setup(float width, float height);
INLINE void set_clip_frame(float left, float right, float bottom, float top);
void set_clip_frame(const LVecBase4f &clip_frame);
INLINE const LVecBase4f &get_clip_frame() const;
INLINE bool has_clip_frame() const;
void clear_clip_frame();
INLINE void set_canvas_transform(const TransformState *transform);
INLINE const TransformState *get_canvas_transform() const;
INLINE PandaNode *get_canvas_node() const;
INLINE PandaNode *get_clip_plane_node() const;
protected:
virtual void clip_frame_changed();
private:
void setup_child_nodes();
private:
bool _has_clip_frame;
LVecBase4f _clip_frame;
PT(ModelNode) _canvas_node;
PT(PandaNode) _clip_plane_node;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
PGItem::init_type();
register_type(_type_handle, "PGVirtualFrame",
PGItem::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
};
#include "pgVirtualFrame.I"
#endif

View File

@ -113,7 +113,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
////////////////////////////////////////////////////////////////////
// Function: PGWaitBar::setup
// Access: Public
// Access: Published
// Description: Creates a PGWaitBar with the indicated dimensions,
// with the indicated maximum range.
////////////////////////////////////////////////////////////////////
@ -124,8 +124,10 @@ setup(float width, float height, float range) {
set_frame(-0.5f * width, 0.5f * width, -0.5f * height, 0.5f * height);
float bevel = 0.05f;
PGFrameStyle style;
style.set_width(0.05f, 0.05f);
style.set_width(bevel, bevel);
style.set_color(0.6f, 0.6f, 0.6f, 1.0f);
style.set_type(PGFrameStyle::T_bevel_in);

View File

@ -1,9 +1,10 @@
#include "config_pgui.cxx"
#include "pgButton.cxx"
#include "pgSliderButton.cxx"
#include "pgButtonNotify.cxx"
#include "pgCullTraverser.cxx"
#include "pgEntry.cxx"
#include "pgMouseWatcherGroup.cxx"
#include "pgMouseWatcherParameter.cxx"
#include "pgFrameStyle.cxx"
#include "pgItem.cxx"
#include "pgItemNotify.cxx"

View File

@ -1,9 +1,9 @@
#include "pgFrameStyle.cxx"
#include "pgItem.cxx"
#include "pgMouseWatcherBackground.cxx"
#include "pgMouseWatcherRegion.cxx"
#include "pgTop.cxx"
#include "pgWaitBar.cxx"
#include "pgScrollFrame.cxx"
#include "pgSliderBar.cxx"
#include "pgSliderBarNotify.cxx"
#include "pgTop.cxx"
#include "pgVirtualFrame.cxx"
#include "pgWaitBar.cxx"

View File

@ -35,9 +35,10 @@ static const unsigned short _bam_major_ver = 5;
// Bumped to major version 4 on 4/10/02 to store new scene graph.
// Bumped to major version 5 on 5/6/05 for new Geom implementation.
static const unsigned short _bam_minor_ver = 2;
static const unsigned short _bam_minor_ver = 3;
// Bumped to minor version 1 on 7/14/05 to add TextureStage::_saved_result.
// Bumped to minor version 2 on 7/21/05 to add TransformState::is_2d.
// Bumped to minor version 3 on 8/25/05 to add ModelNode::_preserve_attributes.
#endif

View File

@ -4,7 +4,7 @@
#begin lib_target
#define TARGET tform
#define LOCAL_LIBS \
dgraph pgraph linmath display event putil gobj gsgbase \
grutil dgraph pgraph linmath display event putil gobj gsgbase \
mathutil device
#define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx

View File

@ -29,6 +29,7 @@
#include "transformState.h"
#include "displayRegion.h"
#include "dcast.h"
#include "indent.h"
#include <algorithm>
@ -122,58 +123,9 @@ get_over_region(const LPoint2f &pos) const {
return get_preferred_region(regions);
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcher::output
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MouseWatcher::
output(ostream &out) const {
DataNode::output(out);
int count = _regions.size();
Groups::const_iterator gi;
for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
MouseWatcherGroup *group = (*gi);
count += group->_regions.size();
}
out << " (" << count << " regions)";
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcher::write
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MouseWatcher::
write(ostream &out, int indent_level) const {
indent(out, indent_level)
<< "MouseWatcher " << get_name() << ":\n";
Regions::const_iterator ri;
for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
MouseWatcherRegion *region = (*ri);
region->write(out, indent_level + 2);
}
if (!_groups.empty()) {
Groups::const_iterator gi;
for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
MouseWatcherGroup *group = (*gi);
indent(out, indent_level + 2)
<< "Subgroup:\n";
for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) {
MouseWatcherRegion *region = (*ri);
region->write(out, indent_level + 4);
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcher::add_group
// Access: Public
// Access: Published
// Description: Adds the indicated group of regions to the set of
// regions the MouseWatcher will monitor each frame.
//
@ -207,7 +159,7 @@ add_group(MouseWatcherGroup *group) {
////////////////////////////////////////////////////////////////////
// Function: MouseWatcher::remove_group
// Access: Public
// Access: Published
// Description: Removes the indicated group from the set of extra
// groups associated with the MouseWatcher. Returns
// true if successful, or false if the group was already
@ -237,6 +189,71 @@ remove_group(MouseWatcherGroup *group) {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcher::get_num_groups
// Access: Published
// Description: Returns the number of separate groups added to the
// MouseWatcher via add_group().
////////////////////////////////////////////////////////////////////
int MouseWatcher::
get_num_groups() const {
return _groups.size();
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcher::get_group
// Access: Published
// Description: Returns the nth group added to the MouseWatcher via
// add_group().
////////////////////////////////////////////////////////////////////
MouseWatcherGroup *MouseWatcher::
get_group(int n) const {
nassertr(n >= 0 && n < (int)_groups.size(), NULL);
return _groups[n];
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcher::output
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MouseWatcher::
output(ostream &out) const {
DataNode::output(out);
int count = _regions.size();
Groups::const_iterator gi;
for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
MouseWatcherGroup *group = (*gi);
count += group->_regions.size();
}
out << " (" << count << " regions)";
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcher::write
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MouseWatcher::
write(ostream &out, int indent_level) const {
indent(out, indent_level)
<< "MouseWatcher " << get_name() << ":\n";
MouseWatcherGroup::write(out, indent_level + 2);
if (!_groups.empty()) {
Groups::const_iterator gi;
for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
MouseWatcherGroup *group = (*gi);
indent(out, indent_level + 2)
<< "Subgroup:\n";
group->write(out, indent_level + 4);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcher::get_over_regions
// Access: Protected
@ -611,14 +628,14 @@ throw_event_pattern(const string &pattern, const MouseWatcherRegion *region,
// being moved from last position.
////////////////////////////////////////////////////////////////////
void MouseWatcher::
move(ButtonHandle button) {
move() {
MouseWatcherParameter param;
param.set_button(button);
param.set_modifier_buttons(_mods);
param.set_mouse(_mouse);
if (_preferred_button_down_region != (MouseWatcherRegion *)NULL)
if (_preferred_button_down_region != (MouseWatcherRegion *)NULL) {
_preferred_button_down_region->move(param);
}
}
////////////////////////////////////////////////////////////////////
@ -980,7 +997,6 @@ consider_keyboard_suppress(const MouseWatcherRegion *region) {
////////////////////////////////////////////////////////////////////
void MouseWatcher::
do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
bool mouse_moved = false;
// Initially, we do not suppress any events to objects below us in
// the data graph.
_internal_suppress = 0;
@ -1002,7 +1018,7 @@ do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
// Asad: determine if mouse moved from last position
const LVecBase2f &last_f = _xy->get_value();
if (f != last_f) {
mouse_moved = true;
move();
}
if (_display_region != (DisplayRegion *)NULL) {
@ -1083,10 +1099,6 @@ do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
if (_has_mouse &&
(_internal_suppress & MouseWatcherRegion::SF_mouse_position) == 0) {
if (mouse_moved) {
move(ButtonHandle::none());
//tform_cat.info() << "do_transmit_data()::mouse_moved" << endl;
}
// Transmit the mouse position.
_xy->set_value(_mouse);
output.set_data(_xy_output, EventParameter(_xy));

View File

@ -109,13 +109,15 @@ PUBLISHED:
INLINE DisplayRegion *get_display_region() const;
INLINE bool has_display_region() const;
bool add_group(MouseWatcherGroup *group);
bool remove_group(MouseWatcherGroup *group);
int get_num_groups() const;
MouseWatcherGroup *get_group(int n) const;
public:
virtual void output(ostream &out) const;
virtual void write(ostream &out, int indent_level = 0) const;
bool add_group(MouseWatcherGroup *group);
bool remove_group(MouseWatcherGroup *group);
protected:
typedef pvector< PT(MouseWatcherRegion) > VRegions;
void get_over_regions(VRegions &regions, const LPoint2f &pos) const;
@ -136,7 +138,7 @@ protected:
const MouseWatcherRegion *region,
const ButtonHandle &button);
void move(ButtonHandle button);
void move();
void press(ButtonHandle button);
void release(ButtonHandle button);
void keystroke(int keycode);

View File

@ -17,10 +17,24 @@
////////////////////////////////////////////////////////////////////
#include "mouseWatcherGroup.h"
#include "lineSegs.h"
#include "indent.h"
TypeHandle MouseWatcherGroup::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: MouseWatcherGroup::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
MouseWatcherGroup::
MouseWatcherGroup() {
#ifndef NDEBUG
_show_regions = false;
_color.set(0.4f, 0.6f, 1.0f, 1.0f);
#endif // NDEBUG
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcherGroup::Destructor
// Access: Public, Virtual
@ -34,31 +48,30 @@ MouseWatcherGroup::
// Function: MouseWatcherGroup::add_region
// Access: Published
// Description: Adds the indicated region to the set of regions in
// the group. Returns true if it was successfully
// added, or false if it was already on the list.
// the group. It is an error to add the same region to
// the set more than once.
////////////////////////////////////////////////////////////////////
bool MouseWatcherGroup::
void MouseWatcherGroup::
add_region(MouseWatcherRegion *region) {
//return _regions.insert(region).second;
PT(MouseWatcherRegion) pt = region;
// We will only bother to check for duplicates in the region list if
// we are building opt 1 or 2. The overhead for doing this may be
// too high if we have many regions.
#ifdef _DEBUG
// we are building a development Panda. The overhead for doing this
// may be too high if we have many regions.
#ifndef NDEBUG
// See if the region is in the set/vector already
Regions::const_iterator ri =
find(_regions.begin(), _regions.end(), pt);
if (ri != _regions.end()) {
// Already in the set, return false
return false;
}
#endif
nassertv(ri == _regions.end());
// Also add it to the vizzes if we have them.
if (_show_regions) {
nassertv(_vizzes.size() == _regions.size());
_vizzes.push_back(make_viz_region(pt));
}
#endif // NDEBUG
// Not in the set, add it and return true
_regions.push_back(pt);
return true;
}
////////////////////////////////////////////////////////////////////
@ -69,7 +82,7 @@ add_region(MouseWatcherRegion *region) {
////////////////////////////////////////////////////////////////////
bool MouseWatcherGroup::
has_region(MouseWatcherRegion *region) const {
// See if the region is in the set/vector
// See if the region is in the vector.
PT(MouseWatcherRegion) pt = region;
Regions::const_iterator ri =
find(_regions.begin(), _regions.end(), pt);
@ -90,14 +103,23 @@ has_region(MouseWatcherRegion *region) const {
////////////////////////////////////////////////////////////////////
bool MouseWatcherGroup::
remove_region(MouseWatcherRegion *region) {
//return _regions.erase(region) != 0;
// See if the region is in the set/vector
// See if the region is in the vector.
PT(MouseWatcherRegion) pt = region;
Regions::iterator ri =
find(_regions.begin(), _regions.end(), pt);
if (ri != _regions.end()) {
// Found it, now erase it
#ifndef NDEBUG
// Also remove it from the vizzes.
if (_show_regions) {
nassertr(_vizzes.size() == _regions.size(), false);
size_t index = ri - _regions.begin();
Vizzes::iterator vi = _vizzes.begin() + index;
_show_regions_root.node()->remove_child(*vi);
_vizzes.erase(vi);
}
#endif // NDEBUG
_regions.erase(ri);
return true;
}
@ -134,4 +156,152 @@ find_region(const string &name) const {
void MouseWatcherGroup::
clear_regions() {
_regions.clear();
#ifndef NDEBUG
if (_show_regions) {
_show_regions_root.node()->remove_all_children();
_vizzes.clear();
}
#endif // NDEBUG
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcherGroup::get_num_regions
// Access: Published
// Description: Returns the number of regions in the group.
////////////////////////////////////////////////////////////////////
int MouseWatcherGroup::
get_num_regions() const {
return _regions.size();
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcherGroup::get_region
// Access: Published
// Description: Returns the nth regions in the group.
////////////////////////////////////////////////////////////////////
MouseWatcherRegion *MouseWatcherGroup::
get_region(int n) const {
nassertr(n >= 0 && n < (int)_regions.size(), NULL);
return _regions[n];
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcherGroup::output
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
void MouseWatcherGroup::
output(ostream &out) const {
out << "MouseWatcherGroup (" << _regions.size() << " regions)";
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcherGroup::write
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
void MouseWatcherGroup::
write(ostream &out, int indent_level) const {
Regions::const_iterator ri;
for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
MouseWatcherRegion *region = (*ri);
region->write(out, indent_level);
}
}
#ifndef NDEBUG
////////////////////////////////////////////////////////////////////
// Function: MouseWatcherGroup::show_regions
// Access: Published
// Description: Enables the visualization of all of the regions
// handled by this MouseWatcherGroup. The supplied
// NodePath should be the root of the 2-d scene graph
// for the window.
////////////////////////////////////////////////////////////////////
void MouseWatcherGroup::
show_regions(const NodePath &render2d) {
_show_regions = true;
_show_regions_root = render2d.attach_new_node("show_regions");
_show_regions_root.set_bin("unsorted", 0);
update_regions();
}
#endif // NDEBUG
#ifndef NDEBUG
////////////////////////////////////////////////////////////////////
// Function: MouseWatcherGroup::set_color
// Access: Published
// Description: Specifies the color used to draw the region
// rectangles for the regions visualized by
// show_regions().
////////////////////////////////////////////////////////////////////
void MouseWatcherGroup::
set_color(const Colorf &color) {
_color = color;
update_regions();
}
#endif // NDEBUG
#ifndef NDEBUG
////////////////////////////////////////////////////////////////////
// Function: MouseWatcherGroup::hide_regions
// Access: Published
// Description: Stops the visualization created by a previous call to
// show_regions().
////////////////////////////////////////////////////////////////////
void MouseWatcherGroup::
hide_regions() {
_show_regions_root.remove_node();
_show_regions = false;
_vizzes.clear();
}
#endif // NDEBUG
#ifndef NDEBUG
////////////////////////////////////////////////////////////////////
// Function: MouseWatcherGroup::update_regions
// Access: Private
// Description: Internally regenerates the show_regions()
// visualization.
////////////////////////////////////////////////////////////////////
void MouseWatcherGroup::
update_regions() {
_show_regions_root.node()->remove_all_children();
_vizzes.clear();
_vizzes.reserve(_regions.size());
Regions::const_iterator ri;
for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
_vizzes.push_back(make_viz_region(*ri));
}
}
#endif // NDEBUG
#ifndef NDEBUG
////////////////////////////////////////////////////////////////////
// Function: MouseWatcherGroup::make_viz_region
// Access: Private
// Description: Creates a node to represent the indicated region, and
// attaches it to the _show_regions_root. Does not add
// it to _vizzes.
////////////////////////////////////////////////////////////////////
PandaNode *MouseWatcherGroup::
make_viz_region(MouseWatcherRegion *region) {
LineSegs ls("show_regions");
ls.set_color(_color);
const LVecBase4f &f = region->get_frame();
ls.move_to(LVector3f::rfu(f[0], 0.0f, f[2]));
ls.draw_to(LVector3f::rfu(f[1], 0.0f, f[2]));
ls.draw_to(LVector3f::rfu(f[1], 0.0f, f[3]));
ls.draw_to(LVector3f::rfu(f[0], 0.0f, f[3]));
ls.draw_to(LVector3f::rfu(f[0], 0.0f, f[2]));
PT(PandaNode) node = ls.create();
_show_regions_root.attach_new_node(node);
return node;
}
#endif // NDEBUG

View File

@ -25,6 +25,7 @@
#include "pointerTo.h"
#include "referenceCount.h"
#include "pvector.h"
#include "nodePath.h"
////////////////////////////////////////////////////////////////////
// Class : MouseWatcherGroup
@ -33,19 +34,45 @@
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA MouseWatcherGroup : virtual public ReferenceCount {
public:
MouseWatcherGroup();
virtual ~MouseWatcherGroup();
PUBLISHED:
bool add_region(MouseWatcherRegion *region);
void add_region(MouseWatcherRegion *region);
bool has_region(MouseWatcherRegion *region) const;
bool remove_region(MouseWatcherRegion *region);
MouseWatcherRegion *find_region(const string &name) const;
void clear_regions();
int get_num_regions() const;
MouseWatcherRegion *get_region(int n) const;
void output(ostream &out) const;
void write(ostream &out, int indent_level = 0) const;
#ifndef NDEBUG
void show_regions(const NodePath &render2d);
void set_color(const Colorf &color);
void hide_regions();
#endif // NDEBUG
protected:
typedef pvector< PT(MouseWatcherRegion) > Regions;
Regions _regions;
private:
#ifndef NDEBUG
void update_regions();
PandaNode *make_viz_region(MouseWatcherRegion *region);
typedef pvector< PT(PandaNode) > Vizzes;
Vizzes _vizzes;
bool _show_regions;
NodePath _show_regions_root;
Colorf _color;
#endif // NDEBUG
public:
static TypeHandle get_class_type() {
ReferenceCount::init_type();