# CSG

## CSG Operations

There are three binary csg operations which can construct extremely complex objects from very simple primitives: union ($\cup$), intersection ($\cap$) and subtraction (i.e. difference).

This diagram shows the basic idea:

The code for this in our system would look this this:

```
cyl = Cylinder(0.7)
cyl_cross = cyl ∪ leaf(cyl, Geometry.rotationd(90, 0, 0)) ∪ leaf(cyl, Geometry.rotationd(0, 90, 0))
cube = Cuboid(1.0, 1.0, 1.0)
sph = Sphere(1.3)
rounded_cube = cube ∩ sph
result = rounded_cube - cyl_cross
Vis.draw(result, numdivisions=100)
```

`OpticSim.leaf`

— Function`leaf(surf::ParametricSurface{T}, transform::Transform{T} = identitytransform(T)) -> CSGGenerator{T}`

Create a leaf node from a parametric surface with a given transform.

`Base.:∪`

— Function`∪(a::Union{CSGGenerator{T},ParametricSurface{T}}, b::Union{CSGGenerator{T},ParametricSurface{T}}) where {T<:Real}`

Create a binary node in the CSG tree representing a union between `a`

and `b`

.

`Base.:∩`

— Function`∩(a::Union{CSGGenerator{T},ParametricSurface{T}}, b::Union{CSGGenerator{T},ParametricSurface{T}}) where {T<:Real}`

Create a binary node in the CSG tree representing an intersection between `a`

and `b`

.

`Base.:-`

— Function`-(a::Union{CSGGenerator{T},ParametricSurface{T}}, b::Union{CSGGenerator{T},ParametricSurface{T}}) where {T<:Real}`

Create a binary node in the CSG tree representing the difference of `a`

and `b`

, essentially `a - b`

.

## Pre-made CSG Shapes

There are also some shortcut methods available to create common CSG objects more easily:

`OpticSim.BoundedCylinder`

— Function`BoundedCylinder(radius::T, height::T; interface::NullOrFresnel{T} = nullinterface(T)) -> CSGGenerator{T}`

Create a cylinder with planar caps on both ends centred at `(0, 0, 0)`

with axis `(0, 0, 1)`

.

`OpticSim.Cuboid`

— Function`Cuboid(halfsizex::T, halfsizey::T, halfsizez::T; interface::NullOrFresnel{T} = nullinterface(T)) -> CSGGenerator{T}`

Create a cuboid centred at `(0, 0, 0)`

.

`OpticSim.HexagonalPrism`

— Function`HexagonalPrism(side_length::T, visheight::T = 2.0; interface::NullOrFresnel{T} = nullinterface(T)) -> CSGGenerator{T}`

Create an infinitely tall hexagonal prism with axis `(0, 0, 1)`

, the longer hexagon diameter is along the x axis. For visualization `visheight`

is used, **note that this does not fully represent the surface**.

`OpticSim.RectangularPrism`

— Function`RectangularPrism(halfsizex::T, halfsizey::T, visheight::T=2.0; interface::NullOrFresnel{T} = nullinterface(T)) -> CSGGenerator{T}`

Create an infinitely tall rectangular prism with axis `(0, 0, 1)`

. For visualization `visheight`

is used, **note that this does not fully represent the surface**.

`OpticSim.TriangularPrism`

— Function`TriangularPrism(side_length::T, visheight::T = 2.0; interface::NullOrFresnel{T} = nullinterface(T)) -> CSGGenerator{T}`

Create an infinitely tall triangular prism with axis `(0, 0, 1)`

. For visualization `visheight`

is used, **note that this does not fully represent the surface**.

`OpticSim.Spider`

— Function`Spider(narms::Int, armwidth::T, radius::T, origin::SVector{3,T} = SVector{3,T}(0.0, 0.0, 0.0), normal::SVector{3,T} = SVector{3,T}(0.0, 0.0, 1.0)) -> Vector{Rectangle{T}}`

Creates a 'spider' obscuration with `narms`

rectangular arms evenly spaced around a circle defined by `origin`

and `normal`

. Each arm is a rectangle `armwidth`

×`radius`

.

e.g. for 3 and 4 arms we get:

```
| _|_
/ \ |
```

## CSG Types

These are the types of the primary CSG elements, i.e. the nodes in the CSG tree.

`OpticSim.CSGTree`

— TypeAbstract type representing any evaluated CSG structure.

`OpticSim.CSGGenerator`

— Type`CSGGenerator{T<:Real}`

This is the type you should use when making CSG objects. This type allows for the construction of `CSGTree`

objects with different transforms. When the generator is evaluated, all transforms are propagated down to the `LeafNode`

s and stored there.

**Example**

```
a = Cylinder(1.0,1.0)
b = Plane([0.0,0.0,1.0], [0.0,0.0,0.0])
generator = a ∩ b
# now make a csg object that can be ray traced
csgobj = generator(Transform(1.0,1.0,2.0))
```

`OpticSim.ComplementNode`

— Type`ComplementNode{T,C<:CSGTree{T}} <: CSGTree{T}`

An evaluated complement node within the CSG tree, must be the second child of a `IntersectionNode`

forming a subtraction.

`OpticSim.UnionNode`

— Type`UnionNode{T,L<:CSGTree{T},R<:CSGTree{T}} <: CSGTree{T}`

An evaluated union node within the CSG tree.

`OpticSim.IntersectionNode`

— Type`IntersectionNode{T,L<:CSGTree{T},R<:CSGTree{T}} <: CSGTree{T}`

An evaluated intersection node within the CSG tree.

`OpticSim.LeafNode`

— Type`LeafNode{T,S<:ParametricSurface{T}} <: CSGTree{T}`

An evaluated leaf node in the CSG tree, `geometry`

attribute which contains a `ParametricSurface`

of type `S`

. The leaf node also has a transform associated which is the composition of all nodes above it in the tree. As such, transforming points from the geometry using this transform puts them in world space, and transforming rays by the inverse transform puts them in object space.

## Additional Functions and Types

These are the internal types and functions used for geometric/CSG operations.

### Functions

`OpticSim.surfaceintersection`

— Method`surfaceintersection(obj::CSGTree{T}, r::AbstractRay{T,N})`

Calculates the intersection of `r`

with CSG object, `obj`

.

Returns an `EmptyInterval`

if there is no intersection, an `Interval`

if there is one or two interesections and a `DisjointUnion`

if there are more than two intersections.

The ray is intersected with the `LeafNode`

s that make up the CSG object and the resulting `Interval`

s and `DisjointUnion`

s are composed with the same boolean operations to give a final result. The ray is transformed by the inverse of the transform associated with the leaf node to put it in *object space* for that node before the intersection is carried out, typically this *object space* is centered at the origin, but may differ for each primitive.

Some intersections are culled without actually evaluating them by first checking if the ray intersects the `BoundingBox`

of each node in the `CSGTree`

, this can substantially improve performance in some cases.

`OpticSim.inside`

— Method```
inside(obj::CSGTree{T}, point::SVector{3,T}) -> Bool
inside(obj::CSGTree{T}, x::T, y::T, z::T) -> Bool
```

Tests whether a 3D point in world space is *inside* `obj`

.

`OpticSim.onsurface`

— Method```
onsurface(obj::CSGTree{T}, point::SVector{3,T}) -> Bool
onsurface(obj::CSGTree{T}, x::T, y::T, z::T) -> Bool
```

Tests whether a 3D point in world space is *on* the surface (i.e. shell) of `obj`

.

### Intervals

`OpticSim.Interval`

— Type`Interval{T} <: AbstractRayInterval{T}`

Datatype representing an interval between two `IntervalPoint`

s on a ray.

The lower element can either be `RayOrigin`

or an `Intersection`

. The upper element can either be an `Intersection`

or `Infinity`

.

```
positivehalfspace(int::Intersection) -> Interval with lower = int, upper = Infinity
rayorigininterval(int::Intersection) -> Interval with lower = RayOrigin, upper = int
Interval(low, high)
```

Has the following accessor methods:

```
lower(a::Interval{T}) -> Union{RayOrigin{T},Intersection{T,3}}
upper(a::Interval{T}) -> Union{Intersection{T,3},Infinity{T}}
```

`OpticSim.EmptyInterval`

— Type`EmptyInterval{T} <: AbstractRayInterval{T}`

An interval with no `Intersection`

s which is also not infinite.

```
EmptyInterval(T = Float64)
EmptyInterval{T}()
```

`OpticSim.DisjointUnion`

— TypeDatatype representing an ordered series of disjoint intervals on a ray. An arbitrary array of `Interval`

s can be input to the constructor and they will automatically be processed into a valid `DisjointUnion`

(or a single `Interval`

if appropriate).

`DisjointUnion(intervals::AbstractVector{Interval{R}})`

`OpticSim.isemptyinterval`

— Function`isemptyinterval(a) -> Bool`

Returns true if `a`

is an `EmptyInterval`

. In performance critical contexts use `a isa EmptyInterval{T}`

.

`OpticSim.ispositivehalfspace`

— Function`ispositivehalfspace(a) -> Bool`

Returns true if `upper(a)`

is `Infinity`

. In performance critical contexts check directly i.e. `upper(a) isa Infinity{T}`

.

`OpticSim.israyorigininterval`

— Function`israyorigininterval(a) -> Bool`

Returns true if `lower(a)`

is `RayOrigin`

. In performance critical contexts check directly i.e. `lower(a) isa RayOrigin{T}`

.

`OpticSim.halfspaceintersection`

— Function`halfspaceintersection(a::Interval{T}) -> Intersection{T,3}`

Returns the `Intersection`

from a half space `Interval`

, throws an error if not a half space.

`OpticSim.closestintersection`

— Function`closestintersection(a::Union{EmptyInterval{T},Interval{T},DisjointUnion{T}}, ignorenull::Bool = true) -> Union{Nothing,Intersection{T,3}}`

Returns the closest `Intersection`

from an `Interval`

or `DisjointUnion`

. Ignores intersection with null interfaces if `ignorenull`

is true. Will return `nothing`

if there is no valid intersection.

`OpticSim.IntervalPool`

— TypeTo prevent allocations we have a manually managed pool of arrays of `Interval`

s which are used to store values during execution. The memory is kept allocated and reused across runs of functions like `trace`

.

`threadedintervalpool`

is a global threadsafe pool which is accessed through the functions:

```
newinintervalpool!(::Type{T} = Float64, tid::Int = Threads.threadid()) -> Vector{Interval{T}}
indexednewinintervalpool!(::Type{T} = Float64, tid::Int = Threads.threadid()) -> Tuple{Int,Vector{Interval{T}}}
emptyintervalpool!(::Type{T} = Float64, tid::Int = Threads.threadid())
getfromintervalpool([::Type{T} = Float64], id::Int, tid::Int = Threads.threadid()) -> Vector{Interval{T}}
```

### Intersections

`OpticSim.IntervalPoint`

— TypeEach `Interval`

consists of two `IntervalPoint`

s, one of `RayOrigin`

, `Intersection`

or `Infinity`

.

`OpticSim.RayOrigin`

— Type`RayOrigin{T} <: IntervalPoint{T}`

Point representing 0 within an `Interval`

, i.e. the start of the ray.

```
RayOrigin(T = Float64)
RayOrigin{T}()
```

`OpticSim.Infinity`

— Type`Infinity{T} <: IntervalPoint{T}`

Point representing ∞ within an `Interval`

.

```
Infinity(T = Float64)
Infinity{T}()
```

`OpticSim.Intersection`

— Type`Intersection{T,N} <: IntervalPoint{T}`

Represents the point at which an `Ray`

hits a `Surface`

. This consists of the distance along the ray, the intersection point in world space, the normal in world space, the UV on the surface and the `OpticalInterface`

hit.

Has the following accessor methods:

```
point(a::Intersection{T,N}) -> SVector{N,T}
normal(a::Intersection{T,N}) -> SVector{N,T}
uv(a::Intersection{T,N}) -> SVector{2,T}
u(a::Intersection{T,N}) -> T
v(a::Intersection{T,N}) -> T
α(a::Intersection{T,N}) -> T
interface(a::Intersection{T,N}) -> OpticalInterface{T}
flippednormal(a::Intersection{T,N}) -> Bool
```

`OpticSim.isinfinity`

— Function`isinfinity(a) -> Bool`

Returns true if `a`

is `Infinity`

. In performance critical contexts use `a isa Infinity{T}`

.

`OpticSim.israyorigin`

— Function`israyorigin(a) -> Bool`

Returns true if `a`

is `RayOrigin`

. In performance critical contexts use `a isa RayOrigin{T}`

.