Helios++
Helios software for LiDAR simulations
SAHKDTreeFactory Class Reference

Class providing building methods for k-dimensional trees with surface area heuristic (SAH) More...

#include <SAHKDTreeFactory.h>

Inheritance diagram for SAHKDTreeFactory:
Collaboration diagram for SAHKDTreeFactory:

Public Member Functions

 SAHKDTreeFactory (size_t const lossNodes=21, double const ci=1, double const cl=1, double const co=1)
 Surface area heuristic KDTree factory default constructor. More...
 
KDTreeFactoryclone () const override
 
void _clone (KDTreeFactory *kdtf) const override
 Assign attributes from SAHKDTreeFactory to its clone.
 
void defineSplit (KDTreeNode *node, KDTreeNode *parent, vector< Primitive * > &primitives, int const depth) const override
 Define the split axis and position for current node. More...
 
void computeKDTreeStats (KDTreeNodeRoot *root) const override
 Compute the simple stats of the KDTree but also its cost based on surface area heuristic defined in defineSplit function. More...
 
void buildChildrenNodes (KDTreeNode *node, KDTreeNode *parent, vector< Primitive * > const &primitives, int const depth, int const index, vector< Primitive * > &leftPrimitives, vector< Primitive * > &rightPrimitives) override
 Build children nodes using \(C_T\) heuristic to handle KDTree in-depth partitioning. More...
 
virtual void buildChildrenNodesRecipe (KDTreeNode *node, KDTreeNode *parent, vector< Primitive * > const &primitives, int const depth, int const index, vector< Primitive * > &leftPrimitives, vector< Primitive * > &rightPrimitives, std::function< void(KDTreeNode *node, int const depth, int const index, vector< Primitive * > &leftPrimitives, vector< Primitive * > &rightPrimitives)>f_buildChildrenNodes)
 The recipe for building of children nodes by SAH algorithm. It is meant to be used by the SAHKDTreeFactory::buildChildrenNodes but also by any alternative implementation which shared the same recipe (global logic) but changes the way some parts are computed. For instance, it is used to handle geometry-level parallelization. More...
 
virtual size_t getLossNodes () const
 Obtain the loss nodes used to compute the Surface Area Heuristic. More...
 
virtual void setLossNodes (size_t const lossNodes)
 Set the number loss nodes used to compute the Surface Area Heuristic. More...
 
virtual double getInteriorCost () const
 Obtain the cost-weight of interior nodes. More...
 
virtual void setInteriorCost (double const ci)
 Set the cost-weight of interior nodes. More...
 
virtual double getLeafCost () const
 Obtain the cost-weight for leaf nodes. More...
 
virtual void setLeafCost (double const cl)
 Set the cost-weight for leaf nodes. More...
 
virtual double getObjectCost () const
 Obtain the cost-weight of testing an object for intersection. More...
 
virtual void setObjectCost (double const co)
 Set the cost-weight of testing an object for intersection. More...
 
- Public Member Functions inherited from SimpleKDTreeFactory
 SimpleKDTreeFactory ()
 SimpleKDTreeFactory default constructor. More...
 
KDTreeFactoryclone () const override
 
KDTreeNodeRootmakeFromPrimitivesUnsafe (vector< Primitive * > &primitives, bool const computeStats=false, bool const reportStats=false) override
 Build a simple KDTree from given primitives. More...
 
- Public Member Functions inherited from KDTreeFactory
 KDTreeFactory ()
 K dimensional tree factory default constructor.
 
virtual KDTreeNodeRootmakeFromPrimitives (vector< Primitive * > const &primitives, bool const computeStats=false, bool const reportStats=false)
 Safe wrapper from makeFromPrimitivesUnsafe which handles a copy to make from primitives by default. This function behavior might be overridden by any derived/child class. It is expected that any implementation of makeFromPrimitives provides a way to implement the makeFromPrimitivesUnsafe method without modifying vector of input primitives. Notice this does not mean primitives themselves cannot be modified, that depends on the type of KDTreeFactory. It only means that the vector itself will not be modified, for instance due to sorting purposes. More...
 
virtual bool isBuildingLightNodes ()
 Check if KDTreeFactory is building light nodes. More...
 
virtual void setBuildingLightNodes (bool const buildLightNodes)
 Set KDTreeFactory so it build light nodes (true) or not (false) More...
 
virtual void setChild (LightKDTreeNode *&child, KDTreeNode *node)
 Set child to given node if and only if node is not null. It must be used to assign children nodes in a thread-safe way. More...
 

Protected Member Functions

bool checkNodeMustSplit (vector< Primitive * > const &primitives, vector< Primitive * > const &leftPrimitives, vector< Primitive * > const &rightPrimitives) const override
 Check wheter the node must be splitted (true) or not (false) depending on its total primitives. More...
 
virtual double splitLoss (vector< Primitive * > const &primitives, int const splitAxis, double const splitPos, double const r) const
 Compute the loss function for the splitting hyperplane. More...
 
virtual void computeBestSplit (vector< Primitive * > &primitives, size_t const lossNodes, double const start, double const step, int const splitAxis, double const minBound, double const boundLength, double &loss, double &splitPos) const
 Iteratively compute the best split position, it is the one with smaller loss. More...
 
virtual double findSplitPositionBySAH (KDTreeNode *node, vector< Primitive * > &primitives) const
 Find the best split position using Surface Area Heuristic (SAH) as described in SAHKDTreeFactory::defineSplit. More...
 
virtual double findSplitPositionBySAHRecipe (KDTreeNode *node, vector< Primitive * > &primitives, std::function< void(vector< Primitive * >::iterator begin, vector< Primitive * >::iterator end, KDTreePrimitiveComparator comparator)> f_sortPrimitives, std::function< void(vector< Primitive * > &primitives, size_t const lossNodes, double const start, double const step, int const splitAxis, double const minBound, double const boundLength, double &loss, double &splitPos)> f_computeLossNodes) const
 The recipe for finding split position by SAH algorithm. It is meant to be used by the SAHKDTreeFactory::findSplitPositionBySAH but also by any alternative implementation which shares the same recipe ( global logic) but changes the way some parts are computed. For instance, it is used to handle geometry-level parallelization. More...
 
virtual double heuristicILOT (double &hi, double &hl, double &ho, double &ht, double const surfaceAreaRoot, double const surfaceAreaInterior, double const surfaceAreaLeaf, vector< Primitive * > const &primitives) const
 Compute the \(C_T\) heuristic preserving partials result of interest. More...
 
virtual double cumulativeILOT (double &hi, double &hl, double &ho, double &ht, double const _hi, double const _hl, double const _ho, double const saRoot) const
 Compute the cumulative of \(C_T\) heuristic ILOT. More...
 
virtual void internalizeILOT (double &hi, double &hl, double &ho, double &ht, KDTreeNode *node, vector< Primitive * > const &primitives, vector< Primitive * > const &leftPrimitives, vector< Primitive * > const &rightPrimitives)
 Compute ILOT corresponding to internalization (make interior) of given node and its corresponding left and right splits. More...
 
virtual void toILOTCache (double const I, double const L, double const O, double const T)
 Set ILOT cache from given values. More...
 
virtual void fromILOTCache (double &I, double &L, double &O, double &T) const
 Set references from ILOT cache. More...
 
virtual void fromILOCache (double &I, double &L, double &O) const
 Set references from ILOT cache but only for ILO components. More...
 
virtual double getCacheT () const
 Obtain the T component of ILOT cache. More...
 
virtual void initILOT (KDTreeNode *root, vector< Primitive * > const &primitives)
 Initialize the ILOT cache from given root node. More...
 
virtual void setCacheRoot (KDTreeNode *root)
 Set the cached root node. More...
 
- Protected Member Functions inherited from SimpleKDTreeFactory
virtual KDTreeNodebuildRecursive (KDTreeNode *parent, bool const left, vector< Primitive * > &primitives, int const depth, int const index)
 Recursively build a KDTree for given primitives. More...
 
virtual KDTreeNodebuildRecursiveRecipe (KDTreeNode *parent, bool const left, vector< Primitive * > &primitives, int const depth, int const index, std::function< void(KDTreeNode *node, KDTreeNode *parent, bool const left, vector< Primitive * > const &primitives)> f_computeNodeBoundaries, std::function< void(KDTreeNode *node, KDTreeNode *parent, vector< Primitive * > &primitives, int const depth)> f_defineSplit, std::function< void(vector< Primitive * > const &primitives, int const splitAxis, double const splitPos, vector< Primitive * > &leftPrimitives, vector< Primitive * > &rightPrimitives)> f_populateSplits, std::function< void(KDTreeNode *node, KDTreeNode *parent, vector< Primitive * > const &primitives, int const depth, int const index, vector< Primitive * > &leftPrimitives, vector< Primitive * > &rightPrimitives)> f_buildChildrenNodes)
 The recipe of the recursive building algorithm. It is meant to be used by the SimpleKDTreeFactory::buildRecursive but also by any alternative implementation which shares the same recipe (global logic) but changes the way some parts are computed. For instance, it is used by the MultiThreadKDTreeFactory to handle geometry-level parallelization. More...
 
virtual void reportKDTreeStats (KDTreeNodeRoot *root, vector< Primitive * > const &primitives) const
 Report KDTree stats of given root node at INFO logging level. More...
 
virtual void populateSplits (vector< Primitive * > const &primitives, int const splitAxis, double const splitPos, vector< Primitive * > &leftPrimitives, vector< Primitive * > &rightPrimitives) const
 Populate list of primitives for left and right splits from given primitives of node being splitted. More...
 
virtual void computeNodeBoundaries (KDTreeNode *node, KDTreeNode *parent, bool const left, vector< Primitive * > const &primitives) const
 Compute min and max position and surface area of bounding cuboid for given node. More...
 
virtual void onPopulateSplitsDigestPrimitive (Primitive *p, int const splitAxis, double const splitPos, vector< Primitive * > &leftPrimitives, vector< Primitive * > &rightPrimitives) const
 Function to assist SimpleKDTreeFactory::populateSplits by providing the logic of digesting a primitive. More...
 
virtual void computeMinMaxSAHForChild (KDTreeNode *node, KDTreeNode *parent, bool const left, vector< Primitive * > const &primitives) const
 Function to assist SimpleKDTreeFactory::computeNodeBoundaries when computing surface area heuristic and minimum and maximum positions for child nodes. More...
 
virtual void onRootBoundariesDigestPrimitive (Primitive *primitive, double &ax, double &ay, double &az, double &bx, double &by, double &bz) const
 Function to assist SimpleKDTreeFactory::computeNodeBoundaries by providing the logic of digesting a primitive. More...
 
virtual void onComputeNodeBoundariesCalcSAH (KDTreeNode *node, double const ax, double const ay, double const az, double const bx, double const by, double const bz) const
 Function to assist SimpleKDTreeFactory::computeNodeBoundaries when computing the SAH for a node. More...
 
virtual void makeLeaf (KDTreeNode *node, vector< Primitive * > const &primitives) const
 Make given node a leaf one. More...
 
- Protected Member Functions inherited from KDTreeFactory
virtual void lighten (KDTreeNodeRoot *root)
 Rebuild all children of given root KDTree node as LightKDTeeeNode nodes. More...
 
virtual LightKDTreeNode_lighten (KDTreeNode *node)
 Assist KDTreeFactory::lighten function by handling the lighten of a given non-root node. More...
 

Protected Attributes

size_t lossNodes
 How many loss nodes must be computed when optimizing the loss function \(\mathcal{L}_2\) to determine the best split position for a given KDTree node. More...
 
double ci
 Cost-weight for traversing interior nodes. More...
 
double cl
 Cost-weight for traversing leaf nodes. More...
 
double co
 Cost-weight for testing an object for intersection. More...
 
KDTreeNodecacheRoot = nullptr
 Cache pointer to root node of current KDTree being built.
 
double cacheI
 Cache last valid interior cost. More...
 
double cacheL
 Cache last valid leaves cost. More...
 
double cacheO
 Cache last valid object cost. More...
 
double cacheT
 Cache last valid tree cost. More...
 
std::function< void(void)> _lockILOT
 Function to lock the ILOT cache on a unique way (no thread but locker one must be able to use it). By default it is a void function, it must be overridden to provide concurrency handling. More...
 
std::function< void(void)> _unlockILOT
 Function to unlock the ILOT cache. It is the counterpart of the _lockILOT function. More...
 
- Protected Attributes inherited from SimpleKDTreeFactory
std::function< KDTreeNode *(KDTreeNode *, bool const, vector< Primitive * > &, int const, int const)> _buildRecursive
 The member function as attribute used to recursively build KDTree nodes. By default it will be assigned to the buildRecursive member function but it might be overridden by other implementations. For instance, to wrap the buildRecursive behavior to handle parallel building of KDTrees. More...
 
size_t minSplitPrimitives
 How many primitives are required for a node to be splitted. More...
 
- Protected Attributes inherited from KDTreeFactory
bool buildLightNodes = true
 When it is true, the KDTreeFactory is expected to build light nodes. It is, built KDTree must have a KDTreeRootNode which children are all LightKDTreeNode. When it is false, KDTreeFactory is allowed to build KDTree with KDTreeNode children, which might require more memory. More...
 
LightKDTreeNodeBlockAllocator lkdtnBlockAllocator
 The block allocator to speed-up lighten of KDTree by reducing allocation calls when instantiating multiple LightKDTreeNode. More...
 

Private Member Functions

template<typename Archive >
void serialize (Archive &ar, unsigned int const version)
 Serialize a surface area heuristic KDTree factory to a stream of bytes. More...
 

Friends

class MultiThreadSAHKDTreeFactory
 
class SAHKDTreeGeometricStrategy
 
class boost::serialization::access
 

Detailed Description

Class providing building methods for k-dimensional trees with surface area heuristic (SAH)

Author
Alberto M. Esmoris Pena
Version
1.0

The surface area heuristic KDTree factory defines the split point from the fact that an optimal performance point must be between the median of the distribution and the geometric center.

Let \(C_i\), \(C_l\) and \(C_o\) be the cost-weight for traversing interior nodes, traversing leaf nodes and testing an object for intersection respectively. Also, let \(N_i\), \(N_l\) and \(N_o\) number of interior nodes, number of leaf nodes and number of objects. Considering \(S_A(i)\) the surface area of the i-th interior node, \(S_A(l)\) the surface area of the l-th leaf node and generically \(S_A(x)\) the surface area of the \(x\) object or the \(x\) set of objects. It is now possible to define the cost of the tree where \(R\) is the root node as follows, where \(N_o(l)\) is the number of objects in the \(l\)-th leaf:

\[ C_T = \frac{ C_i \sum_{i=1}^{N_i}{S_A(i)} + C_l \sum_{l=1}^{N_l}{S_A(l)} + C_o \sum_{l=1}^{N_l}{S_A(l) N_o(l)} } {S_A(R)} \]

Alternatively, let \(S_l(o)\) be the set of leaves in which object \(o\) resides so the cost of the tree can be also defined as:

\[ C'_T = \frac{ C_i \sum_{i=1}^{N_i}{S_A(i)} + C_l \sum_{l=1}^{N_l}{S_A(l)} + C_o \sum_{o=1}^{N_o}{S_A[S_l(o)]} } {S_A(R)} \]

The main difference between \(C_T\) and \(C'_T\) would be that the second one fits better the case where a ray is not going to intersect multiple times the same object.

Now, let \(r\) be the normalized position of the splitting hyperplane for node \(N\) so \(r=0\) is the lower limit, \(r=1\) is the upper limit and \(r=\frac{1}{2}\) is the center. Moreover, let \(L_r\) and \(R_r\) be the left and right parts for the \(r\) split position and \(N_o(L_r)\) and \(N_o(R_r)\) be the number of objects at the left and right splits respectively. In consequence, following loss function arises:

\[ \mathcal{L}(r) = S_A(L_r)N_o(L_r) + S_A(R_r)N_o(R_r) - S_A(N)N_o(N) \]

Alternatively, considering the term \(-S_A(N)N_o(N)\) is the amount of work saved by making the node an interior one (so the minus sign), it can be treated as a constant so for the sake of simplicity it would lead to:

\[ \begin{array}{lll} \mathcal{L}(r) &=& S_A(L_r)N_o(L_r) + S_A(R_r)N_o(R_r) \\ &=& rS_A(N)N_o(L_r) + (1-r)S_A(N)N_o(R_r) \\ &=& S_A(N) \left[rN_o(L_r) + (1-r)N_o(R_r)\right] \end{array} \]

Differentiating with respect to \(r\) leads to:

\[ \frac{d\mathcal{L}}{dr} = \left[2N_o(L_r) - N_o(N)\right]\frac{d}{dr}S_A(L_r) + \left[ S_A(L_r) - S_A(R_r) \right] \frac{d}{dr}N_o(L_r) \]

Although \(N_o(L_r)\) is a discontinuous function, which implies \(\frac{d}{dr}N_o(L_r)\) is not defined, it is known that is always nonnegative which is enough to define a valid minimization criteria. In consequence, it is possible to analyze different scenarios. First, consider the case where the median lies somewhere satisfying \(r < \frac{1}{2}\). Thus, \(\frac{d}{dr}\mathcal{L}(r) < 0\) at the left side of the object median because \(N_o(L_r) < \frac{N_o(N)}{2}\) and \(S_A(L_r) < S_A(R_r)\). On the other hand, \(\frac{d}{dr}\mathcal{L}(r) > 0\) at the right side of the spatial median because \(N_o(L_r) > \frac{N_o(N)}{2}\) and \(S_A(L_r) > S_A(R_r)\). So the minimum must occur between the object median and the spatial median if the object median is to the left of the spatial median. It is easy to see that an analogous argument applies for the case where the object median is to the right of the spatial median. Then, the optimum split must lie between the object median and the spatial median (center).

To clarify, the object median is understood as related to the splitting plane that places one half of the objects on each side of the plane. While the spatial median \(\mu\) for a given KDTree node in \(\mathbb{R}^{n}\) with \(a = (a_1, \ldots, a_n)\) as minimum vertex and \(b = (b_1, \ldots, b_n)\) as maximum vertex is:

\[ \mu = \frac{a+b}{2} = \left(\frac{a_1+b_1}{2}, \ldots, \frac{a_n+b_n}{2}\right) \]

For a more detailed explanation refer to "Heuristics for ray tracing using space subdivision" by J. David MacDonald and Kellogg S. Booth.

See also
SimpleKDTreeFactory
AxisSAHKDTreeFactory

Constructor & Destructor Documentation

◆ SAHKDTreeFactory()

SAHKDTreeFactory::SAHKDTreeFactory ( size_t const  lossNodes = 21,
double const  ci = 1,
double const  cl = 1,
double const  co = 1 
)

Surface area heuristic KDTree factory default constructor.

See also
SAHKDTreeFactory::ci
SAHKDTreeFactory::cl
SAHKDTreeFactory::co

Member Function Documentation

◆ buildChildrenNodes()

void SAHKDTreeFactory::buildChildrenNodes ( KDTreeNode node,
KDTreeNode parent,
vector< Primitive * > const &  primitives,
int const  depth,
int const  index,
vector< Primitive * > &  leftPrimitives,
vector< Primitive * > &  rightPrimitives 
)
overridevirtual

Build children nodes using \(C_T\) heuristic to handle KDTree in-depth partitioning.

If root node, then by default assume it is a leaf node containing all primitives:

\[ t_0 : \mathrm{ILOT} = C_T = \frac{1}{S_A(R)} \left[ C_lS_A(R) + C_oS_A(R)N_o(R) \right] \]

After this, speculate its cost if it is make an interior node with its primitives being splitted into 2 leaf nodes:

\[ C_{Sr} = \frac{1}{S_A(R)} \left[ C_i S_A(R) + C_l \sum_{l=1}^{2} {S_A(l)} + C_o \sum_{l=1}^{2} {S_A(l)N_o(l)} \right] \]

Now if \(C_{Sr} \geq C_T\) at \(t_0\) then the process is stopped and all primitives remain in the root node. Otherwise, \(t_1\) happend so:

\[ t_1 : \mathrm{ILOT} = C_T = C_{Sr} \]

After the initial case, a similar process is recursively applied to each new node until \(C_{Si} \geq C_{T}\). It is, until the speculative cost is found to be greater or equal than current cost. This new speculative cost for non-root interior nodes is computed as follows, where \(N\) is current leaf node which might be splitted depending on analysis and \(L_r\) and \(R_r\) are its left and right splits respectively:

\[ \left\{\begin{array}{lll} k_1 &=& C_i S_A(N) \\ k_2 &=& C_l \left(S_A(L_r) + S_A(R_r)\right) - C_l S_A(N) \\ k_3 &=& C_o \left(S_A(L_r)N_o(L_r) + S_A(R_r)N_o(R_r)\right) - C_o S_A(N)N_o(N) \\ C_{Si} &=& \frac{1}{S_A(R)} \left[ C_i \sum_{i=1}^{N_i} S_A(i) + k_1 + C_l \sum_{l=1}^{N_l} S_A(l) + k_2 + C_o \sum_{l=1}^{N_l} S_A(l)N_o(l) + k_3 \right] \end{array}\right. \]

Thus, iterations \(x>1\) that will only happen when \(C_{Si} < C_T\) can be defined as:

\[ t_{x>1} : \mathrm{ILOT} = C_T = C_{Si} \]

See also
SimpleKDTreeFactory::buildChildrenNodes
SAHKDTreeFactory::defineSplit
SAHKDTreeFactory::computeKDTreeStats

Reimplemented from SimpleKDTreeFactory.

◆ buildChildrenNodesRecipe()

void SAHKDTreeFactory::buildChildrenNodesRecipe ( KDTreeNode node,
KDTreeNode parent,
vector< Primitive * > const &  primitives,
int const  depth,
int const  index,
vector< Primitive * > &  leftPrimitives,
vector< Primitive * > &  rightPrimitives,
std::function< void(KDTreeNode *node, int const depth, int const index, vector< Primitive * > &leftPrimitives, vector< Primitive * > &rightPrimitives)>  f_buildChildrenNodes 
)
virtual

The recipe for building of children nodes by SAH algorithm. It is meant to be used by the SAHKDTreeFactory::buildChildrenNodes but also by any alternative implementation which shared the same recipe (global logic) but changes the way some parts are computed. For instance, it is used to handle geometry-level parallelization.

Parameters
f_buildChildrenNodesFunction to do the building of the children nodes itself
See also
SAHKDTreeFactory::buildChildrenNodes
SAHKDTreeFactory::GEOM_buildChildrenNodes
MultiThreadKDTreeFactory

◆ checkNodeMustSplit()

bool SAHKDTreeFactory::checkNodeMustSplit ( vector< Primitive * > const &  primitives,
vector< Primitive * > const &  leftPrimitives,
vector< Primitive * > const &  rightPrimitives 
) const
overrideprotectedvirtual

Check wheter the node must be splitted (true) or not (false) depending on its total primitives.

For a SAH KDT a node must be splitted if there are enough primitives.

Returns
True if node must be splitted, false otherwise
See also
SimpleKDTreeFactory::checkNodeMustSplit
SimpleKDTreeFactory::minSplitPrimitives

Reimplemented from SimpleKDTreeFactory.

◆ clone()

KDTreeFactory * SAHKDTreeFactory::clone ( ) const
overridevirtual
See also
KDTreeFactory::clone

Implements KDTreeFactory.

◆ computeBestSplit()

void SAHKDTreeFactory::computeBestSplit ( vector< Primitive * > &  primitives,
size_t const  lossNodes,
double const  start,
double const  step,
int const  splitAxis,
double const  minBound,
double const  boundLength,
double &  loss,
double &  splitPos 
) const
protectedvirtual

Iteratively compute the best split position, it is the one with smaller loss.

Parameters
lossWhere initial loss is stored and where best found loss will be written
splitPosWhere initial split position is stored and where best found split position will be written
See also
SAHKDTreeFactory::findSplitPositionBySAH
SAHKDTreeFactory::GEOM_findSplitPositionBySAH
SAHKDTreeFactory::findSplitPositionBySAHRecipe
SAHKDTreeFactory::splitLoss

◆ computeKDTreeStats()

void SAHKDTreeFactory::computeKDTreeStats ( KDTreeNodeRoot root) const
overridevirtual

Compute the simple stats of the KDTree but also its cost based on surface area heuristic defined in defineSplit function.

Current tree cost is computed as \(C_T\) not as \(C'_T\)

See also
SimpleKDTreeFactory::computeKDTreeStats
SAHKDTreeFactory::defineSplit
SAHKDTreeFactory::buildChildrenNodes

Reimplemented from SimpleKDTreeFactory.

◆ cumulativeILOT()

double SAHKDTreeFactory::cumulativeILOT ( double &  hi,
double &  hl,
double &  ho,
double &  ht,
double const  _hi,
double const  _hl,
double const  _ho,
double const  saRoot 
) const
protectedvirtual

Compute the cumulative of \(C_T\) heuristic ILOT.

WARNING given reference to current total tree cost is also updated, not only returned. Thus, this is a full ILOT write function

Parameters
hiCurrent partial interior cost to be updated
hlCurrent partial leaves cost to be updated
hoCurrent partial object cost to be updated
htCurrent total tree cost to be updated
[in]_hiAdding magnitude to update partial interior cost
[in]_hlAdding magnitude to update partial leaves cost
[in]_hoAdding magnitude to update partial objeccts cost
[in]saRootSurface area of root node \(S_A(R)\) defining cumulative ILOT
Returns
Updated total tree cost

◆ defineSplit()

void SAHKDTreeFactory::defineSplit ( KDTreeNode node,
KDTreeNode parent,
vector< Primitive * > &  primitives,
int const  depth 
) const
overridevirtual

Define the split axis and position for current node.

To illustrate the define split method let \(a\) and \(b\) be the minimum and maximum vertices of given node. Let also \(\mu\) be the geometric center or spatial median and \(M_e\) be the object median. For the sake of simplicity, lets assume \(\mu < M_e\) so the iterative method will start at \(\mu\) and end at \(M_e\). If it was the other way, then the iterative method would start at \(M_e\) and end at \(\mu\). Now, if \(n\) is the number of loss nodes, \(\mathcal{L}_2\) is the loss function as defined in SAHKDTreeFactory::splitLoss and \(r = \frac{\phi-a}{b-a} \in [0, 1]\) is the normalized position of split position \(\phi\). Then, the iterative method can be defined as:

\[ \varphi(t) = \mu + t \frac{M_e-\mu}{n-1} \\ \left\{\begin{array}{lll} \phi_1 &=& \mu \\ \phi_{t>1} &=& \left\{\begin{array}{lll} \varphi(t) &,& \mathcal{L}_2\left[(\varphi(t)-a)(b-a)^{-1}\right] < \mathcal{L}_2\left[(\phi_{t-1}-a)(b-a)^{-1}\right] \\ \phi_{t-1} &,& \mathcal{L}_2\left[(\varphi(t)-a)(b-a)^{-1}\right] \geq \mathcal{L}_2\left[(\phi_{t-1}-a)(b-a)^{-1}\right] \end{array}\right. \end{array}\right. \]

Finally, \(r=\phi_n\) is the best found split position.

Notice that the median is constrained so \(M_e \in [a, b]\). Thus, in case there are enough objects lying outside node boundaries causing the median to be also outside, it will be truncated.

See also
SimpleKDTreeFactory::defineSplit
SAHKDTreeFactory::computeKDTreeStats
SAHKDTreeFactory::buildChildrenNodes
SAHKDTreeFactory::splitLoss
SAHKDTreeFactory::lossNodes
SAHKDTreeFactory::findSplitPositionBySAH

Reimplemented from SimpleKDTreeFactory.

◆ findSplitPositionBySAH()

double SAHKDTreeFactory::findSplitPositionBySAH ( KDTreeNode node,
vector< Primitive * > &  primitives 
) const
protectedvirtual

Find the best split position using Surface Area Heuristic (SAH) as described in SAHKDTreeFactory::defineSplit.

Returns
Loss of best split position. The position itself is already stored in given node
See also
SAHKDTreeFactory::defineSplit

Reimplemented in FastSAHKDTreeFactory.

◆ findSplitPositionBySAHRecipe()

double SAHKDTreeFactory::findSplitPositionBySAHRecipe ( KDTreeNode node,
vector< Primitive * > &  primitives,
std::function< void(vector< Primitive * >::iterator begin, vector< Primitive * >::iterator end, KDTreePrimitiveComparator comparator)>  f_sortPrimitives,
std::function< void(vector< Primitive * > &primitives, size_t const lossNodes, double const start, double const step, int const splitAxis, double const minBound, double const boundLength, double &loss, double &splitPos)>  f_computeLossNodes 
) const
protectedvirtual

The recipe for finding split position by SAH algorithm. It is meant to be used by the SAHKDTreeFactory::findSplitPositionBySAH but also by any alternative implementation which shares the same recipe ( global logic) but changes the way some parts are computed. For instance, it is used to handle geometry-level parallelization.

Parameters
f_sortPrimitivesFunction to sort primitives
f_computeLossNodesFunction to iteratively compute loss nodes and find the split position with best loss (the smallest)
See also
SAHKDTreeFactory::findSplitPositionBySAH
SAHKDTreeFactory::GEOM_findSplitPositionBySAH
MultiThreadKDTreeFactory
Returns
Loss of best split position. The position itself is already stored in given node

◆ fromILOCache()

virtual void SAHKDTreeFactory::fromILOCache ( double &  I,
double &  L,
double &  O 
) const
inlineprotectedvirtual

Set references from ILOT cache but only for ILO components.

See also
SAHKDTreeFactory::cacheI
SAHKDTreeFactory::cacheL
SAHKDTreeFactory::cacheO
SAHKDTreeFactory::fromILOTCache

◆ fromILOTCache()

virtual void SAHKDTreeFactory::fromILOTCache ( double &  I,
double &  L,
double &  O,
double &  T 
) const
inlineprotectedvirtual

◆ getCacheT()

virtual double SAHKDTreeFactory::getCacheT ( ) const
inlineprotectedvirtual

Obtain the T component of ILOT cache.

Returns
T component of ILOT cache

◆ getInteriorCost()

virtual double SAHKDTreeFactory::getInteriorCost ( ) const
inlinevirtual

Obtain the cost-weight of interior nodes.

Returns
Cost-weight of interior nodes
See also
SAHKDTreeFactory::ci

◆ getLeafCost()

virtual double SAHKDTreeFactory::getLeafCost ( ) const
inlinevirtual

Obtain the cost-weight for leaf nodes.

Returns
Cost-weight of leaf nodes
See also
SAKHDTreeFactory::cl

◆ getLossNodes()

virtual size_t SAHKDTreeFactory::getLossNodes ( ) const
inlinevirtual

Obtain the loss nodes used to compute the Surface Area Heuristic.

Returns
Nmber of loss nodes used to compute the Surface Area Heuristic
See also
SAHKDTreeFactory::lossNodes

◆ getObjectCost()

virtual double SAHKDTreeFactory::getObjectCost ( ) const
inlinevirtual

Obtain the cost-weight of testing an object for intersection.

Returns
Cost-weight of testing an object for intersection
See also
SAHKDTreeFactory::co

◆ heuristicILOT()

double SAHKDTreeFactory::heuristicILOT ( double &  hi,
double &  hl,
double &  ho,
double &  ht,
double const  surfaceAreaRoot,
double const  surfaceAreaInterior,
double const  surfaceAreaLeaf,
vector< Primitive * > const &  primitives 
) const
protectedvirtual

Compute the \(C_T\) heuristic preserving partials result of interest.

Costs and previous values are taken from factory and ILOT cache respectively

Parameters
[out]hiWhere the partial interior cost will be stored
[out]hlWhere the partial leaves cost will be stored
[out]hoWhere the partial object cost will be stored
[out]htWhere the total tree cost will be stored
[in]surfaceAreaRootSurface area of root node
[in]surfaceAreaInteriorSurface area of interior node
[in]surfaceAreaLeafSurface area of object/leaf node
[in]primitivesPrimitives defining the object/leaf cluster
Returns
\(C_T\) as heuristic ILOT

◆ initILOT()

void SAHKDTreeFactory::initILOT ( KDTreeNode root,
vector< Primitive * > const &  primitives 
)
protectedvirtual

Initialize the ILOT cache from given root node.

Parameters
rootRoot node to initialize ILOT cache from
primitivesPrimitives contained in root node

◆ internalizeILOT()

void SAHKDTreeFactory::internalizeILOT ( double &  hi,
double &  hl,
double &  ho,
double &  ht,
KDTreeNode node,
vector< Primitive * > const &  primitives,
vector< Primitive * > const &  leftPrimitives,
vector< Primitive * > const &  rightPrimitives 
)
protectedvirtual

Compute ILOT corresponding to internalization (make interior) of given node and its corresponding left and right splits.

Parameters
[in]hiTo store new interior heuristic cost
[in]hlTo store new leaves heuristic cost
[in]hoTo store new object heuristic cost
[in]htTo store new tree heuristic cost
nodeNode to internalize (make interior)
primitivesPrimitives on given node
leftPrimitivesPrimitives on left split
rightPrimitivesPrimitives on right split

◆ serialize()

template<typename Archive >
void SAHKDTreeFactory::serialize ( Archive &  ar,
unsigned int const  version 
)
inlineprivate

Serialize a surface area heuristic KDTree factory to a stream of bytes.

Template Parameters
ArchiveType of rendering
Parameters
arSpecific rendering for the stream of bytes
versionVersion number for the surface area heuristic KDTree factory

◆ setCacheRoot()

virtual void SAHKDTreeFactory::setCacheRoot ( KDTreeNode root)
inlineprotectedvirtual

Set the cached root node.

Parameters
rootThe new root node to be cached
See also
SAHKDTreeFactory::cacheRoot

◆ setInteriorCost()

virtual void SAHKDTreeFactory::setInteriorCost ( double const  ci)
inlinevirtual

Set the cost-weight of interior nodes.

Parameters
ciNew cost-weight for interior nodes
See also
SAHKDTreeFactory::ci

◆ setLeafCost()

virtual void SAHKDTreeFactory::setLeafCost ( double const  cl)
inlinevirtual

Set the cost-weight for leaf nodes.

Parameters
clNew cost-weight for leaf nodes
See also
SAHKDTreeFactory::cl

◆ setLossNodes()

virtual void SAHKDTreeFactory::setLossNodes ( size_t const  lossNodes)
inlinevirtual

Set the number loss nodes used to compute the Surface Area Heuristic.

Parameters
lossNodesNew number of loss nodes to compute the Surface Area Heuristic
See also
SAHKDTreeFactory::lossNodes

◆ setObjectCost()

virtual void SAHKDTreeFactory::setObjectCost ( double const  co)
inlinevirtual

Set the cost-weight of testing an object for intersection.

Parameters
coNew cost-weight of testing an object for intersection
See also
SAHKDTreeFactory::co

◆ splitLoss()

double SAHKDTreeFactory::splitLoss ( vector< Primitive * > const &  primitives,
int const  splitAxis,
double const  splitPos,
double const  r 
) const
protectedvirtual

Compute the loss function for the splitting hyperplane.

The loss function by default is:

\[ \begin{array}{lll} \mathcal{L}(r) &=& S_A(L_r)N_o(L_r) + S_A(R_r)N_o(R_r) \\ &=& S_A(N) [rN_o(L_r) + (1-r)N_o(R_r)] \end{array} \]

However, as \(S_A(N)\) is a constant for the same node, it is computationally cheaper to compute an alternative version:

\[ \mathcal{L_2}(r) = rN_o(L_r) + (1-r)N_o(R_r) \]

Parameters
primitivesVector of primitives involved in the split
splitAxisAxis at which split will be done
splitPosPosition of the hyperplane in the split axis
rThe ratio or normalized split position in \([0, 1]\)
Returns
Value obtained after evaluating loss function
See also
SAHKDTreeFactory::defineSplit
SAHKDTreeFactory::lossNodes

◆ toILOTCache()

virtual void SAHKDTreeFactory::toILOTCache ( double const  I,
double const  L,
double const  O,
double const  T 
)
inlineprotectedvirtual

Member Data Documentation

◆ _lockILOT

std::function<void(void)> SAHKDTreeFactory::_lockILOT
protected

Function to lock the ILOT cache on a unique way (no thread but locker one must be able to use it). By default it is a void function, it must be overridden to provide concurrency handling.

See also
MultiThreadSAHKDTreeFactory
SAHKDTreeFactory::_unlockILOT

◆ _unlockILOT

std::function<void(void)> SAHKDTreeFactory::_unlockILOT
protected

Function to unlock the ILOT cache. It is the counterpart of the _lockILOT function.

See also
MultiThreadSAHKDTreeFactory
SAHKDTreeFactory::_lockILOT

◆ cacheI

double SAHKDTreeFactory::cacheI
protected

Cache last valid interior cost.

\[ C_i \sum_{i=1}^{N_i}{S_A(i)} \]

See also
SAHKDTreeFactory::toILOTCache
SAHKDTreeFactory::fromILOTCache

◆ cacheL

double SAHKDTreeFactory::cacheL
protected

Cache last valid leaves cost.

\[ C_l \sum_{l=1}^{N_l}{S_A(l)} \]

See also
SAHKDTreeFactory::toILOTCache
SAHKDTreeFactory::fromILOTCache

◆ cacheO

double SAHKDTreeFactory::cacheO
protected

Cache last valid object cost.

\[ C_o \sum_{l=1}^{N_l}{S_A(l) N_o(l)} \]

See also
SAHKDTreeFactory::toILOTCache
SAHKDTreeFactory::fromILOTCache

◆ cacheT

double SAHKDTreeFactory::cacheT
protected

Cache last valid tree cost.

\[ C_T = \frac{ C_i \sum_{i=1}^{N_i}{S_A(i)} + C_l \sum_{l=1}^{N_l}{S_A(l)} + C_o \sum_{l=1}^{N_l}{S_A(l) N_o(l)} } {S_A(R)} \]

See also
SAHKDTreeFactory::toILOTCache
SAHKDTreeFactory::fromILOTCache

◆ ci

double SAHKDTreeFactory::ci
protected

Cost-weight for traversing interior nodes.

See also
SAHKDTreeFactory::defineSplit

◆ cl

double SAHKDTreeFactory::cl
protected

Cost-weight for traversing leaf nodes.

See also
SAHKDTreeFactory::defineSplit

◆ co

double SAHKDTreeFactory::co
protected

Cost-weight for testing an object for intersection.

See also
SAHKDTreeFactory::defineSplit

◆ lossNodes

size_t SAHKDTreeFactory::lossNodes
protected

How many loss nodes must be computed when optimizing the loss function \(\mathcal{L}_2\) to determine the best split position for a given KDTree node.

See also
SAHKDTreeFactory::splitLoss

The documentation for this class was generated from the following files: