Skip to content

Commit

Permalink
Add shape and volume builder (#1126)
Browse files Browse the repository at this point in the history
* Add 'inserted' result to CSG tree
* Return insertion from CSG unit builder
* Fix test JSON output for CSG tree
* Add script for generating graphs from CSG tree
* Add shape, object, and sketch volume builder
* Add shape test and volume construction
* Rename {Shape, ShapeImpl} -> {ShapeBase, Shape} and add constructor
  • Loading branch information
sethrj committed Feb 28, 2024
1 parent 9e42bd8 commit 3ded3de
Show file tree
Hide file tree
Showing 21 changed files with 707 additions and 89 deletions.
82 changes: 82 additions & 0 deletions scripts/user/orange-csg-to-dot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/usr/bin/env python3
# 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)
"""
Convert an ORANGE CSG JSON representation to a GraphViz input.
"""
import json

class DoxygenGenerator:
def __init__(self, f):
self.f = f
self.write = f.write
self.write("""\
strict digraph {
rankdir=TB;
""")

def __enter__(self):
return self

def __exit__(self, type, value, traceback):
self.write("}\n")

def write_literal(self, i, value):
self.write(f"{i:02d} [label=\"{value}\"];\n")

def write_node(self, i, value, edges):
self.write_literal(i, f"{i}: {value}")
for e in edges:
self.write(f"{i:02d} -> {e:02d};\n")

def process(gen, tree):
for (i, node) in enumerate(tree):
if isinstance(node, str):
# True (or false??)
gen.write_literal(i, node)
continue
(nodetype, value) = node
if nodetype == "S":
# Surface
gen.write_literal(i, f"S{value}")
elif nodetype in ("~", "="):
# Negated
gen.write_node(i, nodetype, [value])
else:
# Joined
gen.write_node(i, nodetype, value)

def run(infile, outfile):
tree = json.load(infile)
with DoxygenGenerator(outfile) as gen:
process(gen, tree)

def main():
import argparse
import sys

parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"input",
help="Input filename (- for stdin)")
parser.add_argument(
"-o", "--output",
default=None,
help="Output filename (empty for stdout)")
args = parser.parse_args()

if args.input == "-":
infile = sys.stdin
else:
infile = open(args.input)

if not args.output:
outfile = sys.stdout
else:
outfile = open(args.output)

run(infile, outfile)

if __name__ == "__main__":
main()
2 changes: 2 additions & 0 deletions src/orange/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ list(APPEND SOURCES
orangeinp/CsgTree.cc
orangeinp/CsgTypes.cc
orangeinp/CsgTreeUtils.cc
orangeinp/Shape.cc
orangeinp/detail/BoundingZone.cc
orangeinp/detail/ConvexSurfaceState.cc
orangeinp/detail/CsgUnitBuilder.cc
orangeinp/detail/LocalSurfaceInserter.cc
orangeinp/detail/NodeSimplifier.cc
orangeinp/detail/TransformInserter.cc
orangeinp/detail/VolumeBuilder.cc
surf/ConeAligned.cc
surf/CylAligned.cc
surf/FaceNamer.cc
Expand Down
4 changes: 2 additions & 2 deletions src/orange/orangeinp/ConvexSurfaceBuilder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ void ConvexSurfaceBuilder::insert_transformed(std::string&& extension,
using SurfaceT = std::decay_t<decltype(final_surf)>;

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

// Replace sense so we know to flip the node if needed
sense = final_sense;
Expand All @@ -123,7 +123,7 @@ void ConvexSurfaceBuilder::insert_transformed(std::string&& extension,
// "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});
node_id = ub_->insert_csg(Negated{node_id}).first;
}

// Add sense to "joined" region
Expand Down
6 changes: 3 additions & 3 deletions src/orange/orangeinp/CsgTree.cc
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ CsgTree::CsgTree()
*
* This performs a single level of simplification.
*/
auto CsgTree::insert(Node&& n) -> NodeId
auto CsgTree::insert(Node&& n) -> Insertion
{
CELER_EXPECT(!n.valueless_by_exception()
&& std::visit(IsUserNodeValid{this->size()}, n));
Expand All @@ -85,7 +85,7 @@ auto CsgTree::insert(Node&& n) -> NodeId
if (auto* a = std::get_if<orangeinp::Aliased>(&n))
{
// Simplified to an aliased node
return a->node;
return {a->node, false};
}
}
}
Expand All @@ -98,7 +98,7 @@ auto CsgTree::insert(Node&& n) -> NodeId
// Add a copy of the new node
nodes_.push_back(iter->first);
}
return iter->second;
return {iter->second, inserted};
}

//---------------------------------------------------------------------------//
Expand Down
9 changes: 5 additions & 4 deletions src/orange/orangeinp/CsgTree.hh
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,18 @@ class CsgTree
using Node = orangeinp::Node;
using NodeId = orangeinp::NodeId;
using size_type = NodeId::size_type;
using Insertion = std::pair<NodeId, bool>;
//!@}

public:
// Construct with no nodes except "true" and "false"
CsgTree();

// Add a node and return the new ID
NodeId insert(Node&& n);
// Add a node and return the new ID and whether it's unique
Insertion insert(Node&& n);

// Add a surface ID and return the new ID
inline NodeId insert(LocalSurfaceId s);
inline Insertion insert(LocalSurfaceId s);

//! Number of nodes
size_type size() const { return nodes_.size(); }
Expand Down Expand Up @@ -104,7 +105,7 @@ std::ostream& operator<<(std::ostream& os, CsgTree const&);
/*!
* Add a surface.
*/
auto CsgTree::insert(LocalSurfaceId s) -> NodeId
auto CsgTree::insert(LocalSurfaceId s) -> Insertion
{
return this->insert(orangeinp::Surface{s});
}
Expand Down
57 changes: 57 additions & 0 deletions src/orange/orangeinp/ObjectInterface.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//----------------------------------*-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/ObjectInterface.hh
//---------------------------------------------------------------------------//
#pragma once

#include <memory>
#include <string_view>

#include "corecel/Macros.hh"

#include "CsgTypes.hh"

namespace celeritas
{
namespace orangeinp
{
namespace detail
{
class VolumeBuilder;
}

//---------------------------------------------------------------------------//
/*!
* Base class for constructing high-level CSG objects in ORANGE.
*/
class ObjectInterface
{
public:
//!@{
//! \name Type aliases
using SPConstObject = std::shared_ptr<ObjectInterface const>;
using VolumeBuilder = detail::VolumeBuilder;
//!@}

public:
//! Short unique name of this object
virtual std::string_view label() const = 0;

//! Construct a volume from this object
virtual NodeId build(VolumeBuilder&) const = 0;

protected:
//!@{
//! Allow construction and assignment only through daughter classes
ObjectInterface() = default;
virtual ~ObjectInterface() = default;
CELER_DEFAULT_COPY_MOVE(ObjectInterface);
//!@}
};

//---------------------------------------------------------------------------//
} // namespace orangeinp
} // namespace celeritas
58 changes: 58 additions & 0 deletions src/orange/orangeinp/Shape.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//----------------------------------*-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/Shape.cc
//---------------------------------------------------------------------------//
#include "Shape.hh"

#include "ConvexSurfaceBuilder.hh"
#include "CsgTreeUtils.hh"

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

namespace celeritas
{
namespace orangeinp
{
//---------------------------------------------------------------------------//
/*!
* Construct a volume from this shape.
*/
NodeId ShapeBase::build(VolumeBuilder& vb) const
{
// Set input attributes for surface state
detail::ConvexSurfaceState css;
css.transform = &vb.local_transform();
css.object_name = this->label();
css.make_face_name = {}; // No prefix for standalone shapes

// Construct surfaces
auto sb = ConvexSurfaceBuilder(&vb.unit_builder(), &css);
this->build_interior(sb);

// Intersect the given surfaces to create a new CSG node
auto node_id = vb.insert_region(Label{std::move(css.object_name)},
Joined{op_and, std::move(css.nodes)},
calc_merged_bzone(css));

return node_id;
}

//---------------------------------------------------------------------------//
// EXPLICIT INSTANTIATION
//---------------------------------------------------------------------------//

template class Shape<Box>;
template class Shape<Cone>;
template class Shape<Cylinder>;
template class Shape<Ellipsoid>;
template class Shape<Prism>;
template class Shape<Sphere>;

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

0 comments on commit 3ded3de

Please sign in to comment.