Skip to content

ClippableFace

Under Construction

This documentation is auto-generated, and is a work in progress. Please see the source code at https://github.com/phetsims/alpenglow/blob/main/js/cag/ClippableFace.ts for the most up-to-date information.

Overview

An interface for clippable/subdivide-able faces, with defined bounds and area.

NOTE: "fake" corners are needed for some clipping operations (notably the binary line clipping operations, without bounds). These are corners that are not actually part of the face, but are used when we don't have access to the entire polygon, and need to output edges which will match up with other results. In these cases, we might generate edges that go OUTSIDE of the resulting bounds, so if we need to access bounds of the ClippableFace, we'll need to ignore these "fake" corners.

@author Jonathan Olson <jonathan.olson@colorado.edu>

Type BinaryClipCallback

import type { BinaryClipCallback } from 'scenerystack/alpenglow';

( isMinFace: boolean, startX: number, startY: number, endX: number, endY: number, startPoint: Vector2 | null, endPoint: Vector2 | null ) => void

Class BinaryClipping

Clipping arbitrary (degenerate, non-convex, self-intersecting, etc.) polygons based on binary criteria (e.g. left/right, inside/outside).

@author Jonathan Olson <jonathan.olson@colorado.edu>

import { BinaryClipping } from 'scenerystack/alpenglow';

Static Methods

binaryXClipEdge( startPoint : Vector2, endPoint : Vector2, x : number, fakeCornerY : number, minLinearEdges : LinearEdge[], maxLinearEdges : LinearEdge[] )

binaryYClipEdge( startPoint : Vector2, endPoint : Vector2, y : number, fakeCornerX : number, minLinearEdges : LinearEdge[], maxLinearEdges : LinearEdge[] )

binaryLineClipEdge( startPoint : Vector2, endPoint : Vector2, normal : Vector2, value : number, fakeCornerPerpendicular : number, minLinearEdges : LinearEdge[], maxLinearEdges : LinearEdge[] )

line where dot( normal, point ) - value = 0. "min" side is dot-products < value, "max" side is dot-products > value

binaryXClipPolygon( polygon : Vector2[], x : number, minPolygon : Vector2[], maxPolygon : Vector2[] )

binaryYClipPolygon( polygon : Vector2[], y : number, minPolygon : Vector2[], maxPolygon : Vector2[] )

binaryLineClipPolygon( polygon : Vector2[], normal : Vector2, value : number, minPolygon : Vector2[], maxPolygon : Vector2[] )

line where dot( normal, point ) - value = 0. "min" side is dot-products < value, "max" side is dot-products > value

binaryXClipEdgedClipped( face : EdgedClippedFace, x : number ) : { minFace: EdgedClippedFace; maxFace: EdgedClippedFace }

binaryYClipEdgedClipped( face : EdgedClippedFace, y : number ) : { minFace: EdgedClippedFace; maxFace: EdgedClippedFace }

Type BinaryPolygonCompleteCallback

import type { BinaryPolygonCompleteCallback } from 'scenerystack/alpenglow';

( isMinFace: boolean ) => void

Class CircularClipping

Clipping arbitrary (degenerate, non-convex, self-intersecting, etc.) polygons to the inside/outside of a circle.

@author Jonathan Olson <jonathan.olson@colorado.edu>

import { CircularClipping } from 'scenerystack/alpenglow';

Static Methods

binaryCircularClipEdges( edges : LinearEdge[], center : Vector2, radius : number, maxAngleSplit : number, inside : LinearEdge[], outside : LinearEdge[] )

Clips a polygon (represented by unsorted LinearEdges) by a circle. This will output both the inside and outside, appending LinearEdges to the given arrays.

@param edges - the edges of the polygon to clip @param center - the center of the circle @param radius - the radius of the circle @param maxAngleSplit - the maximum angle of a circular arc that will be converted into a linear edge @param inside - (OUTPUT) the edges that are inside the circle (will be appended to) @param outside - (OUTPUT) the edges that are outside the circle (will be appended to)

binaryCircularClipPolygon( polygons : Vector2[][], center : Vector2, radius : number, maxAngleSplit : number, inside : Vector2[][], outside : Vector2[][] )

Clips a polygon (represented by polygonal vertex lists) by a circle. This will output both the inside and outside, appending vertices to the arrays

@param polygons @param center - the center of the circle @param radius - the radius of the circle @param maxAngleSplit - the maximum angle of a circular arc that will be converted into a linear edge @param inside - (OUTPUT) the polygon that is inside the circle (will be appended to) @param outside - (OUTPUT) the polygon that is outside the circle (will be appended to)

binaryCircularTracingClipIterate( polygons : Vector2[][], center : Vector2, radius : number, maxAngleSplit : number, callback : BinaryClipCallback, polygonCompleteCallback : BinaryPolygonCompleteCallback )

Clips a polygon (represented by polygonal vertex lists) by a circle. This will output both the inside and outside, appending vertices to the arrays.

maxAngleSplit is the maximum angle of a circular arc that will be converted into a linear edge.

TODO: test this!

Type ClippableFace

TODO: assertions that all types of ClippableFace give the same results for the same methods

import type { ClippableFace } from 'scenerystack/alpenglow';

Type ClippableFaceAccumulator

This is a type meant for building a ClippableFace (of a specific type) by adding edges, and (optionally) marking where we have finished one polygon, and are now going to add edges for another polygon.

When you are done adding edges, use finalizeFace() to get the resulting ClippableFace. If there is no data that gives a non-zero area face, it will return null. This will also reset the internal state, so it can be used to create a fresh new face.

import type { ClippableFaceAccumulator } from 'scenerystack/alpenglow';
  • usesEndPoint: boolean
    A performance marker, such that if this is false, the user can provide arbitrary data to endX/endY/endPoint and it won't matter. This is primarily for polygonal data, where we don't want to require computing the end-data since it will only use the start point of each edge.

Class EdgedClippedFace

A ClippableFace based on a set of line segment edges, where (a) it is contained within a bounding box, and (b) line segments going along the full border of the bounding box (from one corner to another) will be counted separately. This helps with performance, since EdgedFace on its own would build up large counts of these edges that "undo" each other during recursive clipping operations.

Should still represent multiple closed loops, but it is not explicit.

"implicit" edges/vertices are those defined by the clipped counts (minXCount, etc.) "explicit" edges/vertices are those in the edges list

@author Jonathan Olson <jonathan.olson@colorado.edu>

import { EdgedClippedFace } from 'scenerystack/alpenglow';

Constructor

new EdgedClippedFace( edges : LinearEdge[], minX : number, minY : number, maxX : number, maxY : number, minXCount : number, minYCount : number, maxXCount : number, maxYCount : number )

Instance Methods

toPolygonalFace( epsilon ) : PolygonalFace

Converts the face to a polygonal face. Epsilon is used to determine whether start/end points match.

NOTE: This is likely a low-performance method, and should only be used for debugging.

toEdgedFace() : EdgedFace

Converts the face to an edged face.

toEdgedClippedFace( minX : number, minY : number, maxX : number, maxY : number ) : EdgedClippedFace

Converts the face to a edged-clipped face (scanning the edges, to convert bounds-side edges to counts)

toEdgedClippedFaceWithoutCheck( minX : number, minY : number, maxX : number, maxY : number ) : EdgedClippedFace

Converts the face to a edged-clipped face (without scanning the edges to see if they are bounds-side)

forEachImplicitEdge( callback : ( startPoint: Vector2, endPoint: Vector2 ) => void )

getImplicitEdges() : LinearEdge[]

getAllEdges() : LinearEdge[]

getShape( epsilon ) : Shape

Returns a Shape for the face.

NOTE: This is likely a low-performance method, and should only be used for debugging.

getMinXMinY() : Vector2

getMinXMaxY() : Vector2

getMaxXMinY() : Vector2

getMaxXMaxY() : Vector2

hasMinXMinY() : boolean

Returns whether this face has an implicit vertex at the minX-minY corner.

hasMinXMaxY() : boolean

Returns whether this face has an implicit vertex at the minX-maxY corner.

hasMaxXMinY() : boolean

Returns whether this face has an implicit vertex at the maxX-minY corner.

hasMaxXMaxY() : boolean

Returns whether this face has an implicit vertex at the maxX-maxY corner.

hasMinX() : boolean

Returns whether this face has an implicit vertex with minX.

hasMinY() : boolean

Returns whether this face has an implicit vertex with minY.

hasMaxX() : boolean

Returns whether this face has an implicit vertex with maxX.

hasMaxY() : boolean

Returns whether this face has an implicit vertex with maxY.

getBounds() : Bounds2

Returns the bounds of the face (ignoring any "fake" edges, if the type supports them)

getDotRange( normal : Vector2 ) : Range

Returns the range of values for the dot product of the given normal with any point contained within the face (for polygons, this is the same as the range of values for the dot product of the normal with any vertex).

getDistanceRangeToEdges( point : Vector2 ) : Range

Returns the range of distances from the given point to every point along the edges of the face. For instance, if the face was the unit cube, the range would be ½ to sqrt(2), for distances to the middles of the edges and the corners respectively.

getDistanceRangeToInside( point : Vector2 ) : Range

Returns the range of distances from the given point to every point inside the face. The upper bound should be the same as getDistanceRangeToEdges, however the lower bound may be 0 if the point is inside the face.

getArea() : number

Returns the signed area of the face (positive if the vertices are in counter-clockwise order, negative if clockwise)

getCentroidPartial() : Vector2

Returns the partial for the centroid computation. These should be summed up, divided by 6, and divided by the area to give the full centroid

getCentroid( area : number ) : Vector2

Returns the centroid of the face (area is required for the typical integral required to evaluate)

getZero() : number

Returns the evaluation of an integral that will be zero if the boundaries of the face are correctly closed. It is designed so that if there is a "gap" and we have open boundaries, the result will likely be non-zero.

NOTE: This is only used for debugging, so performance is not a concern.

getAverageDistance( point : Vector2, area : number ) : number

Returns the average distance from the given point to every point inside the face. The integral evaluation requires the area (similarly to the centroid computation).

getAverageDistanceTransformedToOrigin( transform : Matrix3, area : number ) : number

Returns the average distance from the origin to every point inside the face transformed by the given matrix.

getClipped( minX : number, minY : number, maxX : number, maxY : number ) : EdgedClippedFace

Returns a copy of the face that is clipped to be within the given axis-aligned bounding box.

getBinaryXClip( x : number, fakeCornerY : number ) : { minFace: EdgedClippedFace; maxFace: EdgedClippedFace }

Returns two copies of the face, one that is clipped to be to the left of the given x value, and one that is clipped to be to the right of the given x value.

The fakeCornerY is used to determine the "fake" corner that is used for unsorted-edge clipping.

getBinaryYClip( y : number, fakeCornerX : number ) : { minFace: EdgedClippedFace; maxFace: EdgedClippedFace }

Returns two copies of the face, one that is clipped to y values less than the given y value, and one that is clipped to values greater than the given y value.

The fakeCornerX is used to determine the "fake" corner that is used for unsorted-edge clipping.

getBinaryLineClip( normal : Vector2, value : number, fakeCornerPerpendicular : number ) : { minFace: EdgedClippedFace; maxFace: EdgedClippedFace }

Returns two copies of the face, one that is clipped to contain points where dot( normal, point ) < value, and one that is clipped to contain points where dot( normal, point ) > value.

The fake corner perpendicular is used to determine the "fake" corner that is used for unsorted-edge clipping

getStripeLineClip( normal : Vector2, values : number[], fakeCornerPerpendicular : number ) : EdgedClippedFace[]

Returns an array of faces, clipped similarly to getBinaryLineClip, but with more than one (parallel) split line at a time. The first face will be the one with dot( normal, point ) < values[0], the second one with values[ 0 ] < dot( normal, point ) < values[1], etc.

getBinaryCircularClip( center : Vector2, radius : number, maxAngleSplit : number ) : { insideFace: EdgedClippedFace; outsideFace: EdgedClippedFace }

Returns two copies of the face, one that is clipped to contain points inside the circle defined by the given center and radius, and one that is clipped to contain points outside the circle.

NOTE: maxAngleSplit is used to determine the polygonal approximation of the circle. The returned result will not have a chord with an angle greater than maxAngleSplit.

gridClipIterate( minX : number, minY : number, maxX : number, maxY : number, stepX : number, stepY : number, stepWidth : number, stepHeight : number, callback : GridClipCallback, polygonCompleteCallback : PolygonCompleteCallback )

Given an integral bounding box and step sizes (which define the grid), this will clip the face to each cell in the grid, calling the callback for each cell's contributing edges (in order, if we are a PolygonalFace). polygonCompleteCallback will be called whenever a polygon is completed (if we are a polygonal type of face).

getBilinearFiltered( pointX : number, pointY : number, minX : number, minY : number ) : number

Returns the evaluation of the bilinear (tent) filter integrals for the given point, ASSUMING that the face is clipped to the transformed unit square of x: [minX,minX+1], y: [minY,minY+1].

getMitchellNetravaliFiltered( pointX : number, pointY : number, minX : number, minY : number ) : number

Returns the evaluation of the Mitchell-Netravali (⅓,⅓) filter integrals for the given point, ASSUMING that the face is clipped to the transformed unit square of x: [minX,minX+1], y: [minY,minY+1].

containsPoint( point : Vector2 ) : boolean

Returns whether the face contains the given point.

getTransformed( transform : Matrix3 ) : EdgedClippedFace

Returns an affine-transformed version of the face.

getRounded( epsilon : number ) : EdgedClippedFace

Returns a rounded version of the face, where [-epsilon/2, epsilon/2] rounds to 0, etc.

withReversedEdges() : EdgedClippedFace

Returns a version of the face with the orientation of all of the edges swapped.

forEachEdge( callback : ( startPoint: Vector2, endPoint: Vector2 ) => void )

Calls the callback with points for each edge in the face.

getScratchAccumulator() : ClippableFaceAccumulator<EdgedClippedFace>

Returns a singleton accumulator for this type of face. This will always return the same instance, and should ONLY be used if there will be no reentrant or co-occurring usage of this accumulator (i.e. only use it when you can guarantee nothing else will be clipped at the same time). If two tasks try to use this at the same time, it will likely break.

This is a method that can be called on an unknown-type face, to reproduce the same type of face. This is important, since we can't feed unsorted edge data directly to a PolygonalFace's accumulator, and in general this is the safest way to get an accumulator for a face.

getAccumulator() : ClippableFaceAccumulator<EdgedClippedFace>

Returns a new accumulator for this type of face. This should be used when concurrent clipping will need to happen.

This is a method that can be called on an unknown-type face, to reproduce the same type of face. This is important, since we can't feed unsorted edge data directly to a PolygonalFace's accumulator, and in general this is the safest way to get an accumulator for a face.

toString() : string

Returns a debugging string.

serialize() : SerializedEdgedClippedFace

Returns a serialized version of the face, that should be able to be deserialized into the same type of face. See {FaceType}.deserialize.

NOTE: If you don't know what type of face this is, use serializeClippableFace instead.

Instance Properties

clippedEdgedFace : EdgedFace

(readonly)

Static Methods

fromEdges( edges : LinearEdge[], minX : number, minY : number, maxX : number, maxY : number ) : EdgedClippedFace

fromEdgesWithoutCheck( edges : LinearEdge[], minX : number, minY : number, maxX : number, maxY : number ) : EdgedClippedFace

getScratchAccumulator() : ClippableFaceAccumulator<EdgedClippedFace>

Returns a singleton accumulator for this type of face. This will always return the same instance, and should ONLY be used if there will be no reentrant or co-occurring usage of this accumulator (i.e. only use it when you can guarantee nothing else will be clipped at the same time). If two tasks try to use this at the same time, it will likely break.

This should be used directly when you know you want an EdgedClippedFace as output.

deserialize( serialized : SerializedEdgedClippedFace ) : EdgedClippedFace

fromBounds( bounds : Bounds2 ) : EdgedClippedFace

fromBoundsValues( minX : number, minY : number, maxX : number, maxY : number ) : EdgedClippedFace

Class EdgedClippedFaceAccumulator

import { EdgedClippedFaceAccumulator } from 'scenerystack/alpenglow';

Instance Methods

addEdge( startX : number, startY : number, endX : number, endY : number, startPoint : Vector2 | null, endPoint : Vector2 | null )

markNewPolygon()

setAccumulationBounds( minX : number, minY : number, maxX : number, maxY : number )

finalizeFace() : EdgedClippedFace | null

Will reset it to the initial state also

finalizeEnsureFace( minX : number, minY : number, maxX : number, maxY : number ) : EdgedClippedFace

reset()

Will reset without creating a face

Instance Properties

usesEndPoint

(readonly)

Class EdgedFace

A ClippableFace based on a set of line segment edges. Should still represent multiple closed loops, but it is not explicit.

@author Jonathan Olson <jonathan.olson@colorado.edu>

import { EdgedFace } from 'scenerystack/alpenglow';

Constructor

new EdgedFace( edges : LinearEdge[], skipValidation )

Instance Methods

toPolygonalFace( epsilon ) : PolygonalFace

Converts the face to a polygonal face. Epsilon is used to determine whether start/end points match.

NOTE: This is likely a low-performance method, and should only be used for debugging.

toEdgedFace() : EdgedFace

Converts the face to an edged face.

toEdgedClippedFace( minX : number, minY : number, maxX : number, maxY : number ) : EdgedClippedFace

Converts the face to a edged-clipped face (scanning the edges, to convert bounds-side edges to counts)

toEdgedClippedFaceWithoutCheck( minX : number, minY : number, maxX : number, maxY : number ) : EdgedClippedFace

Converts the face to a edged-clipped face (without scanning the edges to see if they are bounds-side)

getShape( epsilon ) : Shape

Returns a Shape for the face.

NOTE: This is likely a low-performance method, and should only be used for debugging.

getBounds() : Bounds2

Returns the bounds of the face (ignoring any "fake" edges, if the type supports them)

getDotRange( normal : Vector2 ) : Range

Returns the range of values for the dot product of the given normal with any point contained within the face (for polygons, this is the same as the range of values for the dot product of the normal with any vertex).

getDistanceRangeToEdges( point : Vector2 ) : Range

Returns the range of distances from the given point to every point along the edges of the face. For instance, if the face was the unit cube, the range would be ½ to sqrt(2), for distances to the middles of the edges and the corners respectively.

getDistanceRangeToInside( point : Vector2 ) : Range

Returns the range of distances from the given point to every point inside the face. The upper bound should be the same as getDistanceRangeToEdges, however the lower bound may be 0 if the point is inside the face.

getArea() : number

Returns the signed area of the face (positive if the vertices are in counter-clockwise order, negative if clockwise)

getCentroidPartial() : Vector2

Returns the partial for the centroid computation. These should be summed up, divided by 6, and divided by the area to give the full centroid

getCentroid( area : number ) : Vector2

Returns the centroid of the face (area is required for the typical integral required to evaluate)

getZero() : number

Returns the evaluation of an integral that will be zero if the boundaries of the face are correctly closed. It is designed so that if there is a "gap" and we have open boundaries, the result will likely be non-zero.

NOTE: This is only used for debugging, so performance is not a concern.

getAverageDistance( point : Vector2, area : number ) : number

Returns the average distance from the given point to every point inside the face. The integral evaluation requires the area (similarly to the centroid computation).

getAverageDistanceTransformedToOrigin( transform : Matrix3, area : number ) : number

Returns the average distance from the origin to every point inside the face transformed by the given matrix.

getClipped( minX : number, minY : number, maxX : number, maxY : number ) : EdgedFace

Returns a copy of the face that is clipped to be within the given axis-aligned bounding box.

getBinaryXClip( x : number, fakeCornerY : number ) : { minFace: EdgedFace; maxFace: EdgedFace }

Returns two copies of the face, one that is clipped to be to the left of the given x value, and one that is clipped to be to the right of the given x value.

The fakeCornerY is used to determine the "fake" corner that is used for unsorted-edge clipping.

getBinaryYClip( y : number, fakeCornerX : number ) : { minFace: EdgedFace; maxFace: EdgedFace }

Returns two copies of the face, one that is clipped to y values less than the given y value, and one that is clipped to values greater than the given y value.

The fakeCornerX is used to determine the "fake" corner that is used for unsorted-edge clipping.

getBinaryLineClip( normal : Vector2, value : number, fakeCornerPerpendicular : number ) : { minFace: EdgedFace; maxFace: EdgedFace }

Returns two copies of the face, one that is clipped to contain points where dot( normal, point ) < value, and one that is clipped to contain points where dot( normal, point ) > value.

The fake corner perpendicular is used to determine the "fake" corner that is used for unsorted-edge clipping

getStripeLineClip( normal : Vector2, values : number[], fakeCornerPerpendicular : number ) : EdgedFace[]

Returns an array of faces, clipped similarly to getBinaryLineClip, but with more than one (parallel) split line at a time. The first face will be the one with dot( normal, point ) < values[0], the second one with values[ 0 ] < dot( normal, point ) < values[1], etc.

getBinaryCircularClip( center : Vector2, radius : number, maxAngleSplit : number ) : { insideFace: EdgedFace; outsideFace: EdgedFace }

Returns two copies of the face, one that is clipped to contain points inside the circle defined by the given center and radius, and one that is clipped to contain points outside the circle.

NOTE: maxAngleSplit is used to determine the polygonal approximation of the circle. The returned result will not have a chord with an angle greater than maxAngleSplit.

gridClipIterate( minX : number, minY : number, maxX : number, maxY : number, stepX : number, stepY : number, stepWidth : number, stepHeight : number, callback : GridClipCallback, polygonCompleteCallback : PolygonCompleteCallback )

Given an integral bounding box and step sizes (which define the grid), this will clip the face to each cell in the grid, calling the callback for each cell's contributing edges (in order, if we are a PolygonalFace). polygonCompleteCallback will be called whenever a polygon is completed (if we are a polygonal type of face).

getBilinearFiltered( pointX : number, pointY : number, minX : number, minY : number ) : number

Returns the evaluation of the bilinear (tent) filter integrals for the given point, ASSUMING that the face is clipped to the transformed unit square of x: [minX,minX+1], y: [minY,minY+1].

getMitchellNetravaliFiltered( pointX : number, pointY : number, minX : number, minY : number ) : number

Returns the evaluation of the Mitchell-Netravali (⅓,⅓) filter integrals for the given point, ASSUMING that the face is clipped to the transformed unit square of x: [minX,minX+1], y: [minY,minY+1].

containsPoint( point : Vector2 ) : boolean

Returns whether the face contains the given point.

getTransformed( transform : Matrix3 ) : EdgedFace

Returns an affine-transformed version of the face.

getRounded( epsilon : number ) : EdgedFace

Returns a rounded version of the face, where [-epsilon/2, epsilon/2] rounds to 0, etc.

withReversedEdges() : EdgedFace

Returns a version of the face with the orientation of all of the edges swapped.

forEachEdge( callback : ( startPoint: Vector2, endPoint: Vector2 ) => void )

Calls the callback with points for each edge in the face.

getScratchAccumulator() : ClippableFaceAccumulator<EdgedFace>

Returns a singleton accumulator for this type of face. This will always return the same instance, and should ONLY be used if there will be no reentrant or co-occurring usage of this accumulator (i.e. only use it when you can guarantee nothing else will be clipped at the same time). If two tasks try to use this at the same time, it will likely break.

This is a method that can be called on an unknown-type face, to reproduce the same type of face. This is important, since we can't feed unsorted edge data directly to a PolygonalFace's accumulator, and in general this is the safest way to get an accumulator for a face.

getAccumulator() : ClippableFaceAccumulator<EdgedFace>

Returns a new accumulator for this type of face. This should be used when concurrent clipping will need to happen.

This is a method that can be called on an unknown-type face, to reproduce the same type of face. This is important, since we can't feed unsorted edge data directly to a PolygonalFace's accumulator, and in general this is the safest way to get an accumulator for a face.

toString() : string

Returns a debugging string.

serialize() : SerializedEdgedFace

Returns a serialized version of the face, that should be able to be deserialized into the same type of face. See {FaceType}.deserialize.

NOTE: If you don't know what type of face this is, use serializeClippableFace instead.

Static Methods

getScratchAccumulator() : ClippableFaceAccumulator<EdgedFace>

Returns a singleton accumulator for this type of face. This will always return the same instance, and should ONLY be used if there will be no reentrant or co-occurring usage of this accumulator (i.e. only use it when you can guarantee nothing else will be clipped at the same time). If two tasks try to use this at the same time, it will likely break.

This should be used directly when you know you want an EdgedFace as output.

deserialize( serialized : SerializedEdgedFace ) : EdgedFace

fromBounds( bounds : Bounds2 ) : EdgedFace

fromBoundsValues( minX : number, minY : number, maxX : number, maxY : number ) : EdgedFace

Class EdgedFaceAccumulator

import { EdgedFaceAccumulator } from 'scenerystack/alpenglow';

Instance Methods

addEdge( startX : number, startY : number, endX : number, endY : number, startPoint : Vector2 | null, endPoint : Vector2 | null )

markNewPolygon()

setAccumulationBounds( minX : number, minY : number, maxX : number, maxY : number )

finalizeFace() : EdgedFace | null

Will reset it to the initial state also

reset()

Will reset without creating a face

Instance Properties

usesEndPoint

(readonly)

Class PolygonalFace

A ClippableFace from a set of polygons (each one is a closed loop of Vector2s)

Relies on the main boundary being positive-oriented, and the holes being negative-oriented and non-overlapping

@author Jonathan Olson <jonathan.olson@colorado.edu>

import { PolygonalFace } from 'scenerystack/alpenglow';

Constructor

new PolygonalFace( polygons : Vector2[][] )

Instance Methods

toEdgedFace() : EdgedFace

Converts the face to an edged face.

toPolygonalFace( epsilon? : number ) : PolygonalFace

Converts the face to a polygonal face.

toEdgedClippedFace( minX : number, minY : number, maxX : number, maxY : number ) : EdgedClippedFace

Converts the face to a edged-clipped face (scanning the edges, to convert bounds-side edges to counts)

toEdgedClippedFaceWithoutCheck( minX : number, minY : number, maxX : number, maxY : number ) : EdgedClippedFace

Converts the face to a edged-clipped face (without scanning the edges to see if they are bounds-side)

getShape( epsilon? : number ) : Shape

Returns a Shape for the face.

NOTE: This is likely a low-performance method, and should only be used for debugging.

getBounds() : Bounds2

Returns the bounds of the face (ignoring any "fake" edges, if the type supports them)

getDotRange( normal : Vector2 ) : Range

Returns the range of values for the dot product of the given normal with any point contained within the face (for polygons, this is the same as the range of values for the dot product of the normal with any vertex).

getDistanceRangeToEdges( point : Vector2 ) : Range

Returns the range of distances from the given point to every point along the edges of the face. For instance, if the face was the unit cube, the range would be ½ to sqrt(2), for distances to the middles of the edges and the corners respectively.

getDistanceRangeToInside( point : Vector2 ) : Range

Returns the range of distances from the given point to every point inside the face. The upper bound should be the same as getDistanceRangeToEdges, however the lower bound may be 0 if the point is inside the face.

getArea() : number

Returns the signed area of the face (positive if the vertices are in counter-clockwise order, negative if clockwise)

getCentroidPartial() : Vector2

Returns the partial for the centroid computation. These should be summed up, divided by 6, and divided by the area to give the full centroid

getCentroid( area : number ) : Vector2

Returns the centroid of the face (area is required for the typical integral required to evaluate)

getZero() : number

Returns the evaluation of an integral that will be zero if the boundaries of the face are correctly closed. It is designed so that if there is a "gap" and we have open boundaries, the result will likely be non-zero.

NOTE: This is only used for debugging, so performance is not a concern.

getAverageDistance( point : Vector2, area : number ) : number

Returns the average distance from the given point to every point inside the face. The integral evaluation requires the area (similarly to the centroid computation).

getAverageDistanceTransformedToOrigin( transform : Matrix3, area : number ) : number

Returns the average distance from the origin to every point inside the face transformed by the given matrix.

getClipped( minX : number, minY : number, maxX : number, maxY : number ) : PolygonalFace

Returns a copy of the face that is clipped to be within the given axis-aligned bounding box.

getBinaryXClip( x : number, fakeCornerY : number ) : { minFace: PolygonalFace; maxFace: PolygonalFace }

Returns two copies of the face, one that is clipped to be to the left of the given x value, and one that is clipped to be to the right of the given x value.

The fakeCornerY is used to determine the "fake" corner that is used for unsorted-edge clipping.

getBinaryYClip( y : number, fakeCornerX : number ) : { minFace: PolygonalFace; maxFace: PolygonalFace }

Returns two copies of the face, one that is clipped to y values less than the given y value, and one that is clipped to values greater than the given y value.

The fakeCornerX is used to determine the "fake" corner that is used for unsorted-edge clipping.

getBinaryLineClip( normal : Vector2, value : number, fakeCornerPerpendicular : number ) : { minFace: PolygonalFace; maxFace: PolygonalFace }

Returns two copies of the face, one that is clipped to contain points where dot( normal, point ) < value, and one that is clipped to contain points where dot( normal, point ) > value.

The fake corner perpendicular is used to determine the "fake" corner that is used for unsorted-edge clipping

getStripeLineClip( normal : Vector2, values : number[], fakeCornerPerpendicular : number ) : PolygonalFace[]

Returns an array of faces, clipped similarly to getBinaryLineClip, but with more than one (parallel) split line at a time. The first face will be the one with dot( normal, point ) < values[0], the second one with values[ 0 ] < dot( normal, point ) < values[1], etc.

getBinaryCircularClip( center : Vector2, radius : number, maxAngleSplit : number ) : { insideFace: PolygonalFace; outsideFace: PolygonalFace }

Returns two copies of the face, one that is clipped to contain points inside the circle defined by the given center and radius, and one that is clipped to contain points outside the circle.

NOTE: maxAngleSplit is used to determine the polygonal approximation of the circle. The returned result will not have a chord with an angle greater than maxAngleSplit.

gridClipIterate( minX : number, minY : number, maxX : number, maxY : number, stepX : number, stepY : number, stepWidth : number, stepHeight : number, callback : GridClipCallback, polygonCompleteCallback : PolygonCompleteCallback )

Given an integral bounding box and step sizes (which define the grid), this will clip the face to each cell in the grid, calling the callback for each cell's contributing edges (in order, if we are a PolygonalFace). polygonCompleteCallback will be called whenever a polygon is completed (if we are a polygonal type of face).

getBilinearFiltered( pointX : number, pointY : number, minX : number, minY : number ) : number

Returns the evaluation of the bilinear (tent) filter integrals for the given point, ASSUMING that the face is clipped to the transformed unit square of x: [minX,minX+1], y: [minY,minY+1].

getMitchellNetravaliFiltered( pointX : number, pointY : number, minX : number, minY : number ) : number

Returns the evaluation of the Mitchell-Netravali (⅓,⅓) filter integrals for the given point, ASSUMING that the face is clipped to the transformed unit square of x: [minX,minX+1], y: [minY,minY+1].

containsPoint( point : Vector2 ) : boolean

Returns whether the face contains the given point.

getTransformed( transform : Matrix3 ) : PolygonalFace

Returns an affine-transformed version of the face.

getRounded( epsilon : number ) : PolygonalFace

Returns a rounded version of the face, where [-epsilon/2, epsilon/2] rounds to 0, etc.

withReversedEdges() : PolygonalFace

Returns a version of the face with the orientation of all of the edges swapped.

forEachEdge( callback : ( startPoint: Vector2, endPoint: Vector2 ) => void )

Calls the callback with points for each edge in the face.

getScratchAccumulator() : ClippableFaceAccumulator<PolygonalFace>

Returns a singleton accumulator for this type of face. This will always return the same instance, and should ONLY be used if there will be no reentrant or co-occurring usage of this accumulator (i.e. only use it when you can guarantee nothing else will be clipped at the same time). If two tasks try to use this at the same time, it will likely break.

This is a method that can be called on an unknown-type face, to reproduce the same type of face. This is important, since we can't feed unsorted edge data directly to a PolygonalFace's accumulator, and in general this is the safest way to get an accumulator for a face.

getAccumulator() : ClippableFaceAccumulator<PolygonalFace>

Returns a new accumulator for this type of face. This should be used when concurrent clipping will need to happen.

This is a method that can be called on an unknown-type face, to reproduce the same type of face. This is important, since we can't feed unsorted edge data directly to a PolygonalFace's accumulator, and in general this is the safest way to get an accumulator for a face.

toString() : string

Returns a debugging string.

serialize() : SerializedPolygonalFace

Returns a serialized version of the face, that should be able to be deserialized into the same type of face. See {FaceType}.deserialize.

NOTE: If you don't know what type of face this is, use serializeClippableFace instead.

Static Methods

getScratchAccumulator() : ClippableFaceAccumulator<PolygonalFace>

Returns a singleton accumulator for this type of face. This will always return the same instance, and should ONLY be used if there will be no reentrant or co-occurring usage of this accumulator (i.e. only use it when you can guarantee nothing else will be clipped at the same time). If two tasks try to use this at the same time, it will likely break.

This should be used directly when you know you want a PolygonalFace as output. NOTE: edges SHOULD be ordered such that the endPoint of the last edge is the same as the startPoint of the first edge, UNLESS a loop has been closed and a polygon has been marked.

deserialize( serialized : SerializedPolygonalFace ) : PolygonalFace

fromBounds( bounds : Bounds2 ) : PolygonalFace

fromBoundsValues( minX : number, minY : number, maxX : number, maxY : number ) : PolygonalFace

Class PolygonalFaceAccumulator

import { PolygonalFaceAccumulator } from 'scenerystack/alpenglow';

Instance Methods

addEdge( startX : number, startY : number, endX : number, endY : number, startPoint : Vector2 | null, endPoint : Vector2 | null )

markNewPolygon()

setAccumulationBounds( minX : number, minY : number, maxX : number, maxY : number )

finalizeFace() : PolygonalFace | null

Will reset it to the initial state also

reset()

Will reset without creating a face

Instance Properties

usesEndPoint

(readonly)

Type PolygonCompleteCallback

import type { PolygonCompleteCallback } from 'scenerystack/alpenglow';

() => void

Type SerializedClippableFace

import type { SerializedClippableFace } from 'scenerystack/alpenglow';

{ type: "PolygonalFace"; face: SerializedPolygonalFace } | { type: "EdgedFace"; face: SerializedEdgedFace }

Type SerializedEdgedClippedFace

import type { SerializedEdgedClippedFace } from 'scenerystack/alpenglow';
  • edges: SerializedLinearEdge[]
  • minX: number
  • minY: number
  • maxX: number
  • maxY: number
  • minXCount: number
  • minYCount: number
  • maxXCount: number
  • maxYCount: number

Type SerializedEdgedFace

import type { SerializedEdgedFace } from 'scenerystack/alpenglow';

Type SerializedPolygonalFace

import type { SerializedPolygonalFace } from 'scenerystack/alpenglow';
  • polygons: { x: number; y: number }[][]

Source Code

See the source for ClippableFace.ts in the alpenglow repository.