Shape¶
Under Construction
This documentation is auto-generated, and is a work in progress. Please see the source code at https://github.com/phetsims/kite/blob/main/js/Shape.ts for the most up-to-date information.
Overview¶
Shape handling
Shapes are internally made up of Subpaths, which contain a series of segments, and are optionally closed. Familiarity with how Canvas handles subpaths is helpful for understanding this code.
Canvas spec: http://www.w3.org/TR/2dcontext/ SVG spec: http://www.w3.org/TR/SVG/expanded-toc.html http://www.w3.org/TR/SVG/paths.html#PathData (for paths) Notes for elliptical arcs: http://www.w3.org/TR/SVG/implnote.html#PathElementImplementationNotes Notes for painting strokes: https://svgwg.org/svg2-draft/painting.html
TODO: add nonzero / evenodd support when browsers support it https://github.com/phetsims/kite/issues/76 TODO: docs
@author Jonathan Olson <jonathan.olson@colorado.edu>
Class Shape¶
Constructor¶
new Shape( subpaths? : Subpath[] | string, bounds? : Bounds2 )¶
Instance Methods¶
moveTo( x : number, y : number ) : this¶
Moves to a point given by the coordinates x and y
moveToRelative( x : number, y : number ) : this¶
Moves a relative displacement (x,y) from last point
moveToPointRelative( displacement : Vector2 ) : this¶
Moves a relative displacement (point) from last point
moveToPoint( point : Vector2 ) : this¶
Adds to this shape a subpath that moves (no joint) it to a point
lineTo( x : number, y : number ) : this¶
Adds to this shape a straight line from last point to the coordinate (x,y)
lineToRelative( x : number, y : number ) : this¶
Adds to this shape a straight line displaced by a relative amount x, and y from last point
@param x - horizontal displacement @param y - vertical displacement
lineToPointRelative( displacement : Vector2 ) : this¶
Adds to this shape a straight line displaced by a relative displacement (point)
lineToPoint( point : Vector2 ) : this¶
Adds to this shape a straight line from this lastPoint to point
horizontalLineTo( x : number ) : this¶
Adds a horizontal line (x represents the x-coordinate of the end point)
horizontalLineToRelative( x : number ) : this¶
Adds a horizontal line (x represent a horizontal displacement)
verticalLineTo( y : number ) : this¶
Adds a vertical line (y represents the y-coordinate of the end point)
verticalLineToRelative( y : number ) : this¶
Adds a vertical line (y represents a vertical displacement)
zigZagTo( endX : number, endY : number, amplitude : number, numberZigZags : number, symmetrical : boolean ) : this¶
Zig-zags between the current point and the specified point
@param endX - the end of the shape @param endY - the end of the shape @param amplitude - the vertical amplitude of the zig zag wave @param numberZigZags - the number of oscillations @param symmetrical - flag for drawing a symmetrical zig zag
zigZagToPoint( endPoint : Vector2, amplitude : number, numberZigZags : number, symmetrical : boolean ) : this¶
Zig-zags between the current point and the specified point. Implementation moved from circuit-construction-kit-common on April 22, 2019.
@param endPoint - the end of the shape @param amplitude - the vertical amplitude of the zig zag wave, signed to choose initial direction @param numberZigZags - the number of complete oscillations @param symmetrical - flag for drawing a symmetrical zig zag
quadraticCurveTo( cpx : number, cpy : number, x : number, y : number ) : this¶
Adds a quadratic curve to this shape
The curve is guaranteed to pass through the coordinate (x,y) but does not pass through the control point
@param cpx - control point horizontal coordinate @param cpy - control point vertical coordinate @param x @param y
quadraticCurveToRelative( cpx : number, cpy : number, x : number, y : number ) : this¶
Adds a quadratic curve to this shape. The control and final points are specified as displacment from the last point in this shape
@param cpx - control point horizontal coordinate @param cpy - control point vertical coordinate @param x - final x position of the quadratic curve @param y - final y position of the quadratic curve
quadraticCurveToPointRelative( controlPoint : Vector2, point : Vector2 ) : this¶
Adds a quadratic curve to this shape. The control and final points are specified as displacement from the last point in this shape
@param controlPoint @param point - the quadratic curve passes through this point
smoothQuadraticCurveTo( x : number, y : number ) : this¶
Adds a quadratic curve to this shape. The quadratic curves passes through the x and y coordinate. The shape should join smoothly with the previous subpaths
TODO: consider a rename to put 'smooth' farther back? https://github.com/phetsims/kite/issues/76
@param x - final x position of the quadratic curve @param y - final y position of the quadratic curve
smoothQuadraticCurveToRelative( x : number, y : number ) : this¶
Adds a quadratic curve to this shape. The quadratic curves passes through the x and y coordinate. The shape should join smoothly with the previous subpaths
@param x - final x position of the quadratic curve @param y - final y position of the quadratic curve
quadraticCurveToPoint( controlPoint : Vector2, point : Vector2 ) : this¶
Adds a quadratic bezier curve to this shape.
@param controlPoint @param point - the quadratic curve passes through this point
cubicCurveTo( cp1x : number, cp1y : number, cp2x : number, cp2y : number, x : number, y : number ) : this¶
Adds a cubic bezier curve to this shape.
@param cp1x - control point 1, horizontal coordinate @param cp1y - control point 1, vertical coordinate @param cp2x - control point 2, horizontal coordinate @param cp2y - control point 2, vertical coordinate @param x - final x position of the cubic curve @param y - final y position of the cubic curve
cubicCurveToRelative( cp1x : number, cp1y : number, cp2x : number, cp2y : number, x : number, y : number ) : this¶
@param cp1x - control point 1, horizontal displacement @param cp1y - control point 1, vertical displacement @param cp2x - control point 2, horizontal displacement @param cp2y - control point 2, vertical displacement @param x - final horizontal displacement @param y - final vertical displacment
cubicCurveToPointRelative( control1 : Vector2, control2 : Vector2, point : Vector2 ) : this¶
@param control1 - control displacement 1 @param control2 - control displacement 2 @param point - final displacement
smoothCubicCurveTo( cp2x : number, cp2y : number, x : number, y : number ) : this¶
@param cp2x - control point 2, horizontal coordinate @param cp2y - control point 2, vertical coordinate @param x @param y
smoothCubicCurveToRelative( cp2x : number, cp2y : number, x : number, y : number ) : this¶
@param cp2x - control point 2, horizontal coordinate @param cp2y - control point 2, vertical coordinate @param x @param y
cubicCurveToPoint( control1 : Vector2, control2 : Vector2, point : Vector2 ) : this¶
arc( centerX : number, centerY : number, radius : number, startAngle : number, endAngle : number, anticlockwise? : boolean ) : this¶
@param centerX - horizontal coordinate of the center of the arc @param centerY - Center of the arc @param radius - How far from the center the arc will be @param startAngle - Angle (radians) of the start of the arc @param endAngle - Angle (radians) of the end of the arc @param [anticlockwise] - Decides which direction the arc takes around the center
arcPoint( center : Vector2, radius : number, startAngle : number, endAngle : number, anticlockwise? : boolean ) : this¶
@param center - Center of the arc (every point on the arc is equally far from the center) @param radius - How far from the center the arc will be @param startAngle - Angle (radians) of the start of the arc @param endAngle - Angle (radians) of the end of the arc @param [anticlockwise] - Decides which direction the arc takes around the center
ellipticalArc( centerX : number, centerY : number, radiusX : number, radiusY : number, rotation : number, startAngle : number, endAngle : number, anticlockwise? : boolean ) : this¶
Creates an elliptical arc
@param centerX - horizontal coordinate of the center of the arc @param centerY - vertical coordinate of the center of the arc @param radiusX - semi axis @param radiusY - semi axis @param rotation - rotation of the elliptical arc with respect to the positive x axis. @param startAngle @param endAngle @param [anticlockwise]
ellipticalArcPoint( center : Vector2, radiusX : number, radiusY : number, rotation : number, startAngle : number, endAngle : number, anticlockwise? : boolean ) : this¶
Creates an elliptic arc
@param center @param radiusX @param radiusY @param rotation - rotation of the arc with respect to the positive x axis. @param startAngle - @param endAngle @param [anticlockwise]
close() : this¶
Adds a subpath that joins the last point of this shape to the first point to form a closed shape
newSubpath() : this¶
Moves to the next subpath, but without adding any points to it (like a moveTo would do).
This is particularly helpful for cases where you don't want to have to compute the explicit starting point of the next subpath. For instance, if you want three disconnected circles: - shape.circle( 50, 50, 20 ).newSubpath().circle( 100, 100, 20 ).newSubpath().circle( 150, 50, 20 )
See https://github.com/phetsims/kite/issues/72 for more info.
makeImmutable() : this¶
Makes this Shape immutable, so that attempts to further change the Shape will fail. This allows clients to avoid adding change listeners to this Shape.
isImmutable() : boolean¶
Returns whether this Shape is immutable (see makeImmutable for details).
ellipticalArcToRelative( radiusX : number, radiusY : number, rotation : number, largeArc : boolean, sweep : boolean, x : number, y : number ) : this¶
Matches SVG's elliptical arc from http://www.w3.org/TR/SVG/paths.html
WARNING: rotation (for now) is in DEGREES. This will probably change in the future.
@param radiusX - Semi-major axis size @param radiusY - Semi-minor axis size @param rotation - Rotation of the ellipse (its semi-major axis) @param largeArc - Whether the arc will go the longest route around the ellipse. @param sweep - Whether the arc made goes from start to end "clockwise" (opposite of anticlockwise flag) @param x - End point X position @param y - End point Y position
ellipticalArcTo( radiusX : number, radiusY : number, rotation : number, largeArc : boolean, sweep : boolean, x : number, y : number ) : this¶
Matches SVG's elliptical arc from http://www.w3.org/TR/SVG/paths.html
WARNING: rotation (for now) is in DEGREES. This will probably change in the future.
@param radiusX - Semi-major axis size @param radiusY - Semi-minor axis size @param rotation - Rotation of the ellipse (its semi-major axis) @param largeArc - Whether the arc will go the longest route around the ellipse. @param sweep - Whether the arc made goes from start to end "clockwise" (opposite of anticlockwise flag) @param x - End point X position @param y - End point Y position
circle( center : Vector2, radius : number ) : this¶
Draws a circle using the arc() call
circle( centerX : number, centerY : number, radius : number ) : this¶
circle( centerX : Vector2 | number, centerY : number, radius? : number ) : this¶
ellipse( center : Vector2, radiusX : number, radiusY : number, rotation : number ) : this¶
Draws an ellipse using the ellipticalArc() call
The rotation is about the centerX, centerY.
ellipse( centerX : number, centerY : number, radiusX : number, radiusY : number, rotation : number ) : this¶
ellipse( centerX : Vector2 | number, centerY : number, radiusX : number, radiusY : number, rotation? : number ) : this¶
rect( x : number, y : number, width : number, height : number ) : this¶
Creates a rectangle shape
@param x - left position @param y - bottom position (in non inverted cartesian system) @param width @param height
roundRect( x : number, y : number, width : number, height : number, arcw : number, arch : number ) : this¶
Creates a round rectangle. All arguments are number.
@param x @param y @param width - width of the rectangle @param height - height of the rectangle @param arcw - arc width @param arch - arc height
polygon( vertices : Vector2[] ) : this¶
Creates a polygon from an array of vertices.
cardinalSpline( positions : Vector2[], providedOptions? : CardinalSplineOptions ) : this¶
This is a convenience function that allows to generate Cardinal splines from a position array. Cardinal spline differs from Bezier curves in that all defined points on a Cardinal spline are on the path itself.
It includes a tension parameter to allow the client to specify how tightly the path interpolates between points. One can think of the tension as the tension in a rubber band around pegs. however unlike a rubber band the tension can be negative. the tension ranges from -1 to 1
copy() : Shape¶
Returns a copy of this shape
writeToContext( context : CanvasRenderingContext2D )¶
Writes out this shape's path to a canvas 2d context. does NOT include the beginPath()!
getSVGPath() : string¶
Returns something like "M150 0 L75 200 L225 200 Z" for a triangle (to be used with a SVG path element's 'd' attribute)
transformed( matrix : Matrix3 ) : Shape¶
Returns a new Shape that is transformed by the associated matrix
nonlinearTransformed( providedOptions? : NonlinearTransformedOptions ) : Shape¶
Converts this subpath to a new shape made of many line segments (approximating the current shape) with the transformation applied.
polarToCartesian( options? : NonlinearTransformedOptions ) : Shape¶
Maps points by treating their x coordinate as polar angle, and y coordinate as polar magnitude. See http://en.wikipedia.org/wiki/Polar_coordinate_system
Please see Shape.nonlinearTransformed for more documentation on adaptive discretization options (minLevels, maxLevels, distanceEpsilon, curveEpsilon)
Example: A line from (0,10) to (pi,10) will be transformed to a circular arc from (10,0) to (-10,0) passing through (0,10).
toPiecewiseLinear( options? : NonlinearTransformedOptions ) : Shape¶
Converts each segment into lines, using an adaptive (midpoint distance subdivision) method.
NOTE: uses nonlinearTransformed method internally, but since we don't provide a pointMap or methodName, it won't create anything but line segments. See nonlinearTransformed for documentation of options
containsPoint( point : Vector2 ) : boolean¶
Is this point contained in this shape
intersection( ray : Ray2 ) : RayIntersection[]¶
Hit-tests this shape with the ray. An array of all intersections of the ray with this shape will be returned. For this function, intersections will be returned sorted by the distance from the ray's position.
interiorIntersectsLineSegment( startPoint : Vector2, endPoint : Vector2 ) : boolean¶
Returns whether the provided line segment would have some part on top or touching the interior (filled area) of this shape.
This differs somewhat from an intersection of the line segment with the Shape's path, as we will return true ("intersection") if the line segment is entirely contained in the interior of the Shape's path.
@param startPoint - One end of the line segment @param endPoint - The other end of the line segment
windingIntersection( ray : Ray2 ) : number¶
Returns the winding number for intersection with a ray
intersectsBounds( bounds : Bounds2 ) : boolean¶
Whether the path of the Shape intersects (or is contained in) the provided bounding box. Computed by checking intersections with all four edges of the bounding box, or whether the Shape is totally contained within the bounding box.
getStrokedShape( lineStyles : LineStyles ) : Shape¶
Returns a new Shape that is an outline of the stroked path of this current Shape. currently not intended to be nested (doesn't do intersection computations yet)
TODO: rename stroked( lineStyles )? https://github.com/phetsims/kite/issues/76
getOffsetShape( distance : number ) : Shape¶
Gets a shape offset by a certain amount.
getDashedShape( lineDash : number[], lineDashOffset : number, providedOptions? : GetDashedShapeOptions ) : Shape¶
Returns a copy of this subpath with the dash "holes" removed (has many subpaths usually).
getBounds() : Bounds2¶
Returns the bounds of this shape. It is the bounding-box union of the bounds of each subpath contained.
getStrokedBounds( lineStyles : LineStyles ) : Bounds2¶
Returns the bounds for a stroked version of this shape. The input lineStyles are used to determine the size and style of the stroke, and then the bounds of the stroked shape are returned.
getSimplifiedAreaShape() : Shape¶
Returns a simplified form of this shape.
Runs it through the normal CAG process, which should combine areas where possible, handles self-intersection, etc.
NOTE: Currently (2017-10-04) adjacent segments may get simplified only if they are lines. Not yet complete.
getBoundsWithTransform( matrix : Matrix3, lineStyles? : LineStyles ) : Bounds2¶
getApproximateArea( numSamples : number ) : number¶
Return an approximate value of the area inside of this Shape (where containsPoint is true) using Monte-Carlo.
NOTE: Generally, use getArea(). This can be used for verification, but takes a large number of samples.
@param numSamples - How many times to randomly check for inclusion of points.
getNonoverlappingArea() : number¶
Return the area inside the Shape (where containsPoint is true), assuming there is no self-intersection or overlap, and the same orientation (winding order) is used. Should also support holes (with opposite orientation), assuming they don't intersect the containing subpath.
getArea() : number¶
Returns the area inside the shape.
NOTE: This requires running it through a lot of computation to determine a non-overlapping non-self-intersecting form first. If the Shape is "simple" enough, getNonoverlappingArea would be preferred.
getApproximateCentroid( numSamples : number ) : Vector2¶
Return the approximate location of the centroid of the Shape (the average of all points where containsPoint is true) using Monte-Carlo methods.
@param numSamples - How many times to randomly check for inclusion of points.
getClosestPoints( point : Vector2 ) : ClosestToPointResult[]¶
Returns an array of potential closest point results on the Shape to the given point.
getClosestPoint( point : Vector2 ) : Vector2¶
Returns a single point ON the Shape boundary that is closest to the given point (picks an arbitrary one if there are multiple).
invalidatePoints()¶
Should be called after mutating the x/y of Vector2 points that were passed in to various Shape calls, so that derived information computed (bounds, etc.) will be correct, and any clients (e.g. Scenery Paths) will be notified of the updates.
toString() : string¶
getLastPoint() : Vector2¶
Gets the last point in the last subpath, or null if it doesn't exist
shapeUnion( shape : Shape ) : Shape¶
Returns a new shape that contains a union of the two shapes (a point in either shape is in the resulting shape).
shapeIntersection( shape : Shape ) : Shape¶
Returns a new shape that contains the intersection of the two shapes (a point in both shapes is in the resulting shape).
shapeDifference( shape : Shape ) : Shape¶
Returns a new shape that contains the difference of the two shapes (a point in the first shape and NOT in the second shape is in the resulting shape).
shapeXor( shape : Shape ) : Shape¶
Returns a new shape that contains the xor of the two shapes (a point in only one shape is in the resulting shape).
shapeClip( shape : Shape, options? : { includeExterior?: boolean; includeBoundary: boolean; includeInterior: boolean } ) : Shape¶
Returns a new shape that only contains portions of segments that are within the passed-in shape's area.
// TODO: convert Graph to TS and get the types from there https://github.com/phetsims/kite/issues/76
getArcLength( distanceEpsilon? : number, curveEpsilon? : number, maxLevels? : number ) : number¶
Returns the (sometimes approximate) arc length of all the shape's subpaths combined.
serialize() : SerializedShape¶
Returns an object form that can be turned back into a segment with the corresponding deserialize method.
Instance Properties¶
subpaths : Subpath[]¶
(readonly)
Lower-level piecewise mathematical description using segments, also individually immutable
invalidatedEmitter : TinyEmitter¶
(readonly)
Static Methods¶
deserialize( obj : SerializedShape ) : Shape¶
Returns a Shape from the serialized representation.
rectangle( x : number, y : number, width : number, height : number ) : Shape¶
Creates a rectangle
roundRect( x : number, y : number, width : number, height : number, arcw : number, arch : number ) : Shape¶
Creates a round rectangle {Shape}, with {number} arguments. Uses circular or elliptical arcs if given.
roundedRectangleWithRadii( x : number, y : number, width : number, height : number, cornerRadii? : Partial<CornerRadiiOptions> ) : Shape¶
Creates a rounded rectangle, where each corner can have a different radius. The radii default to 0, and may be set using topLeft, topRight, bottomLeft and bottomRight in the options. If the specified radii are larger than the dimension on that side, they radii are reduced proportionally, see https://github.com/phetsims/under-pressure/issues/151
E.g.:
var cornerRadius = 20; var rect = Shape.roundedRectangleWithRadii( 0, 0, 200, 100, { topLeft: cornerRadius, topRight: cornerRadius } );
@param x - Left edge position @param y - Top edge position @param width - Width of rectangle @param height - Height of rectangle @param [cornerRadii] - Optional object with potential radii for each corner.
boundsOffsetWithRadii( bounds : Bounds2, offsets : OffsetsOptions, radii? : CornerRadiiOptions ) : Shape¶
Returns a Shape from a bounds, offset (expanded) by certain amounts, and with certain corner radii.
polygon( vertices : Vector2[] ) : Shape¶
Creates a closed polygon from an array of vertices by connecting them by a series of lines. The lines are joining the adjacent vertices in the array.
bounds( bounds : Bounds2 ) : Shape¶
Creates a rectangular shape from bounds
lineSegment( x1 : number, y1 : number, x2 : number, y2 : number ) : Shape¶
Creates a line segment, using either (x1,y1,x2,y2) or ({x1,y1},{x2,y2}) arguments
lineSegment( p1 : Vector2, p2 : Vector2 ) : Shape¶
lineSegment( a : Vector2 | number, b : Vector2 | number, c? : number, d? : number ) : Shape¶
regularPolygon( sides : number, radius : number ) : Shape¶
Returns a regular polygon of radius and number of sides The regular polygon is oriented such that the first vertex lies on the positive x-axis.
@param sides - an integer @param radius
circle( centerX : number, centerY : number, radius : number ) : Shape¶
Creates a circle supports both circle( centerX, centerY, radius ), circle( center, radius ), and circle( radius ) with the center default to 0,0
circle( center : Vector2, radius : number ) : Shape¶
circle( radius : number ) : Shape¶
circle( a : Vector2 | number, b? : number, c? : number ) : Shape¶
ellipse( centerX : number, centerY : number, radiusX : number, radiusY : number, rotation : number ) : Shape¶
Supports ellipse( centerX, centerY, radiusX, radiusY, rotation ), ellipse( center, radiusX, radiusY, rotation ), and ellipse( radiusX, radiusY, rotation ) with the center default to 0,0 and rotation of 0. The rotation is about the centerX, centerY.
ellipse( center : Vector2, radiusX : number, radiusY : number, rotation : number ) : Shape¶
ellipse( radiusX : number, radiusY : number, rotation : number ) : Shape¶
ellipse( a : Vector2 | number, b : number, c : number, d? : number, e? : number ) : Shape¶
arc( centerX : number, centerY : number, radius : number, startAngle : number, endAngle : number, anticlockwise? : boolean ) : Shape¶
Supports both arc( centerX, centerY, radius, startAngle, endAngle, anticlockwise ) and arc( center, radius, startAngle, endAngle, anticlockwise )
@param radius - How far from the center the arc will be @param startAngle - Angle (radians) of the start of the arc @param endAngle - Angle (radians) of the end of the arc @param [anticlockwise] - Decides which direction the arc takes around the center
arc( center : Vector2, radius : number, startAngle : number, endAngle : number, anticlockwise? : boolean ) : Shape¶
arc( a : Vector2 | number, b : number, c : number, d : number, e? : number | boolean, f? : boolean ) : Shape¶
union( shapes : Shape[] ) : Shape¶
Returns the union of an array of shapes.
intersection( shapes : Shape[] ) : Shape¶
Returns the intersection of an array of shapes.
xor( shapes : Shape[] ) : Shape¶
Returns the xor of an array of shapes.
segments( segments : Segment[], closed? : boolean ) : Shape¶
Returns a new Shape constructed by appending a list of segments together.
fromGraph( graph : Graph ) : Shape¶
Returns a Shape that creates a subpath for each filled face (with the desired holes).
Generally should be called on a graph created with createFilledSubGraph().
fromSegment( segment : Segment ) : Shape¶
Static Properties¶
rect¶
roundRectangle¶
Type CornerRadiiOptions¶
- topLeft: number
- topRight: number
- bottomRight: number
- bottomLeft: number
Class Graph¶
A multigraph whose edges are segments.
Supports general shape simplification, overlap/intersection removal and computation. General output would include Shapes (from CAG - Constructive Area Geometry) and triangulations.
See Graph.binaryResult for the general procedure for CAG.
TODO: Use https://github.com/mauriciosantos/Buckets-JS for priority queue, implement simple sweep line https://github.com/phetsims/kite/issues/76 with "enters" and "leaves" entries in the queue. When edge removed, remove "leave" from queue. and add any replacement edges. Applies to overlap and intersection handling. NOTE: This should impact performance a lot, as we are currently over-scanning and re-scanning a lot. Intersection is currently (by far?) the performance bottleneck. TODO: Collapse non-Line adjacent edges together. Similar logic to overlap for each segment time, hopefully can factor this out. TODO: Properly handle sorting edges around a vertex when two edges have the same tangent out. We'll need to use curvature, or do tricks to follow both curves by an 'epsilon' and sort based on that. TODO: Consider separating out epsilon values (may be a general Kite thing rather than just ops) TODO: Loop-Blinn output and constrained Delaunay triangulation
@author Jonathan Olson <jonathan.olson@colorado.edu>
Constructor¶
new Graph()¶
Instance Methods¶
serialize() : SerializedGraph¶
Returns an object form that can be turned back into a segment with the corresponding deserialize method.
addShape( shapeId : number, shape : Shape, options? : GraphAddOptions )¶
Adds a Shape (with a given ID for CAG purposes) to the graph.
@param shapeId - The ID which should be shared for all paths/shapes that should be combined with respect to the winding number of faces. For CAG, independent shapes should be given different IDs (so they have separate winding numbers recorded).
addSubpath( shapeId : number, subpath : Subpath, providedOptions? : GraphAddOptions )¶
Adds a subpath of a Shape (with a given ID for CAG purposes) to the graph.
@param shapeId - See addShape() documentation
computeSimplifiedFaces()¶
Simplifies edges/vertices, computes boundaries and faces (with the winding map).
computeFaceInclusion( windingMapFilter : ( windingMap: Record<number, number> ) => boolean )¶
Sets whether each face should be filled or unfilled based on a filter function.
The windingMapFilter will be called on each face's winding map, and will use the return value as whether the face is filled or not.
The winding map is an {Object} associated with each face that has a key for every shapeId that was used in addShape/addSubpath, and the value for those keys is the winding number of the face given all paths with the shapeId.
For example, imagine you added two shapeIds (0 and 1), and the iteration is on a face that is included in one loop specified with shapeId:0 (inside a counter-clockwise curve), and is outside of any segments specified by the second loop (shapeId:1). Then the winding map will be: { 0: 1 // shapeId:0 has a winding number of 1 for this face (generally filled) 1: 0 // shapeId:1 has a winding number of 0 for this face (generally not filled) }
Generally, winding map filters can be broken down into two steps: 1. Given the winding number for each shapeId, compute whether that loop was originally filled. Normally, this is done with a non-zero rule (any winding number is filled, except zero). SVG also provides an even-odd rule (odd numbers are filled, even numbers are unfilled). 2. Given booleans for each shapeId from step 1, compute CAG operations based on boolean formulas. Say you wanted to take the union of shapeIds 0 and 1, then remove anything in shapeId 2. Given the booleans above, this can be directly computed as (filled0 || filled1) && !filled2.
createFilledSubGraph() : Graph¶
Create a new Graph object based only on edges in this graph that separate a "filled" face from an "unfilled" face.
This is a convenient way to "collapse" adjacent filled and unfilled faces together, and compute the curves and holes properly, given a filled "normal" graph.
facesToShape() : Shape¶
Returns a Shape that creates a subpath for each filled face (with the desired holes).
Generally should be called on a graph created with createFilledSubGraph().
dispose()¶
Releases owned objects to their pools, and clears references that may have been picked up from external sources.
computeBoundaryTree()¶
Given the inner and outer boundaries, it computes a tree representation to determine what boundaries are holes of what other boundaries, then sets up face holes with the result.
This information is stored in the childBoundaries array of Boundary, and is then read out to set up faces.
Instance Properties¶
vertices : Vertex[]¶
edges : Edge[]¶
innerBoundaries : Boundary[]¶
outerBoundaries : Boundary[]¶
boundaries : Boundary[]¶
shapeIds : number[]¶
loops : Loop[]¶
unboundedFace : Face¶
faces : Face[]¶
Static Methods¶
deserialize( obj : SerializedGraph ) : Graph¶
Recreate a Graph based on serialized state from serialize()
isInternal( point : Vector2, t : number, segment : Segment, distanceThreshold : number, tThreshold : number ) : boolean¶
BINARY_NONZERO_UNION( windingMap : Record<number, number> ) : boolean¶
"Union" binary winding map filter for use with Graph.binaryResult.
This combines both shapes together so that a point is in the resulting shape if it was in either of the input shapes.
@param windingMap - See computeFaceInclusion for more details
BINARY_NONZERO_INTERSECTION( windingMap : Record<number, number> ) : boolean¶
"Intersection" binary winding map filter for use with Graph.binaryResult.
This combines both shapes together so that a point is in the resulting shape if it was in both of the input shapes.
@param windingMap - See computeFaceInclusion for more details
BINARY_NONZERO_DIFFERENCE( windingMap : Record<number, number> ) : boolean¶
"Difference" binary winding map filter for use with Graph.binaryResult.
This combines both shapes together so that a point is in the resulting shape if it was in the first shape AND was NOT in the second shape.
@param windingMap - See computeFaceInclusion for more details
BINARY_NONZERO_XOR( windingMap : Record<number, number> ) : boolean¶
"XOR" binary winding map filter for use with Graph.binaryResult.
This combines both shapes together so that a point is in the resulting shape if it is only in exactly one of the input shapes. It's like the union minus intersection.
@param windingMap - See computeFaceInclusion for more details
binaryResult( shapeA : Shape, shapeB : Shape, windingMapFilter : ( windingMap: Record<number, number> ) => boolean ) : Shape¶
Returns the resulting Shape obtained by combining the two shapes given with the filter.
unionNonZero( shapes : Shape[] ) : Shape¶
Returns the union of an array of shapes.
intersectionNonZero( shapes : Shape[] ) : Shape¶
Returns the intersection of an array of shapes.
xorNonZero( shapes : Shape[] ) : Shape¶
Returns the xor of an array of shapes.
TODO: reduce code duplication? https://github.com/phetsims/kite/issues/76
simplifyNonZero( shape : Shape ) : Shape¶
Returns a simplified Shape obtained from running it through the simplification steps with non-zero output.
clipShape( clipAreaShape : Shape, shape : Shape, providedOptions? : GraphClipOptions ) : Shape¶
Returns a clipped version of shape
that contains only the parts that are within the area defined by clipAreaShape
Type GraphAddOptions¶
- ensureClosed?: boolean
Type GraphClipOptions¶
- includeExterior?: boolean
Respectively whether segments should be in the returned shape if they are in the exterior of the clipAreaShape (outside), on the boundary, or in the interior. - includeBoundary?: boolean
- includeInterior?: boolean
Type NonlinearTransformedOptions¶
- includeCurvature?: boolean
whether to include a default curveEpsilon (usually off by default) - & PiecewiseLinearOptions
Type SerializedGraph¶
- type: "Graph"
- vertices: SerializedVertex[]
- edges: SerializedEdge[]
- boundaries: SerializedBoundary[]
- innerBoundaries: number[]
- outerBoundaries: number[]
- shapeIds: number[]
- loops: SerializedLoop[]
- unboundedFace: number
- faces: SerializedFace[]
Type SerializedShape¶
a normalized vector for non-zero winding checks var weirdDir = v( Math.PI, 22 / 7 );
- type: "Shape"
- subpaths: SerializedSubpath[]