Skip to content

Commit

Permalink
Add convex surface builder (#1113)
Browse files Browse the repository at this point in the history
* Add prefix to face namer
* Invalidate interior and fix aligned plane in surface clipper
* Fix includes
* Add infinite bounding zone helper
* Add convex surface builder
* Fix explicit instantiation
* Address review feedback
  • Loading branch information
sethrj committed Feb 21, 2024
1 parent f1862f3 commit dd805a2
Show file tree
Hide file tree
Showing 20 changed files with 823 additions and 39 deletions.
1 change: 1 addition & 0 deletions src/orange/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ list(APPEND SOURCES
detail/SurfacesRecordBuilder.cc
detail/UnitInserter.cc
detail/UniverseInserter.cc
orangeinp/ConvexSurfaceBuilder.cc
orangeinp/CsgTree.cc
orangeinp/CsgTypes.cc
orangeinp/CsgTreeUtils.cc
Expand Down
145 changes: 145 additions & 0 deletions src/orange/orangeinp/ConvexSurfaceBuilder.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
//----------------------------------*-C++-*----------------------------------//
// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers.
// See the top-level COPYRIGHT file for details.
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
//---------------------------------------------------------------------------//
//! \file orange/orangeinp/ConvexSurfaceBuilder.cc
//---------------------------------------------------------------------------//
#include "ConvexSurfaceBuilder.hh"

#include "orange/surf/RecursiveSimplifier.hh"
#include "orange/surf/SurfaceClipper.hh"

#include "detail/ConvexSurfaceState.hh"
#include "detail/CsgUnitBuilder.hh"
#include "detail/NegatedSurfaceClipper.hh"

namespace celeritas
{
namespace orangeinp
{
namespace
{
//---------------------------------------------------------------------------//
struct ClipImpl
{
detail::BoundingZone* bzone{nullptr};

template<class S>
void operator()(Sense s, S const& surf)
{
if (s == Sense::inside)
{
SurfaceClipper clip{&bzone->interior, &bzone->exterior};
clip(surf);
}
else
{
detail::NegatedSurfaceClipper clip{bzone};
clip(surf);
}
}
};

//---------------------------------------------------------------------------//
} // namespace

//---------------------------------------------------------------------------//
/*!
* Construct with persistent unit builder and less persistent state.
*
* Both arguments must have lifetimes that exceed the surface builder, but the
* "unit builder" will have a duration of the whole unit construction, whereas
* the state just has the duration of the convex surface set being built.
*/
ConvexSurfaceBuilder::ConvexSurfaceBuilder(UnitBuilder* ub, State* state)
: ub_{ub}, state_{state}
{
CELER_EXPECT(ub_ && state_);
CELER_EXPECT(*state_);
}

//---------------------------------------------------------------------------//
/*!
* Add a surface with a sense.
*
* The resulting surface *MUST* result in a convex region.
*/
template<class S>
void ConvexSurfaceBuilder::operator()(Sense sense, S const& surf)
{
// First, clip the local bounding zone based on the given surface
RecursiveSimplifier clip_simplified_local(ClipImpl{&state_->local_bzone},
ub_->tol());
clip_simplified_local(sense, surf);

// Next, apply the transform and insert
return this->insert_transformed(state_->make_face_name(sense, surf),
sense,
apply_transform(*state_->transform, surf));
}

//---------------------------------------------------------------------------//
// HELPER FUNCTION DEFINITIONS
//---------------------------------------------------------------------------//
/*!
* Add a surface after transforming it to an unknown type.
*
* \param extension Constructed metadata for the surface node
* \param sense Whether the convex region is inside/outside this surface
* \param surf Type-deleted surface
*/
void ConvexSurfaceBuilder::insert_transformed(std::string&& extension,
Sense sense,
VariantSurface const& surf)
{
NodeId node_id;
auto construct_impl = [&](Sense final_sense, auto&& final_surf) {
using SurfaceT = std::decay_t<decltype(final_surf)>;

// Insert transformed surface, deduplicating and creating CSG node
node_id = ub_->insert_surface(final_surf);

// Replace sense so we know to flip the node if needed
sense = final_sense;

// Update surface's global-reference bounding zone using *deduplicated*
// surface
ClipImpl{&state_->global_bzone}(final_sense,
ub_->get_surface<SurfaceT>(node_id));
};

// Construct transformed surface, get back the node ID, update the sense
RecursiveSimplifier construct_final(construct_impl, ub_->tol());
construct_final(sense, surf);
CELER_ASSERT(node_id);

// Add metadata for the surface node
ub_->insert_md(node_id, Label{state_->object_name, std::move(extension)});

if (sense == Sense::inside)
{
// "Inside" the surface (negative quadric evaluation) means we have to
// negate the CSG result
static_assert(Sense::inside == to_sense(false));
node_id = ub_->insert_csg(Negated{node_id});
}

// Add sense to "joined" region
state_->nodes.push_back(node_id);
}

//---------------------------------------------------------------------------//
// FREE FUNCTION DEFINITIONS
//---------------------------------------------------------------------------//
/*!
* Construct a surface using a variant.
*/
void visit(ConvexSurfaceBuilder& csb, Sense sense, VariantSurface const& surf)
{
std::visit([&csb, sense](auto const& s) { csb(sense, s); }, surf);
}

//---------------------------------------------------------------------------//
} // namespace orangeinp
} // namespace celeritas
92 changes: 92 additions & 0 deletions src/orange/orangeinp/ConvexSurfaceBuilder.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//----------------------------------*-C++-*----------------------------------//
// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers.
// See the top-level COPYRIGHT file for details.
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
//---------------------------------------------------------------------------//
//! \file orange/orangeinp/ConvexSurfaceBuilder.hh
//---------------------------------------------------------------------------//
#pragma once

#include <vector>

#include "orange/OrangeTypes.hh"
#include "orange/surf/VariantSurface.hh"

#include "CsgTypes.hh"

namespace celeritas
{
namespace orangeinp
{
namespace detail
{
class CsgUnitBuilder;
struct ConvexSurfaceState;
} // namespace detail

//---------------------------------------------------------------------------//
/*!
* Build a set of intersecting surfaces within a CSG node.
*
* This is the building block for constructing shapes, solids, and so forth.
* The result of this class is:
* - CSG nodes describing the inserted surfaces which have been transformed
* into the global reference frame
* - Metadata combining the surfaces names with the object name
* - Bounding boxes (interior and exterior)
*
* \todo Should we require that the user implicitly guarantee that the result
* is convex, e.g. prohibit quadrics outside "saddle" points? What about a
* torus, which (unless degenerate) is never convex? Or should we just require
* that "exiting a surface exits the region"? (Think about application to OR-ed
* combinations for safety calculation.)
*/
class ConvexSurfaceBuilder
{
public:
//! Add a surface with negative quadric value being "inside"
template<class S>
void operator()(S const& surf)
{
return (*this)(Sense::inside, surf);
}

// Add a surface with specified sense (usually inside except for planes)
template<class S>
void operator()(Sense sense, S const& surf);

public:
// "Private", to be used by testing and detail
using State = detail::ConvexSurfaceState;
using UnitBuilder = detail::CsgUnitBuilder;
using VecNode = std::vector<NodeId>;

// Construct with persistent unit builder and less persistent state
ConvexSurfaceBuilder(UnitBuilder* ub, State* state);

private:
//// TYPES ////

// Helper for constructing surfaces
UnitBuilder* ub_;

// State being modified by this builder
State* state_;

//// HELPER FUNCTION ////

void insert_transformed(std::string&& ext,
Sense sense,
VariantSurface const& surf);
};

//---------------------------------------------------------------------------//
// FREE FUNCTIONS
//---------------------------------------------------------------------------//

// Apply a convex surface builder to an unknown type
void visit(ConvexSurfaceBuilder& csb, Sense sense, VariantSurface const& surf);

//---------------------------------------------------------------------------//
} // namespace orangeinp
} // namespace celeritas
9 changes: 9 additions & 0 deletions src/orange/orangeinp/detail/BoundingZone.cc
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@ BBox calc_union(BBox const& a, BBox const& b, BoxOp op)
//---------------------------------------------------------------------------//
} // namespace

//---------------------------------------------------------------------------//
/*!
* Create an "everything is known inside" zone for intersecting.
*/
BoundingZone BoundingZone::from_infinite()
{
return {BBox::from_infinite(), BBox::from_infinite(), false};
}

//---------------------------------------------------------------------------//
/*!
* Calculate the intersection of two bounding zones.
Expand Down
6 changes: 6 additions & 0 deletions src/orange/orangeinp/detail/BoundingZone.hh
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,14 @@ struct BoundingZone

// Flip inside and outside
inline void negate();

// Create an "everything is known inside" zone for intersecting
static BoundingZone from_infinite();
};

//---------------------------------------------------------------------------//
// FREE FUNCTIONS
//---------------------------------------------------------------------------//
// Calculate the intersection of two bounding zones
BoundingZone calc_intersection(BoundingZone const& a, BoundingZone const& b);

Expand Down
64 changes: 64 additions & 0 deletions src/orange/orangeinp/detail/ConvexSurfaceState.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//----------------------------------*-C++-*----------------------------------//
// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers.
// See the top-level COPYRIGHT file for details.
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
//---------------------------------------------------------------------------//
//! \file orange/orangeinp/detail/ConvexSurfaceState.hh
//---------------------------------------------------------------------------//
#pragma once

#include <string>
#include <vector>

#include "orange/surf/FaceNamer.hh"
#include "orange/transform/VariantTransform.hh"

#include "BoundingZone.hh"
#include "../CsgTypes.hh"

namespace celeritas
{
namespace orangeinp
{
namespace detail
{
//---------------------------------------------------------------------------//
/*!
* Local state for building a set of intersected surfaces.
*
* Note that the surface clippers have *pointers* to local and global bounding
* zones. Those must exceed the lifetime of this state.
*/
struct ConvexSurfaceState
{
//!@{
//! \name Input state
//! Local-to-global transform
VariantTransform const* transform{nullptr};
//! Name of the object being built
std::string object_name;
//! Generate a name from a surface (has internal state)
FaceNamer make_face_name;
//!@}

//!@{
//! \name Output state
//! Local (to convex surface state) interior/exterior
BoundingZone local_bzone = BoundingZone::from_infinite();
//! Global (to unit) interior/exterior
BoundingZone global_bzone = BoundingZone::from_infinite();
//! Inserted CSG nodes
std::vector<NodeId> nodes;
//!@}

//! True if the state is valid
explicit operator bool() const
{
return transform && !object_name.empty();
}
};

//---------------------------------------------------------------------------//
} // namespace detail
} // namespace orangeinp
} // namespace celeritas

0 comments on commit dd805a2

Please sign in to comment.