Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeScript conversion #100

Open
jonathanolson opened this issue Mar 2, 2023 · 0 comments
Open

TypeScript conversion #100

jonathanolson opened this issue Mar 2, 2023 · 0 comments
Assignees

Comments

@jonathanolson
Copy link
Contributor

There are an assortment of issues converting kite internals to TS, notably:

TS2615: Type of property 'edge' circularly references itself in mapped type '{ [key in "edge" | "startVertex" | "endVertex"]: NotNull ; }'.
TS2615: Type of property 'forwardHalf' circularly references itself in mapped type '{ startVertex: Vertex; endVertex: Vertex; segment: Segment; forwardHalf: ActiveHalfEdge; reversedHalf: ActiveHalfEdge; }'.

If TS can't handle Edge having ActiveHalfEdges and HalfEdge having ActiveEdges, then this will be a painful conversion. Possibly will be fixed in the future?

Stopping a quick conversion, progress was:

Subject: [PATCH] Kite internals typescript
---
Index: js/imports.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/imports.ts b/js/imports.ts
--- a/js/imports.ts	(revision 36eadc34844462641aa8b8dc8f7bae5262e27f21)
+++ b/js/imports.ts	(date 1677785165379)
@@ -22,21 +22,30 @@
 export { default as svgPath } from './parser/svgPath.js';
 
 export { default as Segment } from './segments/Segment.js';
-export type { ClosestToPointResult, PiecewiseLinearOptions } from './segments/Segment.js';
+export type { ClosestToPointResult, PiecewiseLinearOptions, SerializedSegment } from './segments/Segment.js';
 export { default as Line } from './segments/Line.js';
+export type { SerializedLine } from './segments/Line.js';
 export { default as Quadratic } from './segments/Quadratic.js';
+export type { SerializedQuadratic } from './segments/Quadratic.js';
 export { default as Cubic } from './segments/Cubic.js';
+export type { SerializedCubic } from './segments/Cubic.js';
 export { default as Arc } from './segments/Arc.js';
+export type { SerializedArc } from './segments/Arc.js';
 export { default as EllipticalArc } from './segments/EllipticalArc.js';
+export type { SerializedEllipticalArc } from './segments/EllipticalArc.js';
 
 export { default as Subpath } from './util/Subpath.js';
 export { default as Shape } from './Shape.js';
 
-export { default as HalfEdge } from './ops/HalfEdge.js';
+export { default as HalfEdge, isActiveHalfEdge } from './ops/HalfEdge.js';
+export type { SerializedHalfEdge, ActiveHalfEdge } from './ops/HalfEdge.js';
 export { default as Vertex } from './ops/Vertex.js';
-export { default as Edge } from './ops/Edge.js';
+export type { SerializedVertex, VertexInternalData } from './ops/Vertex.js';
+export { default as Edge, isActiveEdge } from './ops/Edge.js';
+export type { SerializedEdge, ActiveEdge, EdgeInternalData } from './ops/Edge.js';
 export { default as Face } from './ops/Face.js';
 export { default as Loop } from './ops/Loop.js';
+export type { SerializedLoop } from './ops/Loop.js';
 export { default as Boundary } from './ops/Boundary.js';
 export { default as BoundsIntersection } from './ops/BoundsIntersection.js';
 export { default as SegmentTree } from './ops/SegmentTree.js';
Index: js/segments/EllipticalArc.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/segments/EllipticalArc.ts b/js/segments/EllipticalArc.ts
--- a/js/segments/EllipticalArc.ts	(revision 36eadc34844462641aa8b8dc8f7bae5262e27f21)
+++ b/js/segments/EllipticalArc.ts	(date 1677784394600)
@@ -24,7 +24,7 @@
 // constants
 const toDegrees = Utils.toDegrees;
 
-type SerializedEllipticalArc = {
+export type SerializedEllipticalArc = {
   type: 'EllipticalArc';
   centerX: number;
   centerY: number;
@@ -860,7 +860,7 @@
   /**
    * Returns an object form that can be turned back into a segment with the corresponding deserialize method.
    */
-  public serialize(): SerializedEllipticalArc {
+  public override serialize(): SerializedEllipticalArc {
     return {
       type: 'EllipticalArc',
       centerX: this._center.x,
Index: js/segments/Cubic.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/segments/Cubic.ts b/js/segments/Cubic.ts
--- a/js/segments/Cubic.ts	(revision 36eadc34844462641aa8b8dc8f7bae5262e27f21)
+++ b/js/segments/Cubic.ts	(date 1677784390803)
@@ -31,7 +31,7 @@
   return t >= 0 && t <= 1;
 }
 
-type SerializedCubic = {
+export type SerializedCubic = {
   type: 'Cubic';
   startX: number;
   startY: number;
@@ -852,7 +852,7 @@
   /**
    * Returns an object form that can be turned back into a segment with the corresponding deserialize method.
    */
-  public serialize(): SerializedCubic {
+  public override serialize(): SerializedCubic {
     return {
       type: 'Cubic',
       startX: this._start.x,
Index: js/ops/Boundary.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/ops/Boundary.js b/js/ops/Boundary.ts
rename from js/ops/Boundary.js
rename to js/ops/Boundary.ts
--- a/js/ops/Boundary.js	(revision 36eadc34844462641aa8b8dc8f7bae5262e27f21)
+++ b/js/ops/Boundary.ts	(date 1677785386280)
@@ -7,27 +7,40 @@
  * @author Jonathan Olson <[email protected]>
  */
 
-import Bounds2 from '../../../dot/js/Bounds2.js';
+import Bounds2, { Bounds2StateObject } from '../../../dot/js/Bounds2.js';
 import Ray2 from '../../../dot/js/Ray2.js';
+import Transform3 from '../../../dot/js/Transform3.js';
 import Vector2 from '../../../dot/js/Vector2.js';
 import cleanArray from '../../../phet-core/js/cleanArray.js';
 import Pool from '../../../phet-core/js/Pool.js';
-import { kite, Subpath } from '../imports.js';
+import { ActiveHalfEdge, kite, Subpath } from '../imports.js';
 
 let globaId = 0;
 
+export type SerializedBoundary = {
+  type: 'Boundary';
+  id: number;
+  halfEdges: number[]; // half-edge IDs
+  signedArea: number;
+  bounds: Bounds2StateObject;
+  childBoundaries: number[]; // boundary IDs
+};
+
 class Boundary {
+
+  public readonly id = ++globaId;
+
+  public halfEdges: ActiveHalfEdge[] = [];
+  public signedArea!: number;
+  public bounds!: Bounds2;
+  public childBoundaries: Boundary[] = [];
+
   /**
-   * @public (kite-internal)
+   * (kite-internal)
    *
    * NOTE: Use Boundary.pool.create for most usage instead of using the constructor directly.
-   *
-   * @param {Array.<HalfEdge>} halfEdges
    */
-  constructor( halfEdges ) {
-    // @public {number}
-    this.id = ++globaId;
-
+  public constructor( halfEdges: ActiveHalfEdge[] ) {
     // NOTE: most object properties are declared/documented in the initialize method. Please look there for most
     // definitions.
     this.initialize( halfEdges );
@@ -36,22 +49,11 @@
   /**
    * Similar to a usual constructor, but is set up so it can be called multiple times (with dispose() in-between) to
    * support pooling.
-   * @private
-   *
-   * @param {Array.<HalfEdge>} halfEdges
-   * @returns {Boundary} - This reference for chaining
    */
-  initialize( halfEdges ) {
-    // @public {Array.<HalfEdge>}
+  public initialize( halfEdges: ActiveHalfEdge[] ): this {
     this.halfEdges = halfEdges;
-
-    // @public {number}
     this.signedArea = this.computeSignedArea();
-
-    // @public {Bounds2}
     this.bounds = this.computeBounds();
-
-    // @public {Array.<Boundary>}
     this.childBoundaries = cleanArray( this.childBoundaries );
 
     return this;
@@ -59,11 +61,8 @@
 
   /**
    * Returns an object form that can be turned back into a segment with the corresponding deserialize method.
-   * @public
-   *
-   * @returns {Object}
    */
-  serialize() {
+  public serialize(): SerializedBoundary {
     return {
       type: 'Boundary',
       id: this.id,
@@ -77,9 +76,8 @@
   /**
    * Removes references (so it can allow other objects to be GC'ed or pooled), and frees itself to the pool so it
    * can be reused.
-   * @public
    */
-  dispose() {
+  public dispose(): void {
     this.halfEdges = [];
     cleanArray( this.childBoundaries );
     this.freeToPool();
@@ -88,26 +86,20 @@
   /**
    * Returns whether this boundary is essentially "counter-clockwise" (in the non-reversed coordinate system) with
    * positive signed area, or "clockwise" with negative signed area.
-   * @public
    *
    * Boundaries are treated as "inner" boundaries when they are counter-clockwise, as the path followed will generally
    * follow the inside of a face (given how the "next" edge of a vertex is computed).
-   *
-   * @returns {number}
    */
-  isInner() {
+  public isInner(): boolean {
     return this.signedArea > 0;
   }
 
   /**
    * Returns the signed area of this boundary, given its half edges.
-   * @public
    *
    * Each half-edge has its own contribution to the signed area, which are summed together.
-   *
-   * @returns {number}
    */
-  computeSignedArea() {
+  public computeSignedArea(): number {
     let signedArea = 0;
     for ( let i = 0; i < this.halfEdges.length; i++ ) {
       signedArea += this.halfEdges[ i ].signedAreaFragment;
@@ -117,11 +109,8 @@
 
   /**
    * Returns the bounds of the boundary (the union of each of the boundary's segments' bounds).
-   * @public
-   *
-   * @returns {Bounds2}
    */
-  computeBounds() {
+  public computeBounds(): Bounds2 {
     const bounds = Bounds2.NOTHING.copy();
 
     for ( let i = 0; i < this.halfEdges.length; i++ ) {
@@ -133,16 +122,14 @@
   /**
    * Returns a point on the boundary which, when the shape (and point) are transformed with the given transform, would
    * be a point with the minimal y value.
-   * @public
    *
    * Will only return one point, even if there are multiple points that have the same minimal y values for the
    * boundary. The point may be at the end of one of the edges/segments (at a vertex), but also may somewhere in the
    * middle of an edge/segment.
    *
-   * @param {Transform3} transform - Transform used because we want the inverse also.
-   * @returns {Vector2}
+   * @param  transform - Transform used because we want the inverse also.
    */
-  computeExtremePoint( transform ) {
+  public computeExtremePoint( transform: Transform3 ): Vector2 {
     assert && assert( this.halfEdges.length > 0, 'There is no extreme point if we have no edges' );
 
     // Transform all of the segments into the new transformed coordinate space.
@@ -187,7 +174,6 @@
   /**
    * Returns a ray (position and direction) pointing away from our boundary at an "extreme" point, so that the ray
    * will be guaranteed not to intersect this boundary.
-   * @public
    *
    * The ray's position will be slightly offset from the boundary, so that it will not technically intersect the
    * boundary where the extreme point lies. The extreme point will be chosen such that it would have the smallest
@@ -197,11 +183,8 @@
    * in the negative-y direction (e.g. a vector of (0,-1)). This should guarantee it is facing away from the
    * boundary, and will be consistent in direction with other extreme rays (needed for its use case with the
    * boundary graph).
-   *
-   * @param {Transform3} transform
-   * @returns {Ray2}
    */
-  computeExtremeRay( transform ) {
+  public computeExtremeRay( transform: Transform3 ): Ray2 {
     const extremePoint = this.computeExtremePoint( transform );
     const orientation = transform.inverseDelta2( new Vector2( 0, -1 ) ).normalized();
     return new Ray2( extremePoint.plus( orientation.timesScalar( 1e-4 ) ), orientation );
@@ -209,12 +192,8 @@
 
   /**
    * Returns whether this boundary includes the specified half-edge.
-   * @public
-   *
-   * @param {HalfEdge} halfEdge
-   * @returns {boolean}
    */
-  hasHalfEdge( halfEdge ) {
+  public hasHalfEdge( halfEdge: ActiveHalfEdge ): boolean {
     for ( let i = 0; i < this.halfEdges.length; i++ ) {
       if ( this.halfEdges[ i ] === halfEdge ) {
         return true;
@@ -225,11 +204,8 @@
 
   /**
    * Converts this boundary to a Subpath, so that we can construct things like Shape objects from it.
-   * @public
-   *
-   * @returns {Subpath}
    */
-  toSubpath() {
+  public toSubpath(): Subpath {
     const segments = [];
     for ( let i = 0; i < this.halfEdges.length; i++ ) {
       segments.push( this.halfEdges[ i ].getDirectionalSegment() );
@@ -237,13 +213,11 @@
     return new Subpath( segments, null, true );
   }
 
-  // @public
-  freeToPool() {
+  public freeToPool(): void {
     Boundary.pool.freeToPool( this );
   }
 
-  // @public
-  static pool = new Pool( Boundary );
+  public static pool = new Pool( Boundary );
 }
 
 kite.register( 'Boundary', Boundary );
Index: js/ops/HalfEdge.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/ops/HalfEdge.js b/js/ops/HalfEdge.ts
rename from js/ops/HalfEdge.js
rename to js/ops/HalfEdge.ts
--- a/js/ops/HalfEdge.js	(revision 36eadc34844462641aa8b8dc8f7bae5262e27f21)
+++ b/js/ops/HalfEdge.ts	(date 1677785104628)
@@ -7,25 +7,56 @@
  * @author Jonathan Olson <[email protected]>
  */
 
-import Vector2 from '../../../dot/js/Vector2.js';
+import Vector2, { Vector2StateObject } from '../../../dot/js/Vector2.js';
 import Pool from '../../../phet-core/js/Pool.js';
-import { kite } from '../imports.js';
+import IntentionalAny from '../../../phet-core/js/types/IntentionalAny.js';
+import WithoutNull from '../../../phet-core/js/types/WithoutNull.js';
+import { ActiveEdge, Face, kite, Segment, Vertex } from '../imports.js';
 
 let globaId = 0;
 
+export type ActiveHalfEdge = WithoutNull<HalfEdge, 'edge' | 'startVertex' | 'endVertex'>;
+export const isActiveHalfEdge = ( halfEdge: HalfEdge ): halfEdge is ActiveHalfEdge => halfEdge.edge !== null;
+
+type Data = IntentionalAny;
+
+export type SerializedHalfEdge = {
+  type: 'HalfEdge';
+  id: number;
+  edge: number; // edge.id
+  face: null | number; // face.id
+  isReversed: boolean;
+  signedAreaFragment: number;
+  startVertex: number; // startVertex.id
+  endVertex: number; // endVertex.id
+  sortVector: Vector2StateObject;
+  data: Data | null;
+};
+
 class HalfEdge {
+
+  public readonly id = ++globaId;
+
+  public edge!: ActiveEdge | null; // Null if disposed (in pool)
+  public face!: Face | null;
+  public isReversed!: boolean;
+  public signedAreaFragment!: number;
+  public startVertex!: Vertex | null; // Null if disposed (in pool)
+  public endVertex!: Vertex | null; // Null if disposed (in pool)
+
+  // Available for arbitrary client usage. -- Keep JSONable
+  public data!: Data | null;
+
+  // Used for vertex sorting in Vertex.js. X is angle of end tangent (shifted),
+  // Y is curvature at end. See Vertex edge sort for more information.
+  public readonly sortVector = new Vector2( 0, 0 );
+
   /**
-   * @public (kite-internal)
+   * (kite-internal)
    *
    * NOTE: Use HalfEdge.pool.create for most usage instead of using the constructor directly.
-   *
-   * @param {Edge} edge
-   * @param {boolean} isReversed
    */
-  constructor( edge, isReversed ) {
-    // @public {number}
-    this.id = ++globaId;
-
+  public constructor( edge: ActiveEdge, isReversed: boolean ) {
     // NOTE: most object properties are declared/documented in the initialize method. Please look there for most
     // definitions.
     this.initialize( edge, isReversed );
@@ -34,37 +65,14 @@
   /**
    * Similar to a usual constructor, but is set up so it can be called multiple times (with dispose() in-between) to
    * support pooling.
-   * @private
-   *
-   * @param {Edge} edge
-   * @param {boolean} isReversed
-   * @returns {HalfEdge} - This reference for chaining
    */
-  initialize( edge, isReversed ) {
-    assert && assert( edge instanceof kite.Edge );
-    assert && assert( typeof isReversed === 'boolean' );
-
-    // @public {Edge|null} - Null if disposed (in pool)
+  public initialize( edge: ActiveEdge, isReversed: boolean ): this {
     this.edge = edge;
-
-    // @public {Face|null} - Filled in later, contains a face reference
     this.face = null;
-
-    // @public {boolean}
     this.isReversed = isReversed;
-
-    // @public {number}
     this.signedAreaFragment = edge.signedAreaFragment * ( isReversed ? -1 : 1 );
-
-    // @public {Vertex|null}
     this.startVertex = null;
     this.endVertex = null;
-
-    // @public {Vector2} - Used for vertex sorting in Vertex.js. X is angle of end tangent (shifted),
-    // Y is curvature at end. See Vertex edge sort for more information.
-    this.sortVector = this.sortVector || new Vector2( 0, 0 );
-
-    // @public {*} - Available for arbitrary client usage. --- KEEP JSON
     this.data = null;
 
     this.updateReferences(); // Initializes vertex references
@@ -74,31 +82,30 @@
 
   /**
    * Returns an object form that can be turned back into a segment with the corresponding deserialize method.
-   * @public
-   *
-   * @returns {Object}
    */
-  serialize() {
+  public serialize(): SerializedHalfEdge {
+    assert && assert( isActiveHalfEdge( this ) );
+    const activeThis = this as ActiveHalfEdge;
+
     return {
       type: 'HalfEdge',
-      id: this.id,
-      edge: this.edge.id,
-      face: this.face === null ? null : this.face.id,
-      isReversed: this.isReversed,
-      signedAreaFragment: this.signedAreaFragment,
-      startVertex: this.startVertex === null ? null : this.startVertex.id,
-      endVertex: this.endVertex === null ? null : this.endVertex.id,
-      sortVector: Vector2.Vector2IO.toStateObject( this.sortVector ),
-      data: this.data
+      id: activeThis.id,
+      edge: activeThis.edge.id,
+      face: activeThis.face === null ? null : activeThis.face.id,
+      isReversed: activeThis.isReversed,
+      signedAreaFragment: activeThis.signedAreaFragment,
+      startVertex: activeThis.startVertex.id,
+      endVertex: activeThis.endVertex.id,
+      sortVector: Vector2.Vector2IO.toStateObject( activeThis.sortVector ),
+      data: activeThis.data
     };
   }
 
   /**
    * Removes references (so it can allow other objects to be GC'ed or pooled), and frees itself to the pool so it
    * can be reused.
-   * @public
    */
-  dispose() {
+  public dispose(): void {
     this.edge = null;
     this.face = null;
     this.startVertex = null;
@@ -109,100 +116,101 @@
 
   /**
    * Returns the next half-edge, walking around counter-clockwise as possible. Assumes edges have been sorted.
-   * @public
    *
-   * @param {function} [filter] - function( {Edge} ) => {boolean}. If it returns false, the edge will be skipped, and
-   *                              not returned by getNext
+   * @param [filter] - If it returns false, the edge will be skipped, and not returned by getNext
    */
-  getNext( filter ) {
+  public getNext( filter?: ( edge: ActiveEdge ) => boolean ): HalfEdge {
+    assert && assert( isActiveHalfEdge( this ) );
+    const activeThis = this as ActiveHalfEdge;
+
     // Starting at 1, forever incrementing (we will bail out with normal conditions)
     for ( let i = 1; ; i++ ) {
-      let index = this.endVertex.incidentHalfEdges.indexOf( this ) - i;
+      let index = activeThis.endVertex.incidentHalfEdges.indexOf( activeThis ) - i;
       if ( index < 0 ) {
-        index += this.endVertex.incidentHalfEdges.length;
+        index += activeThis.endVertex.incidentHalfEdges.length;
       }
-      const halfEdge = this.endVertex.incidentHalfEdges[ index ].getReversed();
+      const halfEdge = activeThis.endVertex.incidentHalfEdges[ index ].getReversed();
       if ( filter && !filter( halfEdge.edge ) ) {
         continue;
       }
-      assert && assert( this.endVertex === halfEdge.startVertex );
+      assert && assert( activeThis.endVertex === halfEdge.startVertex );
       return halfEdge;
     }
   }
 
   /**
    * Update possibly reversed vertex references.
-   * @private
    */
-  updateReferences() {
-    this.startVertex = this.isReversed ? this.edge.endVertex : this.edge.startVertex;
-    this.endVertex = this.isReversed ? this.edge.startVertex : this.edge.endVertex;
+  public updateReferences(): void {
+    assert && assert( isActiveHalfEdge( this ) );
+    const activeThis = this as ActiveHalfEdge;
+
+    activeThis.startVertex = activeThis.isReversed ? activeThis.edge.endVertex : activeThis.edge.startVertex;
+    activeThis.endVertex = activeThis.isReversed ? activeThis.edge.startVertex : activeThis.edge.endVertex;
     assert && assert( this.startVertex );
     assert && assert( this.endVertex );
   }
 
   /**
    * Returns the tangent of the edge at the end vertex (in the direction away from the vertex).
-   * @public
-   *
-   * @returns {Vector2}
    */
-  getEndTangent() {
+  public getEndTangent(): Vector2 {
+    assert && assert( isActiveHalfEdge( this ) );
+    const activeThis = this as ActiveHalfEdge;
+
     if ( this.isReversed ) {
-      return this.edge.segment.startTangent;
+      return activeThis.edge.segment.startTangent;
     }
     else {
-      return this.edge.segment.endTangent.negated();
+      return activeThis.edge.segment.endTangent.negated();
     }
   }
 
   /**
    * Returns the curvature of the edge at the end vertex.
-   * @public
-   *
-   * @returns {number}
    */
-  getEndCurvature() {
+  public getEndCurvature(): number {
+    assert && assert( isActiveHalfEdge( this ) );
+    const activeThis = this as ActiveHalfEdge;
+
     if ( this.isReversed ) {
-      return -this.edge.segment.curvatureAt( 0 );
+      return -activeThis.edge.segment.curvatureAt( 0 );
     }
     else {
-      return this.edge.segment.curvatureAt( 1 );
+      return activeThis.edge.segment.curvatureAt( 1 );
     }
   }
 
   /**
    * Returns the opposite half-edge for the same edge.
-   * @public
-   *
-   * @returns {HalfEdge}
    */
-  getReversed() {
-    return this.isReversed ? this.edge.forwardHalf : this.edge.reversedHalf;
+  public getReversed(): ActiveHalfEdge {
+    assert && assert( isActiveHalfEdge( this ) );
+    const activeThis = this as ActiveHalfEdge;
+
+    return activeThis.isReversed ? activeThis.edge.forwardHalf : activeThis.edge.reversedHalf;
   }
 
   /**
    * Returns a segment that starts at our startVertex and ends at our endVertex (may be reversed to accomplish that).
-   * @public
-   *
-   * @returns {Segment}
    */
-  getDirectionalSegment() {
-    if ( this.isReversed ) {
-      return this.edge.segment.reversed();
+  public getDirectionalSegment(): Segment {
+    assert && assert( isActiveHalfEdge( this ) );
+    const activeThis = this as ActiveHalfEdge;
+
+    if ( activeThis.isReversed ) {
+      return activeThis.edge.segment.reversed();
     }
     else {
-      return this.edge.segment;
+      return activeThis.edge.segment;
     }
   }
 
-  // @public
-  freeToPool() {
+  public freeToPool(): void {
     HalfEdge.pool.freeToPool( this );
   }
 
-  // @public
-  static pool = new Pool( HalfEdge );
+  public static pool = new Pool( HalfEdge );
 }
 
 kite.register( 'HalfEdge', HalfEdge );
Index: js/ops/Loop.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/ops/Loop.js b/js/ops/Loop.ts
rename from js/ops/Loop.js
rename to js/ops/Loop.ts
--- a/js/ops/Loop.js	(revision 36eadc34844462641aa8b8dc8f7bae5262e27f21)
+++ b/js/ops/Loop.ts	(date 1677784670670)
@@ -16,22 +16,32 @@
 
 import cleanArray from '../../../phet-core/js/cleanArray.js';
 import Pool from '../../../phet-core/js/Pool.js';
-import { kite, Subpath } from '../imports.js';
+import { ActiveHalfEdge, kite, Subpath } from '../imports.js';
 
 let globaId = 0;
 
+export type SerializedLoop = {
+  type: 'Loop';
+  id: number;
+  shapeId: number;
+  closed: boolean;
+  halfEdges: number[]; // half-edge IDs
+};
+
 class Loop {
+
+  public readonly id = ++globaId;
+
+  public shapeId!: number;
+  public closed!: boolean;
+  public halfEdges: ActiveHalfEdge[] = [];
+
   /**
-   * @public (kite-internal)
+   * (kite-internal)
    *
    * NOTE: Use Loop.pool.create for most usage instead of using the constructor directly.
-   *
-   * @param {number} shapeId
-   * @param {boolean} closed
    */
-  constructor( shapeId, closed ) {
-    // @public {number}
-    this.id = ++globaId;
+  public constructor( shapeId: number, closed: boolean ) {
 
     // NOTE: most object properties are declared/documented in the initialize method. Please look there for most
     // definitions.
@@ -41,23 +51,10 @@
   /**
    * Similar to a usual constructor, but is set up so it can be called multiple times (with dispose() in-between) to
    * support pooling.
-   * @private
-   *
-   * @param {number} shapeId
-   * @param {boolean} closed
-   * @returns {Loop} - This reference for chaining
    */
-  initialize( shapeId, closed ) {
-    assert && assert( typeof shapeId === 'number' );
-    assert && assert( typeof closed === 'boolean' );
-
-    // @public {number}
+  public initialize( shapeId: number, closed: boolean ): this {
     this.shapeId = shapeId;
-
-    // @public {boolean}
     this.closed = closed;
-
-    // @public {Array.<HalfEdge>}
     this.halfEdges = cleanArray( this.halfEdges );
 
     return this;
@@ -65,11 +62,8 @@
 
   /**
    * Returns an object form that can be turned back into a segment with the corresponding deserialize method.
-   * @public
-   *
-   * @returns {Object}
    */
-  serialize() {
+  public serialize(): SerializedLoop {
     return {
       type: 'Loop',
       id: this.id,
@@ -81,11 +75,8 @@
 
   /**
    * Returns a Subpath equivalent to this loop.
-   * @public
-   *
-   * @returns {Subpath}
    */
-  toSubpath() {
+  public toSubpath(): Subpath {
     const segments = [];
     for ( let i = 0; i < this.halfEdges.length; i++ ) {
       segments.push( this.halfEdges[ i ].getDirectionalSegment() );
@@ -96,20 +87,17 @@
   /**
    * Removes references (so it can allow other objects to be GC'ed or pooled), and frees itself to the pool so it
    * can be reused.
-   * @public
    */
-  dispose() {
+  public dispose(): void {
     cleanArray( this.halfEdges );
     this.freeToPool();
   }
 
-  // @public
-  freeToPool() {
+  public freeToPool(): void {
     Loop.pool.freeToPool( this );
   }
 
-  // @public
-  static pool = new Pool( Loop );
+  public static pool = new Pool( Loop );
 }
 
 kite.register( 'Loop', Loop );
Index: js/util/Subpath.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/util/Subpath.js b/js/util/Subpath.js
--- a/js/util/Subpath.js	(revision 36eadc34844462641aa8b8dc8f7bae5262e27f21)
+++ b/js/util/Subpath.js	(date 1677785384207)
@@ -21,7 +21,7 @@
    * NOTE: No arguments required (they are usually used for copy() usage or creation with new segments)
    *
    * @param {Array.<Segment>} [segments]
-   * @param {Array.<Vector2>} [points]
+   * @param {Array.<Vector2> | null} [points]
    * @param {boolean} [closed]
    */
   constructor( segments, points, closed ) {
Index: js/ops/Edge.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/ops/Edge.js b/js/ops/Edge.ts
rename from js/ops/Edge.js
rename to js/ops/Edge.ts
--- a/js/ops/Edge.js	(revision 36eadc34844462641aa8b8dc8f7bae5262e27f21)
+++ b/js/ops/Edge.ts	(date 1677785178722)
@@ -7,24 +7,61 @@
  */
 
 import Pool from '../../../phet-core/js/Pool.js';
-import { HalfEdge, kite, Line, Segment, Vertex } from '../imports.js';
+import IntentionalAny from '../../../phet-core/js/types/IntentionalAny.js';
+import WithoutNull from '../../../phet-core/js/types/WithoutNull.js';
+import { ActiveHalfEdge, HalfEdge, kite, Line, Segment, SerializedHalfEdge, SerializedSegment, Vertex } from '../imports.js';
 
 let globaId = 0;
 
+export type ActiveEdge = WithoutNull<Edge, 'segment' | 'startVertex' | 'endVertex' | 'forwardHalf' | 'reversedHalf'>;
+export const isActiveEdge = ( edge: Edge ): edge is ActiveEdge => edge.segment !== null;
+
+export type EdgeInternalData = {
+  removedId?: number;
+  segmentId?: number;
+};
+
+type Data = IntentionalAny;
+
+export type SerializedEdge = {
+  type: 'Edge';
+  id: number;
+  segment: SerializedSegment;
+  startVertex: number;
+  endVertex: number;
+  signedAreaFragment: number;
+  forwardHalf: SerializedHalfEdge;
+  reversedHalf: SerializedHalfEdge;
+  visited: boolean;
+  data: Data | null;
+};
+
 class Edge {
+
+  public readonly id = ++globaId;
+
+  public segment!: Segment | null; // Null if disposed (in pool)
+  public startVertex!: Vertex | null; // Null if disposed (in pool)
+  public endVertex!: Vertex | null; // Null if disposed (in pool)
+  public signedAreaFragment!: number;
+  public forwardHalf!: ActiveHalfEdge | null; // Null if disposed (in pool)
+  public reversedHalf!: ActiveHalfEdge | null; // Null if disposed (in pool)
+
+  // Used for depth-first search
+  public visited!: boolean;
+
+  // Available for arbitrary client usage. -- Keep JSONable
+  public data!: Data | null;
+
+  // (kite-internal)
+  public internalData!: EdgeInternalData;
+
   /**
-   * @public (kite-internal)
+   * (kite-internal)
    *
    * NOTE: Use Edge.pool.create for most usage instead of using the constructor directly.
-   *
-   * @param {Segment} segment
-   * @param {Vertex} startVertex
-   * @param {Vertex} endVertex
    */
-  constructor( segment, startVertex, endVertex ) {
-    // @public {number}
-    this.id = ++globaId;
-
+  public constructor( segment: Segment, startVertex: Vertex, endVertex: Vertex ) {
     // NOTE: most object properties are declared/documented in the initialize method. Please look there for most
     // definitions.
     this.initialize( segment, startVertex, endVertex );
@@ -33,81 +70,59 @@
   /**
    * Similar to a usual constructor, but is set up so it can be called multiple times (with dispose() in-between) to
    * support pooling.
-   * @private
-   *
-   * @param {Segment} segment
-   * @param {Vertex} startVertex
-   * @param {Vertex} endVertex
-   * @returns {Edge} - This reference for chaining
    */
-  initialize( segment, startVertex, endVertex ) {
-    assert && assert( segment instanceof Segment );
-    assert && assert( startVertex instanceof Vertex );
-    assert && assert( endVertex instanceof Vertex );
+  public initialize( segment: Segment, startVertex: Vertex, endVertex: Vertex ): this {
     assert && assert( segment.start.distance( startVertex.point ) < 1e-3 );
     assert && assert( segment.end.distance( endVertex.point ) < 1e-3 );
 
-    // @public {Segment|null} - Null when disposed (in pool)
     this.segment = segment;
-
-    // @public {Vertex|null} - Null when disposed (in pool)
     this.startVertex = startVertex;
     this.endVertex = endVertex;
-
-    // @public {number}
     this.signedAreaFragment = segment.getSignedAreaFragment();
-
-    // @public {HalfEdge|null} - Null when disposed (in pool)
-    this.forwardHalf = HalfEdge.pool.create( this, false );
-    this.reversedHalf = HalfEdge.pool.create( this, true );
-
-    // @public {boolean} - Used for depth-first search
+    this.forwardHalf = HalfEdge.pool.create( this as ActiveEdge, false ) as ActiveHalfEdge;
+    this.reversedHalf = HalfEdge.pool.create( this as ActiveEdge, true ) as ActiveHalfEdge;
     this.visited = false;
-
-    // @public {*} - Available for arbitrary client usage. -- Keep JSONable
     this.data = null;
-
-    // @public {*} - kite-internal
-    this.internalData = {
-
-    };
+    this.internalData = {};
 
     return this;
   }
 
   /**
    * Returns an object form that can be turned back into a segment with the corresponding deserialize method.
-   * @public
-   *
-   * @returns {Object}
    */
-  serialize() {
+  public serialize(): SerializedEdge {
+    assert && assert( isActiveEdge( this ) );
+    const activeThis = this as ActiveEdge;
+
     return {
       type: 'Edge',
-      id: this.id,
-      segment: this.segment.serialize(),
-      startVertex: this.startVertex === null ? null : this.startVertex.id,
-      endVertex: this.endVertex === null ? null : this.endVertex.id,
-      signedAreaFragment: this.signedAreaFragment,
-      forwardHalf: this.forwardHalf.serialize(),
-      reversedHalf: this.reversedHalf.serialize(),
-      visited: this.visited,
-      data: this.data
+      id: activeThis.id,
+      segment: activeThis.segment.serialize(),
+      startVertex: activeThis.startVertex.id,
+      endVertex: activeThis.endVertex.id,
+      signedAreaFragment: activeThis.signedAreaFragment,
+      forwardHalf: activeThis.forwardHalf.serialize(),
+      reversedHalf: activeThis.reversedHalf.serialize(),
+      visited: activeThis.visited,
+      data: activeThis.data
     };
   }
 
   /**
    * Removes references (so it can allow other objects to be GC'ed or pooled), and frees itself to the pool so it
    * can be reused.
-   * @public
    */
-  dispose() {
+  public dispose(): void {
+    assert && assert( isActiveEdge( this ) );
+    const activeThis = this as ActiveEdge;
+
     this.segment = null;
     this.startVertex = null;
     this.endVertex = null;
 
-    this.forwardHalf.dispose();
-    this.reversedHalf.dispose();
+    activeThis.forwardHalf.dispose();
+    activeThis.reversedHalf.dispose();
 
     this.forwardHalf = null;
     this.reversedHalf = null;
@@ -119,36 +134,34 @@
 
   /**
    * Returns the other vertex associated with an edge.
-   * @public
-   *
-   * @param {Vertex} vertex
-   * @returns {Vertex}
    */
-  getOtherVertex( vertex ) {
+  public getOtherVertex( vertex: Vertex ): Vertex {
     assert && assert( vertex === this.startVertex || vertex === this.endVertex );
+    assert && assert( isActiveEdge( this ) );
+    const activeThis = this as ActiveEdge;
 
-    return this.startVertex === vertex ? this.endVertex : this.startVertex;
+    return activeThis.startVertex === vertex ? activeThis.endVertex : activeThis.startVertex;
   }
 
   /**
    * Update possibly reversed vertex references (since they may be updated)
-   * @public
    */
-  updateReferences() {
-    this.forwardHalf.updateReferences();
-    this.reversedHalf.updateReferences();
+  public updateReferences(): void {
+    assert && assert( isActiveEdge( this ) );
+    const activeThis = this as ActiveEdge;
 
-    assert && assert( !( this.segment instanceof Line ) || this.startVertex !== this.endVertex,
+    activeThis.forwardHalf.updateReferences();
+    activeThis.reversedHalf.updateReferences();
+
+    assert && assert( !( activeThis.segment instanceof Line ) || activeThis.startVertex !== activeThis.endVertex,
       'No line segments for same vertices' );
   }
 
-  // @public
-  freeToPool() {
+  public freeToPool(): void {
     Edge.pool.freeToPool( this );
   }
 
-  // @public
-  static pool = new Pool( Edge );
+  public static pool = new Pool( Edge );
 }
 
 kite.register( 'Edge', Edge );
Index: js/segments/Quadratic.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/segments/Quadratic.ts b/js/segments/Quadratic.ts
--- a/js/segments/Quadratic.ts	(revision 36eadc34844462641aa8b8dc8f7bae5262e27f21)
+++ b/js/segments/Quadratic.ts	(date 1677784401514)
@@ -24,7 +24,7 @@
   return t >= 0 && t <= 1;
 }
 
-type SerializedQuadratic = {
+export type SerializedQuadratic = {
   type: 'Quadratic';
   startX: number;
   startY: number;
@@ -597,7 +597,7 @@
   /**
    * Returns an object form that can be turned back into a segment with the corresponding deserialize method.
    */
-  public serialize(): SerializedQuadratic {
+  public override serialize(): SerializedQuadratic {
     return {
       type: 'Quadratic',
       startX: this._start.x,
Index: js/segments/Arc.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/segments/Arc.ts b/js/segments/Arc.ts
--- a/js/segments/Arc.ts	(revision 36eadc34844462641aa8b8dc8f7bae5262e27f21)
+++ b/js/segments/Arc.ts	(date 1677784386025)
@@ -16,7 +16,7 @@
 // TODO: See if we should use this more
 const TWO_PI = Math.PI * 2;
 
-type SerializedArc = {
+export type SerializedArc = {
   type: 'Arc';
   centerX: number;
   centerY: number;
@@ -747,7 +747,7 @@
   /**
    * Returns an object form that can be turned back into a segment with the corresponding deserialize method.
    */
-  public serialize(): SerializedArc {
+  public override serialize(): SerializedArc {
     return {
       type: 'Arc',
       centerX: this._center.x,
Index: js/segments/Segment.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/segments/Segment.ts b/js/segments/Segment.ts
--- a/js/segments/Segment.ts	(revision 36eadc34844462641aa8b8dc8f7bae5262e27f21)
+++ b/js/segments/Segment.ts	(date 1677784466793)
@@ -18,7 +18,7 @@
 import Vector2 from '../../../dot/js/Vector2.js';
 import optionize from '../../../phet-core/js/optionize.js';
 import IntentionalAny from '../../../phet-core/js/types/IntentionalAny.js';
-import { Arc, BoundsIntersection, EllipticalArc, kite, Line, RayIntersection, SegmentIntersection, Shape, Subpath } from '../imports.js';
+import { Arc, BoundsIntersection, EllipticalArc, kite, Line, RayIntersection, SegmentIntersection, SerializedArc, SerializedCubic, SerializedEllipticalArc, SerializedLine, SerializedQuadratic, Shape, Subpath } from '../imports.js';
 
 type DashValues = {
 
@@ -47,6 +47,8 @@
   distanceSquared: number;
 };
 
+export type SerializedSegment = SerializedArc | SerializedCubic | SerializedEllipticalArc | SerializedLine | SerializedQuadratic;
+
 export type PiecewiseLinearOptions = {
   // how many levels to force subdivisions
   minLevels?: number;
@@ -768,6 +770,11 @@
     }
     return true;
   }
+
+  /**
+   * Returns an object form that can be turned back into a segment with the corresponding deserialize method.
+   */
+  public abstract serialize(): SerializedSegment;
 }
 
 kite.register( 'Segment', Segment );
Index: js/segments/Line.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/segments/Line.ts b/js/segments/Line.ts
--- a/js/segments/Line.ts	(revision 36eadc34844462641aa8b8dc8f7bae5262e27f21)
+++ b/js/segments/Line.ts	(date 1677784398244)
@@ -15,7 +15,7 @@
 
 const scratchVector2 = new Vector2( 0, 0 );
 
-type SerializedLine = {
+export type SerializedLine = {
   type: 'Line';
   startX: number;
   startY: number;
@@ -446,7 +446,7 @@
   /**
    * Returns an object form that can be turned back into a segment with the corresponding deserialize method.
    */
-  public serialize(): SerializedLine {
+  public override serialize(): SerializedLine {
     return {
       type: 'Line',
       startX: this._start.x,
Index: js/ops/Vertex.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/ops/Vertex.js b/js/ops/Vertex.ts
rename from js/ops/Vertex.js
rename to js/ops/Vertex.ts
--- a/js/ops/Vertex.js	(revision 36eadc34844462641aa8b8dc8f7bae5262e27f21)
+++ b/js/ops/Vertex.ts	(date 1677785845244)
@@ -8,23 +8,57 @@
  * @author Jonathan Olson <[email protected]>
  */
 
-import Vector2 from '../../../dot/js/Vector2.js';
+import Vector2, { Vector2StateObject } from '../../../dot/js/Vector2.js';
 import cleanArray from '../../../phet-core/js/cleanArray.js';
 import Pool from '../../../phet-core/js/Pool.js';
-import { kite, Line } from '../imports.js';
+import { ActiveHalfEdge, kite, Line } from '../imports.js';
 
 let globaId = 0;
 
+export type VertexInternalData = {
+  removedId?: number;
+  segmentId?: number;
+};
+
+export type SerializedVertex = {
+  type: 'Vertex';
+  id: number;
+  point: Vector2StateObject;
+  incidentHalfEdges: number[]; // half-edge IDs
+  visited: boolean;
+  visitIndex: number;
+  lowIndex: number;
+};
+
 class Vertex {
+
+  public readonly id = ++globaId;
+
+  public point!: Vector2;
+
+  // Records the half-edge that points to (ends at) this vertex.
+  public incidentHalfEdges: ActiveHalfEdge[] = [];
+
+  // Used for depth-first search
+  public visited!: boolean;
+
+  // Visit index for bridge detection (more efficient to have inline here)
+  public visitIndex!: number;
+
+  // Low index for bridge detection (more efficient to have inline here)
+  public lowIndex!: number;
+
+  // (kite-internal)
+  public internalData!: VertexInternalData;
+
   /**
-   * @public (kite-internal)
+   * (kite-internal)
    *
    * NOTE: Use Vertex.pool.create for most usage instead of using the constructor directly.
    *
-   * @param {Vector2} point - The point where the vertex should be located.
+   * @param point - The point where the vertex should be located.
    */
-  constructor( point ) {
-    // @public {number}
+  public constructor( point: Vector2 ) {
     this.id = ++globaId;
 
     // NOTE: most object properties are declared/documented in the initialize method. Please look there for most
@@ -35,47 +69,22 @@
   /**
    * Similar to a usual constructor, but is set up so it can be called multiple times (with dispose() in-between) to
    * support pooling.
-   * @private
-   *
-   * @param {Vector2} point
-   * @returns {Vertex} - This reference for chaining
    */
-  initialize( point ) {
-    assert && assert( point instanceof Vector2 );
-
-    // @public {Vector2}
+  public initialize( point: Vector2 ): this {
     this.point = point;
-
-    // @public {Array.<HalfEdge>} - Records the half-edge that points to (ends at) this vertex.
     this.incidentHalfEdges = cleanArray( this.incidentHalfEdges );
-
-    // @public {boolean} - Used for depth-first search
     this.visited = false;
-
-    // @public {number} - Visit index for bridge detection (more efficient to have inline here)
     this.visitIndex = 0;
-
-    // @public {number} - Low index for bridge detection (more efficient to have inline here)
     this.lowIndex = 0;
-
-    // @public {*} - Available for arbitrary client usage. -- Keep JSONable
-    this.data = null;
-
-    // @public {*} - kite-internal
-    this.internalData = {
-
-    };
+    this.internalData = {};
 
     return this;
   }
 
   /**
    * Returns an object form that can be turned back into a segment with the corresponding deserialize method.
-   * @public
-   *
-   * @returns {Object}
    */
-  serialize() {
+  public serialize(): SerializedVertex {
     return {
       type: 'Vertex',
       id: this.id,
@@ -90,9 +99,8 @@
   /**
    * Removes references (so it can allow other objects to be GC'ed or pooled), and frees itself to the pool so it
    * can be reused.
-   * @public
    */
-  dispose() {
+  public dispose(): void {
     this.point = Vector2.ZERO;
     cleanArray( this.incidentHalfEdges );
     this.freeToPool();
@@ -100,9 +108,8 @@
 
   /**
    * Sorts the edges in increasing angle order.
-   * @public
    */
-  sortEdges() {
+  public sortEdges(): void {
     const vectors = []; // x coordinate will be "angle", y coordinate will be curvature
     for ( let i = 0; i < this.incidentHalfEdges.length; i++ ) {
       const halfEdge = this.incidentHalfEdges[ i ];
@@ -138,13 +145,8 @@
   /**
    * Compare two edges for sortEdges. Should have executed that first, as it relies on information looked up in that
    * process.
-   * @public
-   *
-   * @param {Edge} halfEdgeA
-   * @param {Edge} halfEdgeB
-   * @returns {number}
    */
-  static edgeComparison( halfEdgeA, halfEdgeB ) {
+  public static edgeComparison( halfEdgeA: ActiveHalfEdge, halfEdgeB: ActiveHalfEdge ): number {
     const angleA = halfEdgeA.sortVector.x;
     const angleB = halfEdgeB.sortVector.x;
 
@@ -167,13 +169,11 @@
     }
   }
 
-  // @public
-  freeToPool() {
+  public freeToPool(): void {
     Vertex.pool.freeToPool( this );
   }
 
-  // @public
-  static pool = new Pool( Vertex );
+  public static pool = new Pool( Vertex );
 }
 
 kite.register( 'Vertex', Vertex );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant