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:
```diff
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
*/
-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.} 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.} halfEdges
- * @returns {Boundary} - This reference for chaining
*/
- initialize( halfEdges ) {
- // @public {Array.}
+ public initialize( halfEdges: ActiveHalfEdge[] ): this {
this.halfEdges = halfEdges;
-
- // @public {number}
this.signedArea = this.computeSignedArea();
-
- // @public {Bounds2}
this.bounds = this.computeBounds();
-
- // @public {Array.}
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
*/
-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;
+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.}
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.} [segments]
- * @param {Array.} [points]
+ * @param {Array. | 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;
+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
*/
-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.} - 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 );
```
There are an assortment of issues converting kite internals to TS, notably:
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: