Imath::Box3f StandardNodeGadget::bound() const { Box3f b = IndividualContainer::bound(); LinearContainer::Orientation orientation = inputNoduleContainer()->getOrientation(); if( orientation == LinearContainer::X ) { // enforce a minimum width float width = std::max( b.size().x, g_minWidth ); float c = b.center().x; b.min.x = c - width / 2.0f; b.max.x = c + width / 2.0f; } // add the missing spacing to the border if we have no nodules on a given side Box3f inputContainerBound = inputNoduleContainer()->transformedBound( this ); Box3f outputContainerBound = outputNoduleContainer()->transformedBound( this ); if( inputContainerBound.isEmpty() ) { if( orientation == LinearContainer::X ) { b.max.y += g_spacing + g_borderWidth; } else { b.min.x -= g_spacing + g_borderWidth; } } if( outputContainerBound.isEmpty() ) { if( orientation == LinearContainer::X ) { b.min.y -= g_spacing + g_borderWidth; } else { b.max.x += g_spacing + g_borderWidth; } } // add on a little bit in the major axis, so that the nodules don't get drawn in the frame corner if( orientation == LinearContainer::X ) { b.min.x -= g_borderWidth; b.max.x += g_borderWidth; } else { b.min.y -= g_borderWidth; b.max.y += g_borderWidth; } return b; }
void StandardNodeGadget::doRender( const Style *style ) const { // decide what state we're rendering in Gaffer::ConstScriptNodePtr script = node()->scriptNode(); Style::State state = Style::NormalState; if( script && script->selection()->contains( node() ) ) { state = Style::HighlightedState; } // draw Box3f b = bound(); LinearContainer::Orientation orientation = inputNoduleContainer()->getOrientation(); Box3f inputContainerBound = inputNoduleContainer()->transformedBound( this ); Box3f outputContainerBound = outputNoduleContainer()->transformedBound( this ); if( !inputContainerBound.isEmpty() ) { if( orientation == LinearContainer::X ) { b.max.y -= inputContainerBound.size().y / 2.0f; } else { b.min.x += inputContainerBound.size().x / 2.0f; } } if( !outputContainerBound.isEmpty() ) { if( orientation == LinearContainer::X ) { b.min.y += outputContainerBound.size().y / 2.0f; } else { b.max.x -= outputContainerBound.size().x / 2.0f; } } style->renderFrame( Box2f( V2f( b.min.x, b.min.y ) + V2f( g_borderWidth ), V2f( b.max.x, b.max.y ) - V2f( g_borderWidth ) ), g_borderWidth, state ); NodeGadget::doRender( style ); if( !m_nodeEnabled && !IECoreGL::Selector::currentSelector() ) { /// \todo Replace renderLine() with a specific method (renderNodeStrikeThrough?) on the Style class /// so that styles can do customised drawing based on knowledge of what is being drawn. style->renderLine( IECore::LineSegment3f( V3f( b.min.x, b.min.y, 0 ), V3f( b.max.x, b.max.y, 0 ) ) ); } }
void BackdropNodeGadget::frame( const std::vector<Gaffer::Node *> &nodes ) { GraphGadget *graph = ancestor<GraphGadget>(); if( !graph ) { return; } Box3f b; for( std::vector<Node *>::const_iterator it = nodes.begin(), eIt = nodes.end(); it != eIt; ++it ) { NodeGadget *nodeGadget = graph->nodeGadget( *it ); if( nodeGadget ) { b.extendBy( nodeGadget->transformedBound( NULL ) ); } } if( b.isEmpty() ) { return; } graph->setNodePosition( node(), V2f( b.center().x, b.center().y ) ); V2f s( b.size().x / 2.0f, b.size().y / 2.0f ); boundPlug()->setValue( Box2f( V2f( -s ) - V2f( g_margin ), V2f( s ) + V2f( g_margin + 2.0f * g_margin ) ) ); }
bool intersectLine( const Box3f& box, const Vector3f& rayOrigin, const Vector3f& rayDirection, float& tNear, float& tFar ) { assert( box.isStandard() ); assert( !box.isEmpty() ); // Compute t to each face. Vector3f rcpDir = 1.0f / rayDirection; // Three "bottom" faces (min of the box). Vector3f tBottom = rcpDir * ( box.origin - rayOrigin ); // three "top" faces (max of the box) Vector3f tTop = rcpDir * ( box.rightTopFront() - rayOrigin ); // find the smallest and largest distances along each axis Vector3f tMin = libcgt::core::math::minimum( tBottom, tTop ); Vector3f tMax = libcgt::core::math::maximum( tBottom, tTop ); // tNear is the largest tMin tNear = libcgt::core::math::maximum( tMin ); // tFar is the smallest tMax tFar = libcgt::core::math::minimum( tMax ); return tFar > tNear; }
bool intersectRay( const Box3f& box, const Vector3f& rayOrigin, const Vector3f& rayDirection, float& tIntersect, float tMin ) { assert( box.isStandard() ); assert( !box.isEmpty() ); float tNear; float tFar; bool intersect = intersectLine( box, rayOrigin, rayDirection, tNear, tFar ); if( intersect ) { if( tNear >= tMin ) { tIntersect = tNear; } else if( tFar >= tMin ) { tIntersect = tFar; } else { intersect = false; } } return intersect; }
bool carefulIntersectBoxRay( const Box3f& box, const Vector3f& rayOrigin, const Vector3f& rayDirection, float& t0, float& t1, int& t0Face, int& t1Face, float rayTMin, float rayTMax ) { assert( box.isStandard() ); assert( !box.isEmpty() ); t0 = rayTMin; t1 = rayTMax; t0Face = -1; t1Face = -1; // Compute t to each face. Vector3f rcpDir = 1.0f / rayDirection; Vector3f boxMax = box.rightTopFront(); for( int i = 0; i < 3; ++i ) { // Compute the intersection between the line and the slabs along the // i-th axis, parameterized as [tNear, tFar]. float rcpDir = 1.0f / rayDirection[ i ]; float tNear = rcpDir * ( box.origin[ i ] - rayOrigin[ i ] ); float tFar = rcpDir * ( boxMax[ i ] - rayOrigin[ i ] ); // Which face we're testing against. int nearFace = 2 * i; int farFace = 2 * i + 1; // Swap such that tNear < tFAr. if( tNear > tFar ) { std::swap( tNear, tFar ); std::swap( nearFace, farFace ); } // Compute the set intersection between [tNear, tFar] and [t0, t1]. if( tNear > t0 ) { t0 = tNear; t0Face = nearFace; } if( tFar < t1 ) { t1 = tFar; t1Face = farFace; } // Early abort if the range is empty. if( t0 > t1 ) { return false; } } return true; }
void IECoreArnold::RendererImplementation::procedural( IECore::Renderer::ProceduralPtr proc ) { Box3f bound = proc->bound(); if( bound.isEmpty() ) { return; } AtNode *procedural = AiNode( "procedural" ); if( ExternalProcedural *externalProc = dynamic_cast<ExternalProcedural *>( proc.get() ) ) { AiNodeSetStr( procedural, "dso", externalProc->fileName().c_str() ); ParameterAlgo::setParameters( procedural, externalProc->parameters() ); applyTransformToNode( procedural ); } else { // we have to transform the bound, as we're not applying the current transform to the // procedural node, but instead applying absolute transforms to the shapes the procedural // generates. if( bound != Procedural::noBound ) { Box3f transformedBound; for( size_t i = 0, e = m_transformStack.numSamples(); i < e; ++i ) { transformedBound.extendBy( transform( bound, m_transformStack.sample( i ) ) ); } bound = transformedBound; } AiNodeSetPtr( procedural, "funcptr", (void *)procLoader ); ProceduralData *data = new ProceduralData; data->procedural = proc; data->renderer = new IECoreArnold::Renderer( new RendererImplementation( *this ) ); AiNodeSetPtr( procedural, "userptr", data ); } if( bound != Procedural::noBound ) { AiNodeSetPnt( procedural, "min", bound.min.x, bound.min.y, bound.min.z ); AiNodeSetPnt( procedural, "max", bound.max.x, bound.max.y, bound.max.z ); } else { // No bound available - expand procedural immediately. AiNodeSetBool( procedural, "load_at_init", true ); } // we call addNode() rather than addShape() as we don't want to apply transforms and // shaders and attributes to procedurals. if we do, they override the things we set // on the nodes generated by the procedurals, which is frankly useless. addNode( procedural ); }
Imath::Box3f Seeds::computeBranchBound( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context ) const { Box3f b = inPlug()->bound( parentPath ); if( !b.isEmpty() ) { // The PointsPrimitive we make has a default point width of 1, // so we must expand our bounding box to take that into account. b.min -= V3f( 0.5 ); b.max += V3f( 0.5 ); } return b; }
void ImageView::preRender() { if( m_framed ) { return; } const Box3f b = m_imageGadget->bound(); if( b.isEmpty() ) { return; } viewportGadget()->frame( b ); m_framed = true; }
bool Line::intersect( float angle, const Box3f& box ) const { if (box.isEmpty()) return false; const Vec3f &max = box.getMax(), &min = box.getMin(); float fuzz = 0.0; int i; if (angle < 0.0) { fuzz = - angle; } else { // Find the farthest point on the bounding box (where the pick // cone will be largest). The amount of fuzz at this point will // be the minimum we can use. Expand the box by that amount and // do an intersection. double tanA = tan(angle); for(i = 0; i < 8; i++) { Vec3f point(i & 01 ? min[0] : max[0], i & 02 ? min[1] : max[1], i & 04 ? min[2] : max[2]); // how far is point from line origin?? Vec3f diff(point - getPosition()); double thisFuzz = sqrt(diff.dot(diff)) * tanA; if (thisFuzz > fuzz) fuzz = float(thisFuzz); } } Box3f fuzzBox = box; fuzzBox.extendBy(Vec3f(min[0] - fuzz, min[1] - fuzz, min[2] - fuzz)); fuzzBox.extendBy(Vec3f(max[0] + fuzz, max[1] + fuzz, max[2] + fuzz)); Vec3f scratch1, scratch2; return intersect(fuzzBox, scratch1, scratch2); }
ConnectionCreator *StandardNodeGadget::closestDragDestination( const DragDropEvent &event ) const { if( event.buttons != DragDropEvent::Left ) { // See comments in StandardNodule::dragEnter() return nullptr; } ConnectionCreator *result = nullptr; float maxDist = Imath::limits<float>::max(); for( RecursiveConnectionCreatorIterator it( this ); !it.done(); it++ ) { if( !(*it)->getVisible() ) { it.prune(); continue; } if( !canConnect( event, it->get() ) ) { continue; } const Box3f bound = (*it)->transformedBound( this ); if( bound.isEmpty() ) { continue; } const V3f closestPoint = closestPointOnBox( event.line.p0, bound ); const float dist = ( closestPoint - event.line.p0 ).length2(); if( dist < maxDist ) { result = it->get(); maxDist = dist; } } return result; }
bool Line::intersect( const Box3f& box, Vec3f& enter, Vec3f& exit ) const { if (box.isEmpty()) { return false; } const Vec3f &pos = getPosition(), &dir = getDirection(); const Vec3f &max = box.getMax(), &min = box.getMin(); Vec3f points[8], inter, bary; Plane plane; int i, v0, v1, v2; bool front = false, valid, validIntersection = false; // // First, check the distance from the ray to the center // of the box. If that distance is greater than 1/2 // the diagonal distance, there is no intersection // diff is the vector from the closest point on the ray to the center // dist2 is the square of the distance from the ray to the center // radi2 is the square of 1/2 the diagonal length of the bounding box // float t = (box.getCenter() - pos).dot(dir); Vec3f diff(pos + dir * t - box.getCenter()); float dist2 = diff.dot(diff); float radi2 = (max - min).dot(max - min) * 0.25f; if (dist2 > radi2) { return false; } // set up the eight coords of the corners of the box for(i = 0; i < 8; i++) { points[i].setValue(i & 01 ? min[0] : max[0], i & 02 ? min[1] : max[1], i & 04 ? min[2] : max[2]); } // intersect the 12 triangles. for(i = 0; i < 12; i++) { switch(i) { case 0: v0 = 2; v1 = 1; v2 = 0; break; // +z case 1: v0 = 2; v1 = 3; v2 = 1; break; case 2: v0 = 4; v1 = 5; v2 = 6; break; // -z case 3: v0 = 6; v1 = 5; v2 = 7; break; case 4: v0 = 0; v1 = 6; v2 = 2; break; // -x case 5: v0 = 0; v1 = 4; v2 = 6; break; case 6: v0 = 1; v1 = 3; v2 = 7; break; // +x case 7: v0 = 1; v1 = 7; v2 = 5; break; case 8: v0 = 1; v1 = 4; v2 = 0; break; // -y case 9: v0 = 1; v1 = 5; v2 = 4; break; case 10: v0 = 2; v1 = 7; v2 = 3; break; // +y case 11: v0 = 2; v1 = 6; v2 = 7; break; default: assert(false && "Must never happened"); v0 = v1 = v2 = 0; // to remove a warning. } valid = intersect( points[v0], points[v1], points[v2], inter, bary, front); if ( valid ) { if (front) { enter = inter; validIntersection = valid; } else { exit = inter; validIntersection = valid; } } } return validIntersection; }
void IECoreArnold::RendererImplementation::procedural( IECore::Renderer::ProceduralPtr proc ) { Box3f bound = proc->bound(); if( bound.isEmpty() ) { return; } AtNode *node = NULL; std::string nodeType = "procedural"; if( const ExternalProcedural *externalProc = dynamic_cast<ExternalProcedural *>( proc.get() ) ) { // Allow a parameter "ai:nodeType" == "volume" to create a volume shape rather // than a procedural shape. Volume shapes provide "dso", "min" and "max" parameters // just as procedural shapes do, so the mapping is a fairly natural one. CompoundDataMap::const_iterator nodeTypeIt = externalProc->parameters().find( "ai:nodeType" ); if( nodeTypeIt != externalProc->parameters().end() && nodeTypeIt->second->isInstanceOf( StringData::staticTypeId() ) ) { nodeType = static_cast<const StringData *>( nodeTypeIt->second.get() )->readable(); } node = AiNode( nodeType.c_str() ); AiNodeSetStr( node, "dso", externalProc->fileName().c_str() ); ParameterAlgo::setParameters( node, externalProc->parameters() ); applyTransformToNode( node ); } else { node = AiNode( nodeType.c_str() ); // we have to transform the bound, as we're not applying the current transform to the // procedural node, but instead applying absolute transforms to the shapes the procedural // generates. if( bound != Procedural::noBound ) { Box3f transformedBound; for( size_t i = 0, e = m_transformStack.numSamples(); i < e; ++i ) { transformedBound.extendBy( transform( bound, m_transformStack.sample( i ) ) ); } bound = transformedBound; } AiNodeSetPtr( node, "funcptr", (void *)procLoader ); ProceduralData *data = new ProceduralData; data->procedural = proc; data->renderer = new IECoreArnold::Renderer( new RendererImplementation( *this ) ); AiNodeSetPtr( node, "userptr", data ); } if( bound != Procedural::noBound ) { AiNodeSetPnt( node, "min", bound.min.x, bound.min.y, bound.min.z ); AiNodeSetPnt( node, "max", bound.max.x, bound.max.y, bound.max.z ); } else { // No bound available - expand procedural immediately. AiNodeSetBool( node, "load_at_init", true ); } if( nodeType == "procedural" ) { // We call addNode() rather than addShape() as we don't want to apply transforms and // shaders and attributes to procedurals. If we do, they override the things we set // on the nodes generated by the procedurals, which is frankly useless. addNode( node ); } else { addShape( node ); } }
void LinearContainer::calculateChildTransforms() const { if( m_clean ) { return; } int axis = m_orientation - 1; V3f size( 0 ); vector<Box3f> bounds; for( ChildContainer::const_iterator it=children().begin(); it!=children().end(); it++ ) { const Gadget *child = static_cast<const Gadget *>( it->get() ); if( !child->getVisible() ) { continue; } Box3f b = child->bound(); if( !b.isEmpty() ) { for( int a=0; a<3; a++ ) { if( a==axis ) { size[a] += b.size()[a]; } else { size[a] = max( size[a], b.size()[a] ); } } } bounds.push_back( b ); } size[axis] += (bounds.size() - 1) * m_spacing; float offset = size[axis] / 2.0f * ( m_direction==Increasing ? -1.0f : 1.0f ); int i = 0; for( ChildContainer::const_iterator it=children().begin(); it!=children().end(); it++ ) { Gadget *child = static_cast<Gadget *>( it->get() ); if( !child->getVisible() ) { continue; } const Box3f &b = bounds[i++]; V3f childOffset( 0 ); if( !b.isEmpty() ) { for( int a=0; a<3; a++ ) { if( a==axis ) { childOffset[a] = offset - ( m_direction==Increasing ? b.min[a] : b.max[a] ); } else { switch( m_alignment ) { case Min : childOffset[a] = -size[a]/2.0f - b.min[a]; break; case Centre : childOffset[a] = -b.center()[a]; break; default : // max childOffset[a] = size[a]/2.0f - b.max[a]; } } } offset += b.size()[axis] * ( m_direction==Increasing ? 1.0f : -1.0f ); } offset += m_spacing * ( m_direction==Increasing ? 1.0f : -1.0f ); M44f m; m.translate( childOffset ); child->setTransform( m ); } m_clean = true; }