@ -9,7 +9,7 @@ import {
SegmentWithIntersection , Triangle
} from "./pslg" ;
export function getFirstIntersectingSegmentInDirection ( raySegment : Segment , boundary : GraphBoundary , graph : Graph , direction : GraphDirection ): [ Segment , Point ] {
export function getFirstIntersectingSegmentInDirection ( raySegment : Segment , boundary : GraphBoundary , graph : Graph , direction : GraphDirection , inclusive = false ): [ Segment , Point ] {
const intersectingSegment = boundary . getBoundary ( direction )
const intersectingPoint = raySegment . getIntersectionWith ( intersectingSegment )
if ( ! intersectingPoint ) {
@ -29,7 +29,7 @@ export function getFirstIntersectingSegmentInDirection(raySegment: Segment, boun
return {
segment ,
intersect : segment .getIntersectionWithin ( raySegment )
intersect : segment [ inclusive ? 'getIntersectionWith' : 'getIntersectionWithin' ] ( raySegment )
}
} )
. filter ( x = > x && x . intersect ) as SegmentWithIntersection [ ] )
@ -45,12 +45,13 @@ export function getFirstIntersectingSegmentInDirection(raySegment: Segment, boun
export function triangulate ( originalGraph : Graph ) : Graph {
const graph = originalGraph . clone ( )
const boundary = addBoundingSquareTo ( graph )
const trapezoidSegments: Segment [ ] = [ ] //graph.segments.filter(segment => segment.isHorizontal())
const boundary = addBoundingSquareTo ( graph )
const leftBound = boundary . getLeftBoundary ( )
const rightBound = boundary . getRightBoundary ( )
const trapezoidSegments : Segment [ ] = [ ]
// trapezoidSegments.push(boundary.getLowerBoundary())
// For each vertex in the original graph, create a horizontal line that
// extends in both directions until it intersects with either (1) the boundary
@ -123,15 +124,21 @@ export function triangulate(originalGraph: Graph): Graph {
}
}
// Any horizontal segments present in the original graph will also be used for form
// trapezoids, so push them onto the list of trapezoid base segments.
originalGraph . segments
. filter ( segment = > segment . isHorizontal ( ) )
. forEach ( segment = > trapezoidSegments . push ( segment ) )
// Now, go through and identify trapezoids for all the horizontal segments we just added
for ( const segment of trapezoidSegments ) {
// First, find the trapezoid formed with the segment as the bottom
// Fi nd the trapezoid formed with the segment as the bottom
// Create a vertical segment from the midpoint of the segment to the top boundary
const horizontalMidpoint = segment . getMidpoint ( )
let upperBoundaryPoint = Point . from ( horizontalMidpoint . x , boundary . ymax )
let upperBoundaryVerticalSegment = new Segment ( horizontalMidpoint , upperBoundaryPoint )
cons t [ upperIntersectSegment , upperIntersectPoint ] = getFirstIntersectingSegmentInDirection (
le t [ upperIntersectSegment , upperIntersectPoint ] = getFirstIntersectingSegmentInDirection (
upperBoundaryVerticalSegment ,
boundary ,
graph ,
@ -151,10 +158,10 @@ export function triangulate(originalGraph: Graph): Graph {
leftBoundaryHorizontalSegment ,
boundary ,
graph ,
GraphDirection . LEFT
GraphDirection . LEFT ,
true
)
console . log ( 'got leftIntersectSegment' , leftBoundaryHorizontalSegment . toQuickDisplay ( ) , leftIntersectSegment . toQuickDisplay ( ) , leftIntersectPoint . coordinate )
leftBoundaryHorizontalSegment = new Segment ( verticalMidpoint , leftIntersectPoint )
// Repeat to get the right boundary
@ -166,10 +173,38 @@ export function triangulate(originalGraph: Graph): Graph {
boundary ,
graph ,
GraphDirection . RIGHT ,
true
)
rightBoundaryHorizontalSegment = new Segment ( verticalMidpoint , rightIntersectPoint )
// Check if the upper boundary segment extends beyond the x-range of the left- and right-boundary segments
// If so, we need to split it to fit within the bounds of the current trapezoid, starting with the right side
if ( upperIntersectSegment . xmax > rightIntersectSegment . xmax ) {
let [ upperIntersectSplit1 , upperIntersectSplit2 ] = upperIntersectSegment . splitAt (
Point . from ( rightIntersectSegment . xmax , upperIntersectSegment . ymax )
)
graph . removeSegment ( upperIntersectSegment )
upperIntersectSplit1 = graph . findExistingSegmentOrAdd ( upperIntersectSplit1 )
upperIntersectSplit2 = graph . findExistingSegmentOrAdd ( upperIntersectSplit2 )
upperIntersectSegment = upperIntersectSplit1 . xmax === rightIntersectSegment . xmax ? upperIntersectSplit1 : upperIntersectSplit2
}
// Repeat for the left side
if ( upperIntersectSegment . xmin < leftIntersectSegment . xmin ) {
let [ upperIntersectSplit1 , upperIntersectSplit2 ] = upperIntersectSegment . splitAt (
Point . from ( leftIntersectSegment . xmin , upperIntersectSegment . ymax )
)
graph . removeSegment ( upperIntersectSegment )
upperIntersectSplit1 = graph . findExistingSegmentOrAdd ( upperIntersectSplit1 )
upperIntersectSplit2 = graph . findExistingSegmentOrAdd ( upperIntersectSplit2 )
upperIntersectSegment = upperIntersectSplit1 . xmin === leftIntersectSegment . xmin ? upperIntersectSplit1 : upperIntersectSplit2
}
// Now, check if we actually have a 4-bound trapezoid, or if we have a triangle
const points = Point . distinct ( [
segment . from ,
@ -178,21 +213,6 @@ export function triangulate(originalGraph: Graph): Graph {
upperIntersectSegment . to ,
] )
if ( points . length === 3 ) {
// We found a triangle! Less work.
// Create the triangle and push it onto the graph
const [ p1 , p2 , p3 ] = points . map ( x = > graph . findExistingPointOrAdd ( x ) )
const s12 = graph . findExistingSegmentOrAdd ( new Segment ( p1 , p2 ) )
const s23 = graph . findExistingSegmentOrAdd ( new Segment ( p2 , p3 ) )
const s31 = graph . findExistingSegmentOrAdd ( new Segment ( p3 , p1 ) )
graph . findExistingTriangleOrAdd ( new Triangle ( [ s12 , s23 , s31 ] ) )
continue // FIXME - remove to handle below-segment case
}
if ( points . length !== 4 ) {
throw new RangeError ( 'Found shape with invalid number of distinct points!' )
}
// Now, we have the 4 bounding segments of the trapezoid.
// Let's find the segments that make up the trapezoid
// We will do this by re-creating segments for the four sides of the trapezoid
@ -205,33 +225,76 @@ export function triangulate(originalGraph: Graph): Graph {
// TODO Account for the case where we don't need to split the segment.
let trapezoidLeftBoundSegment = leftIntersectSegment
let leftSegment1 : Segment | undefined
let leftSegment2 : Segment | undefined
// let leftSegment1: Segment | undefined
// let leftSegment2: Segment | undefined
if ( ! leftIntersectSegment . hasPoint ( leftSegmentIntersectPoint ) ) {
let [ l ocalL eftSegment1, l ocalL eftSegment2] = leftIntersectSegment . splitAt ( leftSegmentIntersectPoint )
let [ l eftSegment1, l eftSegment2] = leftIntersectSegment . splitAt ( leftSegmentIntersectPoint )
graph . removeSegment ( leftIntersectSegment )
leftSegment1 = graph . findExistingSegmentOrAdd ( l ocalL eftSegment1)
leftSegment2 = graph . findExistingSegmentOrAdd ( l ocalL eftSegment2)
leftSegment1 = graph . findExistingSegmentOrAdd ( l eftSegment1)
leftSegment2 = graph . findExistingSegmentOrAdd ( l eftSegment2)
// We care about the upper-segment from the split, as that is the bound of our trapezoid
trapezoidLeftBoundSegment = leftSegment1 . ymin === leftSegmentIntersectPoint . y ? leftSegment1 : leftSegment2
// Now, we need to consider the case where the upper segment we split extends beyond the upper bound of
// the trapezoid we are working with now. If so, split the upper segment again.
if ( trapezoidLeftBoundSegment . ymax > upperIntersectPoint . y && points . length > 3 ) {
// The left bound extends beyond the top of this trapezoid. So, split it.
let localLeftUpperSplitPoint = Point . from ( trapezoidLeftBoundSegment . xmin , upperIntersectPoint . y )
let [ leftUpperSegment1 , leftUpperSegment2 ] = trapezoidLeftBoundSegment . splitAt ( localLeftUpperSplitPoint )
graph . removeSegment ( trapezoidLeftBoundSegment )
leftUpperSegment1 = graph . findExistingSegmentOrAdd ( leftUpperSegment1 )
leftUpperSegment2 = graph . findExistingSegmentOrAdd ( leftUpperSegment2 )
trapezoidLeftBoundSegment = leftUpperSegment1 . ymax === upperIntersectPoint . y ? leftUpperSegment1 : leftUpperSegment2
}
}
graph . findExistingSegmentOrAdd ( trapezoidLeftBoundSegment )
// Repeat this process for the right-side segment
const rightSegmentIntersectPoint = rightIntersectSegment . getIntersectionWith ( segment )
if ( ! rightSegmentIntersectPoint ) throw new Error ( 'Unable to find trapezoid segment intersection' )
let trapezoidRightBoundSegment = rightIntersectSegment
let rightSegment1 : Segment | undefined
let rightSegment2 : Segment | undefined
if ( ! rightIntersectSegment . hasPoint ( rightSegmentIntersectPoint ) ) {
let [ localRightSegment1 , localRightSegment2 ] = rightIntersectSegment . splitAt ( rightSegmentIntersectPoint )
let [ rightSegment1 , rightSegment2 ] = rightIntersectSegment . splitAt ( rightSegmentIntersectPoint )
graph . removeSegment ( rightIntersectSegment )
rightSegment1 = graph . findExistingSegmentOrAdd ( localRightSegment1 )
rightSegment2 = graph . findExistingSegmentOrAdd ( localR ightSegment2)
rightSegment1 = graph . findExistingSegmentOrAdd ( r ightSegment1)
rightSegment2 = graph . findExistingSegmentOrAdd ( r ightSegment2)
// We care about the upper-segment from the split, as that is the bound of our trapezoid
trapezoidRightBoundSegment = rightSegment1 . ymin === rightSegmentIntersectPoint . y ? rightSegment1 : rightSegment2
// Now, we need to consider the case where the upper segment we split extends beyond the upper bound of
// the trapezoid we are working with now. If so, split the upper segment again.
if ( trapezoidRightBoundSegment . ymax > upperIntersectPoint . y && points . length > 3 ) {
// The left bound extends beyond the top of this trapezoid. So, split it.
let localRightUpperSplitPoint = Point . from ( trapezoidRightBoundSegment . xmin , upperIntersectPoint . y )
let [ rightUpperSegment1 , rightUpperSegment2 ] = trapezoidRightBoundSegment . splitAt ( localRightUpperSplitPoint )
graph . removeSegment ( trapezoidRightBoundSegment )
rightUpperSegment1 = graph . findExistingSegmentOrAdd ( rightUpperSegment1 )
rightUpperSegment2 = graph . findExistingSegmentOrAdd ( rightUpperSegment2 )
trapezoidRightBoundSegment = rightUpperSegment1 . ymax === upperIntersectPoint . y ? rightUpperSegment1 : rightUpperSegment2
}
}
// break;
if ( points . length === 3 ) {
// We found a triangle! Less work.
// Create the triangle and push it onto the graph
// const [p1, p2, p3] = points.map(x => graph.findExistingPointOrAdd(x))
// const s12 = graph.findExistingSegmentOrAdd(new Segment(p1, p2))
// const s23 = graph.findExistingSegmentOrAdd(new Segment(p2, p3))
// const s31 = graph.findExistingSegmentOrAdd(new Segment(p3, p1))
graph . findExistingTriangleOrAdd ( new Triangle ( [ trapezoidLeftBoundSegment , trapezoidRightBoundSegment , segment ] ) )
continue // FIXME - remove to handle below-segment case
}
if ( points . length !== 4 ) {
throw new RangeError ( 'Found shape with invalid number of distinct points!' )
}
// Now we have all 4 bounding segments. We find the bisector that creates
@ -243,29 +306,27 @@ export function triangulate(originalGraph: Graph): Graph {
const bottomLeftBisectorSegment = new Segment ( lowerLeftPoint , upperRightPoint )
const bottomLeftBisectorUpperTriangle = new Triangle ( [ bottomLeftBisectorSegment , upperIntersectSegment , trapezoidLeftBoundSegment ] )
// const bottomLeftBisectorLowerTriangle = new Triangle([bottomLeftBisectorSegment, segment, trapezoidRightBoundSegment])
// const bottomLeftBisectorMinAngle = Math.min(bottomLeftBisectorUpperTriangle.getMinimumAngle(), bottomLeftBisectorLowerTriangle.getMinimumAngle())
// const upperLeftPoint = graph.findExistingPointOrAdd(Point.from(upperIntersectSegment.xmin, upperIntersectSegment.ymax))
// const lowerRightPoint = graph.findExistingPointOrAdd(Point.from(segment.xmax, segment.ymin))
//
// const topRightBisectorSegment = new Segment(upperLeftPoint, lowerRightPoint)
// const upperRightBisectorUpperTriangle = new Triangle([topRightBisectorSegment, upperIntersectSegment, trapezoidRightBoundSegment])
// const upperRightBisectorLowerTriangle = new Triangle([topRightBisectorSegment, trapezoidLeftBoundSegment, segment])
// const upperRightBisectorMinAngle = Math.min(upperRightBisectorUpperTriangle.getMinimumAngle(), upperRightBisectorLowerTriangle.getMinimumAngle())
//
// const optimalBisectorUpperTriangle = upperRightBisectorMinAngle > bottomLeftBisectorMinAngle ? upperRightBisectorUpperTriangle : bottomLeftBisectorUpperTriangle
// const optimalBisectorLowerTriangle = upperRightBisectorMinAngle > bottomLeftBisectorMinAngle ? upperRightBisectorLowerTriangle : bottomLeftBisectorLowerTriangle
//
// // Add the triangles to the graph
// const upperTriangleSegments = optimalBisectorUpperTriangle.sides.map(side => graph.findExistingSegmentOrAdd(side))
// graph.findExistingTriangleOrAdd(new Triangle(upperTriangleSegments as [Segment, Segment, Segment]))
//
// const lowerTriangleSegments = optimalBisectorLowerTriangle.sides.map(side => graph.findExistingSegmentOrAdd(side))
// graph.findExistingTriangleOrAdd(new Triangle(lowerTriangleSegments as [Segment, Segment, Segment]))
}
const bottomLeftBisectorLowerTriangle = new Triangle ( [ bottomLeftBisectorSegment , segment , trapezoidRightBoundSegment ] )
const bottomLeftBisectorMinAngle = Math . min ( bottomLeftBisectorUpperTriangle . getMinimumAngle ( ) , bottomLeftBisectorLowerTriangle . getMinimumAngle ( ) )
// FIXME handle the lower-trapezoid case
const upperLeftPoint = graph . findExistingPointOrAdd ( Point . from ( upperIntersectSegment . xmin , upperIntersectSegment . ymax ) )
const lowerRightPoint = graph . findExistingPointOrAdd ( Point . from ( segment . xmax , segment . ymin ) )
const topRightBisectorSegment = new Segment ( upperLeftPoint , lowerRightPoint )
const upperRightBisectorUpperTriangle = new Triangle ( [ topRightBisectorSegment , upperIntersectSegment , trapezoidRightBoundSegment ] )
const upperRightBisectorLowerTriangle = new Triangle ( [ topRightBisectorSegment , trapezoidLeftBoundSegment , segment ] )
const upperRightBisectorMinAngle = Math . min ( upperRightBisectorUpperTriangle . getMinimumAngle ( ) , upperRightBisectorLowerTriangle . getMinimumAngle ( ) )
const optimalBisectorUpperTriangle = upperRightBisectorMinAngle > bottomLeftBisectorMinAngle ? upperRightBisectorUpperTriangle : bottomLeftBisectorUpperTriangle
const optimalBisectorLowerTriangle = upperRightBisectorMinAngle > bottomLeftBisectorMinAngle ? upperRightBisectorLowerTriangle : bottomLeftBisectorLowerTriangle
// Add the triangles to the graph
const upperTriangleSegments = optimalBisectorUpperTriangle . sides . map ( side = > graph . findExistingSegmentOrAdd ( side ) )
graph . findExistingTriangleOrAdd ( new Triangle ( upperTriangleSegments as [ Segment , Segment , Segment ] ) )
const lowerTriangleSegments = optimalBisectorLowerTriangle . sides . map ( side = > graph . findExistingSegmentOrAdd ( side ) )
graph . findExistingTriangleOrAdd ( new Triangle ( lowerTriangleSegments as [ Segment , Segment , Segment ] ) )
}
return graph
}