> module BSPT
Module that defines Binary Space Partitioning Trees and basic
operations thereon.
> ( BSPT(..),Status(..),Point,Region,Line,
> Face,Faces,buildBSPT,bsp',bsp'',mkCell, partFaces,
> scanLine,countLeaves,classifyPoint,area,
> foldBSPT)
> where
> import EuclidGMS ( Location(..),Partition,Region,
> mkPart,getPart,newRegion,location,renderBorder,
> bisect,toBack,findVertices,flip_YORK,
> Point(..),Line,Face(..),Faces,Halfspace(..),
> space,convert,triangleArea,Segment )
> import GeomNum
> import Stdlib (mapcat,const3)
> import Libfuns
A binary space partition tree is either:
A leaf node tagged Cell with three arguments--
1. Status (as defined below)
describes the state of the convex cell
2. Region indicates the area described by the
cell
3. Gives the Area of the cell (used as a memoing
argument).
A branch node tagged BSP with the following arguments
1. a Line equation description of the partitioning
of space at this node.
2. a triple that stores the faces that lie in the plane
described by 1. an indication of the area
covered the tree whose root is this node and
the section of the partition that runs through the
region.
3/4. BSP trees representing the Rear and Fore
halfspaces formed by the partition at this node.
> data BSPT = Cell Status Region Numb |
> BSP Partition (Faces,Region) BSPT BSPT -- deriving (Text)
Status is an enumeration of classifications of points with
respect to objects.
> data Status = In | Out | On deriving (Eq,Show{-was:Text-})
A partitioning is a triple of Faces representing the faces
(w.r.t. a partition) in the rear halfspace, coincident to, and
in the fore halfspace respectively.
> type Partitioning = (Faces,Faces,Faces)
buildBSPT : Creates a BSPT for a polygon defined by a B-rep
(i.e. a list of faces). Building is done by
auxiliary buildBSPTAux.
> buildBSPT :: Faces -> BSPT
> buildBSPT = buildBSPTAux Out renderBorder
buildBSPTAux: Builds a BSPT from the first argument list of
faces. The second argument carries the current
region in consideration and is used for
augmenting created nodes and cells. The
third argument indicates the default value
of the branch and is In when building the
left subtree and Out when building the right.
The function heuristic selects a single face according
to some determined rule to form the partition at the node.
convert takes the Face and creates the equation for the
line that embeds that face.
partFaces creates a partitioning of the remaining
polygon face w.r.t. the partition chosen.
> buildBSPTAux :: Status -> Region -> Faces -> BSPT
> buildBSPTAux status region [] = mkCell status region
> buildBSPTAux _ region faces = par right (seq left (BSP partition (coin,region) left right))
> where
> left = buildBSPTAux In (newRegion region partition) rear
> right = buildBSPTAux Out (newRegion region (flip_YORK partition)) fore
> (rear,coin,fore) = partFaces part faces
> partition = mkPart region part
> part = heuristic faces
bsp' : Used to construct a BSPT when there is a possibility that
both children are cells with the same status.
When this occurs the cells are condensed.
> bsp' :: Partition -> (Faces,Region) -> BSPT -> BSPT -> BSPT
> bsp' part (faces,region) (Cell x _ a) (Cell y _ b) | x==y = Cell x region (a+b)
> bsp' part nodeInfo left right = BSP part nodeInfo left right
bsp'' : Used to construct a BSPT when there is a possibility of
condensing as above and, additionally, there may
be a need to update the faces that lie in the
sub-hyperplane formed by the partition.
bsp'' also checks that a partition is needed.
A partition is needed if there are faces stored
in its hyperplane. bsp'' simplifies the tree when
such a partition occurs, removing the redundent partition.
> bsp'' :: Partition -> (Faces, Region) -> BSPT -> BSPT -> BSPT
> bsp'' part (faces,region) left right
> = if newfaces==[]
> then simplify part region left right
> else BSP part (newfaces,region) left right
> where
> newfaces = updateFaces left right faces
simplify : implements the removal of a redundant partition.
Note need for region to be passed in. So that
replacement node represents the correct area.
> simplify :: Partition -> Region -> BSPT -> BSPT -> BSPT
> simplify _ region (Cell _ _ _) (BSP part (faces,_) left right)
> = BSP (mkPart region (getPart part)) (faces,region) left right
> simplify _ region (BSP part (faces,_) left right) (Cell _ _ _)
> = BSP (mkPart region (getPart part)) (faces,region) left right
> simplify part region tree1 tree2 = bsp' part ([],region) tree1 tree2
mkCell: construction function defined for simplification.
Area calculation done by default method.
> mkCell :: Status -> Region -> BSPT
> mkCell status region = Cell status region (areaRegion region)
partFaces : Splits a list of faces into three groups.
Those lying in the rear halfspace defined by part.
Those lying in the sub-hyperplane defined by part.
Those lying in the fore halfspace defined by part.
The function location determines the which case
applies for each face in the list.
Note that when the face intersects the partition,
that face is bisect(ed) and the two resulting faces
added to the appropriate group.
> partFaces :: Line -> Faces -> (Faces,Faces,Faces)
> partFaces part [] = ([],[],[])
> partFaces part (face@(Fc section _):faces)
> = par rest
> (case (location part section) of
> Coincident -> (rear,face:coin,fore)
> Intersects -> (rearHalf:rear,coin,foreHalf:fore)
> ToTheRear -> (face:rear,coin,fore)
> ToTheFore -> (rear,coin,face:fore))
> where
> (rear,coin,fore) = rest
> rest = partFaces part faces
> (rearHalf,foreHalf) = bisect face part
heuristic : decides according to some rule which faces to pick
to form the current partition. Returns this face and the rest
as a pair.
Currently is selects the first.
When going parallel it will need to split the faces
into two equal(ish) sized groups.
> heuristic :: Faces -> Line
> heuristic (Fc _ l:_) = l
updateFaces : Takes a list of faces and produces a new list
where the new list is the old list with segments
of lines removed that no longer live on the edge
of the object. It does this by first classfiying
each edge with respect to the left subtree.
These classification are passed down into the right
subtree which as it classifies the face can decide
whether it lies on the edge or not. This is done
on the basis that to lie on the edge a faces
classification most differ for each subtree.
> updateFaces :: BSPT -> BSPT -> Faces -> Faces
> updateFaces left right = mapcat (rubout right).classifyFace left
classifyFace : produces a list of face-classification pairs
formed by taking the segments of each face
with respect a BSP tree and tagging appropriate
at the Cell nodes.
> classifyFace :: BSPT -> Faces -> [(Face,Status)]
> classifyFace tree = mapcat (segments tagStatus tree)
> where
> tagStatus x face = [(face,x)]
rubout : Effectively classfies to the right and filters away faces
that have the same classification as they did to the
right.
> rubout :: BSPT -> (Face,Status) -> Faces
> rubout tree (face,x) = segments (erase x) tree face
> where erase x y face | x==y = []
> | otherwise = [face]
segments : Higher order generalised function.
First argument is a cell operation.
The face defined in the third argument
is cut into segments by a decent of the BSP tree
defined in the second argument. A segment of a face
reaching a Cell node has the cell operation performed
on it.
For example to segment a face into part lying in the different
concave cells defined by the BSP tree. Use
segments ignoreStatus
where ignoreStatus a b = b
> segments :: (Status->Face->[a]) -> BSPT -> Face -> [a]
> segments cellop (Cell status _ _) face = cellop status face
> segments cellop (BSP part@(Fc _ p) _ left right) face@(Fc fs _)
> = case (location p fs) of
> Coincident -> cellop In face
> Intersects -> segments cellop left leftside ++
> segments cellop right rightside
> ToTheRear -> segments cellop left face
> ToTheFore -> segments cellop right face
> where
> (leftside,rightside) = bisect face p
scanLine : Uses segments to filter away parts of the face given
that are not in the object.
filterInside ignores faces in Out cells.
> scanLine :: BSPT -> Face -> Faces
> scanLine = segments filterInside
> where
> filterInside In face = [face]
> filterInside Out _ = []
foldBSPT : folds up a BSPT applying nodeop to the partition, node
data and children at nodes and applying cellop to the
leafs.
> foldBSPT :: (Status->Region->Numb->a)->(Partition->(Faces,Region)->a->a->a)->BSPT->a
> foldBSPT cellop nodeop (Cell x r a) = cellop x r a
> foldBSPT cellop nodeop (BSP part nodeinfo left right)
> = nodeop part nodeinfo left' right'
> where
> left' = f left
> right' = f right
> f = foldBSPT cellop nodeop
countLeaves : Uses foldBSPT to fold up the tree into a count
of the number of Leaves. cellop counts one for
each leaf. Nodeop is simply addition, but we first
arrange to drop the partition arguments.
> countLeaves :: BSPT -> Int
> countLeaves = foldBSPT (const3 1) plus
> where
> plus _ _ = (+)
area: determines the area of a BSPT represented object
does this by folding plus over the tree with In cells
counting their value Out cells counting zero.
> area :: BSPT -> Numb
> area = foldBSPT sumInRegions plus
> where
> sumInRegions In _ a = a
> sumInRegions _ _ _ = 0
> plus _ _ = (+)
areaRegion - calculates the area of an convex region.
> areaRegion :: Region -> Numb
> areaRegion = sum.map triangleArea.triangles.findVertices
triangles - splits a convex region into triangles
> triangles :: [Point] -> [[Point]]
> triangles [p1,p2] = []
> triangles [p1,p2,p3] = [[p1,p2,p3]]
> triangles (p1:p2:ps) = if left/=[] && right /=[] then
> triangles (p1:p2:left) ++ triangles (p1:p2:right)
> else triangles (p1:ps++[p2])
> where
> (left,right) = partPoints (convert p1 p2) ps
partPoints - partition a list of points w.r.t. a line
> partPoints :: Line -> [Point] -> ([Point],[Point])
> partPoints eqn [] = ([],[])
> partPoints eqn (p:pts) = if toBack p eqn
> then (p:left,right)
> else (left,p:right)
> where
> (left,right) = partPoints eqn pts
classifyPoint - point classification w.r.t. object
> classifyPoint :: Point -> BSPT -> Status
> classifyPoint pt = foldBSPT status (deter pt)
> where
> status s _ _ = s
> deter pt (Fc _ part) _ = deter' (space part pt)
> where
> deter' Fore _ x = x
> deter' Rear x _ = x
> deter' Coin x y | x==y = x -- was: At (no such thing)
> | otherwise = On
|