本文整理汇总了C++中osg::NodeVisitor::asCullVisitor方法的典型用法代码示例。如果您正苦于以下问题:C++ NodeVisitor::asCullVisitor方法的具体用法?C++ NodeVisitor::asCullVisitor怎么用?C++ NodeVisitor::asCullVisitor使用的例子?那么, 这里精选的方法代码示例或许可以为您提供帮助。您也可以进一步了解该方法所在类osg::NodeVisitor
的用法示例。
在下文中一共展示了NodeVisitor::asCullVisitor方法的12个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于系统推荐出更棒的C++代码示例。
示例1: traverse
void RayTracedTechnique::traverse(osg::NodeVisitor& nv)
{
// OSG_NOTICE<<"RayTracedTechnique::traverse(osg::NodeVisitor& nv)"<<std::endl;
if (!_volumeTile) return;
// if app traversal update the frame count.
if (nv.getVisitorType()==osg::NodeVisitor::UPDATE_VISITOR)
{
if (_volumeTile->getDirty()) _volumeTile->init();
osgUtil::UpdateVisitor* uv = nv.asUpdateVisitor();
if (uv)
{
update(uv);
return;
}
}
else if (nv.getVisitorType()==osg::NodeVisitor::CULL_VISITOR)
{
osgUtil::CullVisitor* cv = nv.asCullVisitor();
if (cv)
{
cull(cv);
return;
}
}
if (_volumeTile->getDirty())
{
OSG_INFO<<"******* Doing init ***********"<<std::endl;
_volumeTile->init();
}
}
示例2: traverse_implementation
void Technique::traverse_implementation(osg::NodeVisitor& nv, Effect* fx)
{
// define passes if necessary
if (_passes.empty()) {
define_passes();
}
// special actions must be taken if the node visitor is actually a CullVisitor
osgUtil::CullVisitor* cv = nv.asCullVisitor();
if (cv)
{
// traverse all passes
for (int i=0; i<getNumPasses(); ++i)
{
// push the i-th pass' StateSet
cv->pushStateSet(_passes[i].get());
// traverse the override node if defined, otherwise
// traverse children as a Group would do
osg::Node *override = getOverrideChild(i);
if (override) {
override->accept(nv);
} else {
fx->inherited_traverse(nv);
}
// pop the StateSet
cv->popStateSet();
}
示例3: traverse
void TXPNode::traverse(osg::NodeVisitor& nv)
{
switch(nv.getVisitorType())
{
case osg::NodeVisitor::CULL_VISITOR:
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
osgUtil::CullVisitor* cv = nv.asCullVisitor();
if (cv)
{
//#define PRINT_TILEMAPP_TIMEINFO
#ifdef PRINT_TILEMAPP_TIMEINFO
const osg::Timer& timer = *osg::Timer::instance();
osg::Timer_t start = timer.tick();
std::cout<<"Doing visible tile search"<<std::endl;
#endif // PRINT_TILEMAPP_TIMEINFO
osg::ref_ptr<TileMapper> tileMapper = new TileMapper;
tileMapper->setLODScale(cv->getLODScale());
tileMapper->pushReferenceViewPoint(cv->getReferenceViewPoint());
tileMapper->pushViewport(cv->getViewport());
tileMapper->pushProjectionMatrix((cv->getProjectionMatrix()));
tileMapper->pushModelViewMatrix((cv->getModelViewMatrix()), osg::Transform::RELATIVE_RF);
// traverse the scene graph to search for valid tiles
accept(*tileMapper);
tileMapper->popModelViewMatrix();
tileMapper->popProjectionMatrix();
tileMapper->popViewport();
tileMapper->popReferenceViewPoint();
//std::cout<<" found " << tileMapper._tileMap.size() << std::endl;
cv->setUserData(tileMapper.get());
#ifdef PRINT_TILEMAPP_TIMEINFO
std::cout<<"Completed visible tile search in "<<timer.delta_m(start,timer.tick())<<std::endl;
#endif // PRINT_TILEMAPP_TIMEINFO
}
updateEye(nv);
break;
}
case osg::NodeVisitor::UPDATE_VISITOR:
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
updateSceneGraph();
break;
}
default:
break;
}
Group::traverse(nv);
}
示例4: lock
void osgParticle::ParticleSystemUpdater::traverse(osg::NodeVisitor& nv)
{
osgUtil::CullVisitor* cv = nv.asCullVisitor();
if (cv)
{
if (nv.getFrameStamp())
{
if( _frameNumber < nv.getFrameStamp()->getFrameNumber())
{
_frameNumber = nv.getFrameStamp()->getFrameNumber();
double t = nv.getFrameStamp()->getSimulationTime();
if (_t0 != -1.0)
{
ParticleSystem_Vector::iterator i;
for (i=_psv.begin(); i!=_psv.end(); ++i)
{
ParticleSystem* ps = i->get();
ParticleSystem::ScopedWriteLock lock(*(ps->getReadWriteMutex()));
// We need to allow at least 2 frames difference, because the particle system's lastFrameNumber
// is updated in the draw thread which may not have completed yet.
if (!ps->isFrozen() &&
(!ps->getFreezeOnCull() || ((nv.getFrameStamp()->getFrameNumber()-ps->getLastFrameNumber()) <= 2)) )
{
ps->update(t - _t0, nv);
}
}
}
_t0 = t;
}
}
else
{
OSG_WARN << "osgParticle::ParticleSystemUpdater::traverse(NodeVisitor&) requires a valid FrameStamp to function, particles not updated.\n";
}
}
Node::traverse(nv);
}
示例5: traverse
void ShadowTechnique::traverse(osg::NodeVisitor& nv)
{
if (!_shadowedScene) return;
if (nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR)
{
if (_dirty) init();
update(nv);
}
else if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR)
{
osgUtil::CullVisitor* cv = nv.asCullVisitor();
if (cv) cull(*cv);
else _shadowedScene->osg::Group::traverse(nv);
}
else
{
_shadowedScene->osg::Group::traverse(nv);
}
}
示例6: lock
void osgParticle::ParticleSystemUpdater::traverse(osg::NodeVisitor& nv)
{
osgUtil::CullVisitor* cv = nv.asCullVisitor();
if (cv)
{
if (nv.getFrameStamp())
{
if( _frameNumber < nv.getFrameStamp()->getFrameNumber())
{
_frameNumber = nv.getFrameStamp()->getFrameNumber();
double t = nv.getFrameStamp()->getSimulationTime();
if (_t0 != -1.0)
{
ParticleSystem_Vector::iterator i;
for (i=_psv.begin(); i!=_psv.end(); ++i)
{
ParticleSystem* ps = i->get();
ParticleSystem::ScopedWriteLock lock(*(ps->getReadWriteMutex()));
if (!ps->isFrozen() && (ps->getLastFrameNumber() >= (nv.getFrameStamp()->getFrameNumber() - 1) || !ps->getFreezeOnCull()))
{
ps->update(t - _t0, nv);
}
}
}
_t0 = t;
}
}
else
{
OSG_WARN << "osgParticle::ParticleSystemUpdater::traverse(NodeVisitor&) requires a valid FrameStamp to function, particles not updated.\n";
}
}
Node::traverse(nv);
}
示例7: lock
void osgParticle::ParticleProcessor::traverse(osg::NodeVisitor& nv)
{
// typecast the NodeVisitor to CullVisitor
osgUtil::CullVisitor* cv = nv.asCullVisitor();
// continue only if the visitor actually is a cull visitor
if (cv) {
// continue only if the particle system is valid
if (_ps.valid())
{
if (nv.getFrameStamp())
{
ParticleSystem::ScopedWriteLock lock(*(_ps->getReadWriteMutex()));
//added- 1/17/06- [email protected]
//a check to make sure we havent updated yet this frame
if(_frameNumber < nv.getFrameStamp()->getFrameNumber())
{
// retrieve the current time
double t = nv.getFrameStamp()->getSimulationTime();
// reset this processor if we've reached the reset point
if ((_currentTime >= _resetTime) && (_resetTime > 0))
{
_currentTime = 0;
_t0 = -1;
}
// skip if we haven't initialized _t0 yet
if (_t0 != -1)
{
// check whether the processor is alive
bool alive = false;
if (_currentTime >= _startTime)
{
if (_endless || (_currentTime < (_startTime + _lifeTime)))
alive = true;
}
// update current time
_currentTime += t - _t0;
// process only if the particle system is not frozen/culled
// We need to allow at least 2 frames difference, because the particle system's lastFrameNumber
// is updated in the draw thread which may not have completed yet.
if (alive &&
_enabled &&
!_ps->isFrozen() &&
(!_ps->getFreezeOnCull() || ((nv.getFrameStamp()->getFrameNumber()-_ps->getLastFrameNumber()) <= 2)) )
{
// initialize matrix flags
_need_ltw_matrix = true;
_need_wtl_matrix = true;
_current_nodevisitor = &nv;
// do some process (unimplemented in this base class)
process( t - _t0 );
} else {
//The values of _previous_wtl_matrix and _previous_ltw_matrix will be invalid
//since processing was skipped for this frame
_first_ltw_compute = true;
_first_wtl_compute = true;
}
}
_t0 = t;
}
//added- 1/17/06- [email protected]
//updates the _frameNumber, keeping it current
_frameNumber = nv.getFrameStamp()->getFrameNumber();
}
else
{
OSG_WARN << "osgParticle::ParticleProcessor::traverse(NodeVisitor&) requires a valid FrameStamp to function, particles not updated.\n";
}
} else
{
OSG_WARN << "ParticleProcessor \"" << getName() << "\": invalid particle system\n";
}
}
// call the inherited method
Node::traverse(nv);
}
示例8: traverse
void VolumeScene::traverse(osg::NodeVisitor& nv)
{
osgUtil::CullVisitor* cv = nv.asCullVisitor();
if (!cv)
{
Group::traverse(nv);
return;
}
osg::ref_ptr<ViewData> viewData;
bool initializeViewData = false;
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_viewDataMapMutex);
if (!_viewDataMap[cv])
{
_viewDataMap[cv] = new ViewData;
initializeViewData = true;
}
viewData = _viewDataMap[cv];
}
if (initializeViewData)
{
OSG_NOTICE<<"Creating ViewData"<<std::endl;
int textureWidth = 512;
int textureHeight = 512;
osg::Viewport* viewport = cv->getCurrentRenderStage()->getViewport();
if (viewport)
{
textureWidth = static_cast<int>(viewport->width());
textureHeight = static_cast<int>(viewport->height());
}
// set up depth texture
viewData->_depthTexture = new osg::Texture2D;
viewData->_depthTexture->setTextureSize(textureWidth, textureHeight);
viewData->_depthTexture->setInternalFormat(GL_DEPTH_COMPONENT);
viewData->_depthTexture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
viewData->_depthTexture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
viewData->_depthTexture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_BORDER);
viewData->_depthTexture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_BORDER);
viewData->_depthTexture->setBorderColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
// set up color texture
viewData->_colorTexture = new osg::Texture2D;
viewData->_colorTexture->setTextureSize(textureWidth, textureHeight);
viewData->_colorTexture->setInternalFormat(GL_RGBA);
viewData->_colorTexture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
viewData->_colorTexture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
viewData->_colorTexture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_EDGE);
viewData->_colorTexture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_EDGE);
// set up the RTT Camera to capture the main scene to a color and depth texture that can be used in post processing
viewData->_rttCamera = new osg::Camera;
viewData->_rttCamera->setName("viewData->_rttCamera");
viewData->_rttCamera->attach(osg::Camera::DEPTH_BUFFER, viewData->_depthTexture.get());
viewData->_rttCamera->attach(osg::Camera::COLOR_BUFFER, viewData->_colorTexture.get());
viewData->_rttCamera->setCullCallback(new RTTCameraCullCallback(this));
viewData->_rttCamera->setViewport(0,0,textureWidth,textureHeight);
// clear the depth and colour bufferson each clear.
viewData->_rttCamera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
// set the camera to render before the main camera.
viewData->_rttCamera->setRenderOrder(osg::Camera::PRE_RENDER);
// tell the camera to use OpenGL frame buffer object where supported.
viewData->_rttCamera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
viewData->_rttCamera->setReferenceFrame(osg::Transform::RELATIVE_RF);
viewData->_rttCamera->setProjectionMatrix(osg::Matrixd::identity());
viewData->_rttCamera->setViewMatrix(osg::Matrixd::identity());
// create mesh for rendering the RTT textures onto the screen
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
geode->setCullingActive(false);
viewData->_backdropSubgraph = geode;
//geode->addDrawable(osg::createTexturedQuadGeometry(osg::Vec3(-1.0f,-1.0f,-1.0f),osg::Vec3(2.0f,0.0f,-1.0f),osg::Vec3(0.0f,2.0f,-1.0f)));
viewData->_geometry = new osg::Geometry;
geode->addDrawable(viewData->_geometry.get());
viewData->_geometry->setUseDisplayList(false);
viewData->_geometry->setUseVertexBufferObjects(false);
viewData->_vertices = new osg::Vec3Array(4);
viewData->_geometry->setVertexArray(viewData->_vertices.get());
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array(1);
(*colors)[0].set(1.0f,1.0f,1.0f,1.0f);
//.........这里部分代码省略.........
示例9: traverse
void LightPointNode::traverse(osg::NodeVisitor& nv)
{
if (_lightPointList.empty())
{
// no light points so no op.
return;
}
//#define USE_TIMER
#ifdef USE_TIMER
osg::Timer timer;
osg::Timer_t t1=0,t2=0,t3=0,t4=0,t5=0,t6=0,t7=0,t8=0;
#endif
#ifdef USE_TIMER
t1 = timer.tick();
#endif
osgUtil::CullVisitor* cv = nv.asCullVisitor();
#ifdef USE_TIMER
t2 = timer.tick();
#endif
// should we disable small feature culling here?
if (cv /*&& !cv->isCulled(_bbox)*/)
{
osg::Matrix matrix = *(cv->getModelViewMatrix());
osg::RefMatrix& projection = *(cv->getProjectionMatrix());
osgUtil::StateGraph* rg = cv->getCurrentStateGraph();
if (rg->leaves_empty())
{
// this is first leaf to be added to StateGraph
// and therefore should not already know current render bin,
// so need to add it.
cv->getCurrentRenderBin()->addStateGraph(rg);
}
#ifdef USE_TIMER
t3 = timer.tick();
#endif
LightPointDrawable* drawable = NULL;
osg::Referenced* object = rg->getUserData();
if (object)
{
if (typeid(*object)==typeid(LightPointDrawable))
{
// resuse the user data attached to the render graph.
drawable = static_cast<LightPointDrawable*>(object);
}
else if (typeid(*object)==typeid(LightPointSpriteDrawable))
{
drawable = static_cast<LightPointSpriteDrawable*>(object);
}
else
{
// will need to replace UserData.
OSG_WARN << "Warning: Replacing osgUtil::StateGraph::_userData to support osgSim::LightPointNode, may have undefined results."<<std::endl;
}
}
if (!drawable)
{
drawable = _pointSprites ? new LightPointSpriteDrawable : new LightPointDrawable;
rg->setUserData(drawable);
if (cv->getFrameStamp())
{
drawable->setSimulationTime(cv->getFrameStamp()->getSimulationTime());
}
}
// search for a drawable in the RenderLeaf list equal to the attached the one attached to StateGraph user data
// as this will be our special light point drawable.
osgUtil::StateGraph::LeafList::iterator litr;
for(litr = rg->_leaves.begin();
litr != rg->_leaves.end() && (*litr)->_drawable.get()!=drawable;
++litr)
{}
if (litr == rg->_leaves.end())
{
// haven't found the drawable added in the RenderLeaf list, therefore this may be the
// first time through LightPointNode in this frame, so need to add drawable into the StateGraph RenderLeaf list
// and update its time signatures.
drawable->reset();
rg->addLeaf(new osgUtil::RenderLeaf(drawable,&projection,NULL,FLT_MAX));
// need to update the drawable's frame count.
if (cv->getFrameStamp())
{
drawable->updateSimulationTime(cv->getFrameStamp()->getSimulationTime());
//.........这里部分代码省略.........
示例10: getOrCreateStateSet
void osgParticle::ParticleSystem::update(double dt, osg::NodeVisitor& nv)
{
// reset bounds
_reset_bounds_flag = true;
if (_useShaders)
{
// Update shader uniforms
// This slightly reduces the consumption of traversing the particle vector, because we
// don't compute tile and angle attributes that are useleff for shaders.
// At present, our lcoal shader implementation will ignore these particle props:
// _cur_tile, _s_coord, _t_coord, _prev_pos, _prev_angle and _angle
osg::StateSet* stateset = getOrCreateStateSet();
if (_dirty_uniforms)
{
osg::Uniform* u_vd = stateset->getUniform("visibilityDistance");
if (u_vd) u_vd->set((float)_visibilityDistance);
_dirty_uniforms = false;
}
}
for(unsigned int i=0; i<_particles.size(); ++i)
{
Particle& particle = _particles[i];
if (particle.isAlive())
{
if (particle.update(dt, _useShaders))
{
update_bounds(particle.getPosition(), particle.getCurrentSize());
}
else
{
reuseParticle(i);
}
}
}
if (_sortMode != NO_SORT)
{
// sort particles
osgUtil::CullVisitor* cv = nv.asCullVisitor();
if (cv)
{
osg::Matrixd modelview = *(cv->getModelViewMatrix());
double scale = (_sortMode==SORT_FRONT_TO_BACK ? -1.0 : 1.0);
double deadDistance = DBL_MAX;
for (unsigned int i=0; i<_particles.size(); ++i)
{
Particle& particle = _particles[i];
if (particle.isAlive())
particle.setDepth(distance(particle.getPosition(), modelview) * scale);
else
particle.setDepth(deadDistance);
}
std::sort<Particle_vector::iterator>(_particles.begin(), _particles.end());
// Repopulate the death stack as it will have been invalidated by the sort.
unsigned int numDead = _deadparts.size();
if (numDead>0)
{
// clear the death stack
_deadparts = Death_stack();
// copy the tail of the _particles vector as this will contain all the dead Particle thanks to the depth sort against DBL_MAX
Particle* first_dead_ptr = &_particles[_particles.size()-numDead];
Particle* last_dead_ptr = &_particles[_particles.size()-1];
for(Particle* dead_ptr = first_dead_ptr; dead_ptr<=last_dead_ptr; ++dead_ptr)
{
_deadparts.push(dead_ptr);
}
}
}
}
// force recomputing of bounding box on next frame
dirtyBound();
}
示例11: traverseImplementation
void Widget::traverseImplementation(osg::NodeVisitor& nv)
{
if (!_graphicsInitialized && nv.getVisitorType()!=osg::NodeVisitor::CULL_VISITOR) createGraphics();
osgGA::EventVisitor* ev = nv.asEventVisitor();
if (ev)
{
if (_visible && _enabled)
{
updateFocus(nv);
// OSG_NOTICE<<"EventTraversal getHasEventFocus()="<<getHasEventFocus()<<std::endl;
// signify that event has been taken by widget with focus
bool widgetsWithFocusSetHandled = getHasEventFocus();
osgGA::EventQueue::Events& events = ev->getEvents();
for(osgGA::EventQueue::Events::iterator itr = events.begin();
itr != events.end();
++itr)
{
if (handle(ev, itr->get()) || widgetsWithFocusSetHandled)
{
(*itr)->setHandled(true);
ev->setEventHandled(true);
}
}
GraphicsSubgraphMap::iterator itr = _graphicsSubgraphMap.begin();
while(itr!= _graphicsSubgraphMap.end() && itr->first<=0)
{
itr->second->accept(nv);
++itr;
}
osg::Group::traverse(nv);
while(itr!= _graphicsSubgraphMap.end())
{
itr->second->accept(nv);
++itr;
}
}
}
else if (_visible ||
(nv.getVisitorType()!=osg::NodeVisitor::UPDATE_VISITOR && nv.getVisitorType()!=osg::NodeVisitor::CULL_VISITOR && nv.getVisitorType()!=osg::NodeVisitor::INTERSECTION_VISITOR) )
{
osgUtil::CullVisitor* cv = (nv.getVisitorType()==osg::NodeVisitor::CULL_VISITOR) ? nv.asCullVisitor() : 0;
if (cv && _widgetStateSet.valid()) cv->pushStateSet(_widgetStateSet.get());
GraphicsSubgraphMap::iterator itr = _graphicsSubgraphMap.begin();
while(itr!= _graphicsSubgraphMap.end() && itr->first<=0)
{
itr->second->accept(nv);
++itr;
}
Group::traverse(nv);
while(itr!= _graphicsSubgraphMap.end())
{
itr->second->accept(nv);
++itr;
}
if (cv && _widgetStateSet.valid()) cv->popStateSet();
}
}
示例12: traverse
void Impostor::traverse(osg::NodeVisitor& nv)
{
if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR)
{
LOD::traverse(nv);
return;
}
osgUtil::CullVisitor* cv = nv.asCullVisitor();
if (!cv)
{
LOD::traverse(nv);
return;
}
osg::Vec3 eyeLocal = nv.getEyePoint();
const BoundingSphere& bs = getBound();
unsigned int contextID = cv->getState() ? cv->getState()->getContextID() : 0;
float distance2 = (eyeLocal-bs.center()).length2();
float LODScale = cv->getLODScale();
if (!cv->getImpostorsActive() ||
distance2*LODScale*LODScale<osg::square(getImpostorThreshold()) ||
distance2<bs.radius2()*2.0f)
{
// outwith the impostor distance threshold therefore simple
// traverse the appropriate child of the LOD.
LOD::traverse(nv);
}
else
{
// within the impostor distance threshold therefore attempt
// to use impostor instead.
RefMatrix& matrix = *cv->getModelViewMatrix();
// search for the best fit ImpostorSprite;
ImpostorSprite* impostorSprite = findBestImpostorSprite(contextID,eyeLocal);
if (impostorSprite)
{
// impostor found, now check to see if it is good enough to use
float error = impostorSprite->calcPixelError(*(cv->getMVPW()));
if (error>cv->getImpostorPixelErrorThreshold())
{
// chosen impostor sprite pixel error is too great to use
// from this eye point, therefore invalidate it.
impostorSprite=NULL;
}
}
// need to think about sprite reuse and support for multiple context's.
if (impostorSprite==NULL)
{
// no appropriate sprite has been found therefore need to create
// one for use
// create the impostor sprite.
impostorSprite = createImpostorSprite(cv);
//if (impostorSprite) impostorSprite->_color.set(0.0f,0.0f,1.0f,1.0f);
}
//else impostorSprite->_color.set(1.0f,1.0f,1.0f,1.0f);
if (impostorSprite)
{
// update frame number to show that impostor is in action.
impostorSprite->setLastFrameUsed(cv->getTraversalNumber());
if (cv->getComputeNearFarMode()) cv->updateCalculatedNearFar(matrix,*impostorSprite, false);
StateSet* stateset = impostorSprite->getStateSet();
if (stateset) cv->pushStateSet(stateset);
cv->addDrawableAndDepth(impostorSprite, &matrix, distance(getCenter(),matrix));
if (stateset) cv->popStateSet();
}
else
{
// no impostor has been selected or created so default to
// traversing the usual LOD selected child.
LOD::traverse(nv);
}
}
}