Skip to content

Segment

Segment (from scenerystack/kite) is the abstract base class behind every curve type kite offers: Arc, Cubic, Quadratic, Line (exported as KiteLine), and EllipticalArc all extend it. A Shape is ultimately just an array of Subpaths, and each Subpath is an ordered array of Segments — this page documents the contract they all share, so you can treat a mixed array of segment types uniformly (e.g. when walking shape.subpaths[i].segments) without caring which concrete subclass each one is.

ts
import { Shape, Segment } from 'scenerystack/kite';

const shape = new Shape().moveTo( 0, 0 ).lineTo( 100, 0 ).quadraticCurveTo( 150, 50, 100, 100 );

for ( const subpath of shape.subpaths ) {
  for ( const segment: Segment of subpath.segments ) {
    // Every segment, regardless of concrete type, supports this same API:
    console.log( segment.start.toString(), '->', segment.end.toString() );
    console.log( 'arc length:', segment.getArcLength() );
    console.log( 'midpoint:', segment.positionAt( 0.5 ).toString() );
  }
}

Every segment is parameterized over t from 0 (its start) to 1 (its end) — the same convention Cubic and Quadratic's Bézier parameter uses, and Arc/EllipticalArc map linearly onto their angular sweep.

The shared contract

These members are declared abstract on Segment — every concrete subclass must implement them, each according to its own geometry:

MemberEffect
start / end (getters)The segment's endpoints, at t=0 and t=1
startTangent / endTangent (getters)Normalized tangent vectors at the endpoints, pointing in the direction of travel
bounds (getter) / getBounds()The segment's axis-aligned Bounds2
positionAt( t )The point at parametric value t (0 <= t <= 1)
tangentAt( t )The non-normalized tangent (dx/dt, dy/dt) at t
curvatureAt( t )Signed curvature at t (positive for a visually-clockwise turn)
subdivided( t )Splits the segment into up to 2 sub-segments at t, together tracing the same curve
getInteriorExtremaTs()The interior t values (0 < t < 1) where dx/dt or dy/dt is zero — subdividing at these produces monotone pieces
strokeLeft( lineWidth ) / strokeRight( lineWidth )The offset-curve segments for the logical left/right side of a stroke
windingIntersection( ray )The winding-number contribution of a ray/segment intersection, used by Shape.containsPoint()
intersection( ray )The list of RayIntersections between this segment and a Ray2
getOverlaps( segment, epsilon? )Whether (and how) this segment overlaps another of a compatible type, for shape-simplification purposes
getSignedAreaFragment()This segment's contribution to a subpath's signed area (Green's theorem)
getNondegenerateSegments()A list of equivalent, non-degenerate segments (e.g. an Arc with zero radius simplifies away)
transformed( matrix )A new segment of the same or an equivalent type, transformed by a Matrix3
reversed()A new segment tracing the same curve with start/end swapped
getSVGPathFragment()The SVG path-data fragment for this segment alone (assuming the pen is already at start)
writeToContext( context )Draws the segment to a CanvasRenderingContext2D, assuming the context is already at start
serialize() / Segment.deserialize()Round-trip to/from a plain serializable object
invalidate()Recomputes any cached derived state after mutating the segment in place

Shared concrete behavior

Segment also implements a handful of concrete methods, in terms of the abstract ones above, so every subclass gets them for free:

MemberEffect
getArcLength( distanceEpsilon?, curveEpsilon?, maxLevels? )Adaptive-subdivision arc length estimate — recursively subdivided() until each piece is "sufficiently flat," then sums straight-line distances
slice( t0, t1 )The portion of the segment between two parametric values, via subdivided()
subdivisions( tList )Splits at every t in a sorted list, in one pass
subdividedIntoMonotone()subdivisions( this.getInteriorExtremaTs() )
isSufficientlyFlat( distanceEpsilon, curveEpsilon )Whether the segment is close enough to its start-end chord to treat as flat, used internally by getArcLength() and piecewise-linear conversion
getDashValues( lineDash, lineDashOffset, ... )Parametric t values where dash boundaries fall, for rendering a dashed stroke
toPiecewiseLinearSegments( options )Approximates the curve as a series of Line segments
getClosestPoints( point ) / Segment.closestToPoint( segments, point, threshold )Finds the closest point(s) on a segment (or set of segments) to an arbitrary point
Segment.intersect( a, b )Static: all SegmentIntersections between two arbitrary segments

Reach for the shared Segment API when writing type-agnostic geometry code

Anything that processes shape.subpaths[i].segments generically — measuring total path length, hit-testing, generating a dashed outline, or converting to line-only approximations for export — should be written against this shared Segment contract rather than switching on instanceof Arc/Cubic/etc. The five concrete pages (Arc, Cubic, Quadratic, Line, EllipticalArc) only need to document what's specific to each curve type — their constructors and shape-specific accessors (center/radius for Arc, control points for Cubic/Quadratic) — since everything on this page already applies uniformly.