diff --git a/src/diagram.ts b/src/diagram.ts index d93b80b..e7f5e31 100644 --- a/src/diagram.ts +++ b/src/diagram.ts @@ -1,286 +1,5 @@ -import * as THREE from "three"; -import { Animation } from "./animation/index.js"; -import * as Geometry from "./geometry/index.js"; -import * as Utils from "./utils.js"; -import * as Text from "./text.js"; -import Angle from "./angle.js"; -import { Shape } from "./geometry/index.js"; - -interface IndicatorConfig { - tickLength?: number; -} - -class Indicator extends THREE.Group { - public startTick: Geometry.Line; - public endTick: Geometry.Line; - public stem; - - constructor( - public start: THREE.Vector3, - public end: THREE.Vector3, - config: IndicatorConfig & Geometry.Style = {}, - ) { - const { tickLength = 0.4 } = config; - - super(); - this.stem = Geometry.Line.centeredLine(start, end, config); - - const tickVector = new THREE.Vector3() - .subVectors(end, start) - .normalize() - .applyAxisAngle(Utils.OUT, Math.PI / 2) - .multiplyScalar(tickLength / 2); - const negativeTickVector = tickVector.clone().multiplyScalar(-1); - - this.startTick = Geometry.Line.centeredLine( - new THREE.Vector3().addVectors(start, tickVector), - new THREE.Vector3().addVectors(start, negativeTickVector), - config, - ); - - this.endTick = Geometry.Line.centeredLine( - new THREE.Vector3().addVectors(end, tickVector), - new THREE.Vector3().addVectors(end, negativeTickVector), - config, - ); - - const center = new THREE.Vector3().addVectors(start, end).divideScalar(2); - for (const mesh of [this.stem, this.startTick, this.endTick]) { - mesh.position.sub(center); - this.add(mesh); - } - this.position.copy(center); - } - - grow(config?): Animation { - const vec = new THREE.Vector3().subVectors(this.end, this.start); - this.startTick.position.set(0, 0, 0); - this.endTick.position.set(0, 0, 0); - this.stem.stroke.material.uniforms.drawRange.value.set(0.5, 0.5); - return new Animation( - (elapsedTime: number) => { - const halfTime = elapsedTime / 2; - this.stem.stroke.material.uniforms.drawRange.value.set( - 0.5 - halfTime, - 0.5 + halfTime, - ); - this.startTick.position.set(0, 0, 0).addScaledVector(vec, halfTime); - this.endTick.position.set(0, 0, 0).addScaledVector(vec, -halfTime); - }, - { object: this, ...config }, - ); - } -} - -class CongruentLine extends THREE.Group { - constructor( - ticks: number, - start: THREE.Vector3, - end: THREE.Vector3, - config: Geometry.Style & { - tickLength?: number; - spacing?: number; - } = {}, - ) { - config = Object.assign({ tickLength: 0.25, spacing: 0.15 }, config); - super(); - const left = -(config.spacing * (ticks - 1)) / 2; - for (let i = 0; i < ticks; i++) { - const pos = left + config.spacing * i; - const tick = new Geometry.Line( - new THREE.Vector3(pos, -config.tickLength / 2, 0), - new THREE.Vector3(pos, config.tickLength / 2, 0), - config, - ); - this.add(tick); - } - - this.moveToSegment(start, end); - } - - moveToSegment(start: THREE.Vector3, end: THREE.Vector3) { - const center = new THREE.Vector3().addVectors(start, end).divideScalar(2); - this.position.copy(center); - - const segmentVector = new THREE.Vector3().subVectors(end, start); - this.rotation.z = Math.atan2(segmentVector.y, segmentVector.x); - } -} - -class CongruentAngle extends Shape { - constructor( - public arcs: number, - public point1: THREE.Vector3, - public point2: THREE.Vector3, - public point3: THREE.Vector3, - public config: Geometry.Style & { - minRadius?: number; - spacing?: number; - } = {}, - ) { - config = { - minRadius: 0.4, - spacing: 0.15, - ...config, - }; - - super(); - - this.intrinsicChildren = new THREE.Group(); - for (let i = 0; i < arcs; i++) { - const arc = new Angle(point1, point2, point3, { - radius: config.minRadius + i * config.spacing, - ...config, - }); - this.intrinsicChildren.add(arc); - } - - this.add(this.intrinsicChildren); - } - - getAttributes() { - return { - arcs: this.arcs, - point1: this.point1, - point2: this.point2, - point3: this.point3, - }; - } -} - -class RightAngle extends Geometry.Polyline { - constructor( - point1: THREE.Vector3, - point2: THREE.Vector3, - point3: THREE.Vector3, - config: Geometry.Style & { sideLength?: number } = {}, - ) { - config = { sideLength: 0.35, ...config }; - const vector21 = new THREE.Vector3() - .subVectors(point1, point2) - .setLength(config.sideLength); - const vector23 = new THREE.Vector3() - .subVectors(point3, point2) - .setLength(config.sideLength); - - super( - [ - new THREE.Vector3().addVectors(point2, vector21), - new THREE.Vector3().add(point2).add(vector21).add(vector23), - new THREE.Vector3().addVectors(point2, vector23), - ], - config, - ); - } -} - -class Number extends THREE.Group { - static geometries = Number.initializeGeometries(); - meshes: THREE.Mesh[] = []; - material = new THREE.MeshBasicMaterial({ color: "black" }); - decimals: number; - centerData = { - center: new THREE.Vector3(), - box: new THREE.Box3(), - offset: new THREE.Vector3(), - worldPosition: new THREE.Vector3(), - }; - - constructor( - value = 0, - config: { color?: THREE.ColorRepresentation; decimals?: number } = {}, - ) { - const fullConfig = { - color: config.color ?? "black", - decimals: config.decimals ?? 2, - }; - - super(); - this.material.color = new THREE.Color(fullConfig.color); - this.decimals = fullConfig.decimals; - this.scale.set(0.0008, -0.0008, 0.0008); - this.updateFromValue(value); - } - - reshape( - value: number, - config: { color?: THREE.ColorRepresentation; decimals?: number } = {}, - ) { - const fullConfig = { - color: config.color ?? "black", - decimals: config.decimals ?? 2, - }; - this.material.color = new THREE.Color(fullConfig.color); - this.decimals = fullConfig.decimals; - this.clear(); - this.updateFromValue(value); - } - - updateFromValue(value: number) { - const characters = value.toFixed(this.decimals).split(""); - for (let i = 0; i < characters.length; i++) { - const character = characters[i]; - - if (character === undefined) { - throw new Error(`No character at index ${i}`); - } - - const geometry = Number.geometries.get(character); - if (geometry === undefined) { - throw new Error(`Character ${character} isn't supported in Number.`); - } - - let mesh = this.meshes[i]; - if (mesh !== undefined) { - mesh.geometry = geometry; - } else { - mesh = new THREE.Mesh(geometry, this.material); - this.meshes.push(mesh); - } - this.add(mesh); - } - - for (let i = 1; i < this.children.length; i++) { - const previousChild = this.children[i - 1]; - const currentChild = this.children[i]; - currentChild.moveNextTo(previousChild, Utils.RIGHT, 0.025); - } - - this.centerData.worldPosition.copy(this.position); - this.localToWorld(this.centerData.worldPosition); - - this.centerData.box.setFromObject(this).getCenter(this.centerData.center); - this.centerData.center.y *= -1; - - this.centerData.offset - .subVectors(this.centerData.worldPosition, this.centerData.center) - .multiplyScalar(1 / 0.0008); - - this.children.forEach((child) => - child.position.add(this.centerData.offset), - ); - } - - static extractGeometry(textShape: Text.Text) { - return textShape.children[0].children[0].children[0] - .geometry as THREE.ShapeGeometry; - } - - static initializeGeometries() { - const geometryMap = new Map(); - - for (let i = 0; i < 10; i++) { - const numberShape = new Text.Text(i.toString()); - const numberGeometry = Number.extractGeometry(numberShape); - geometryMap.set(i.toString(), numberGeometry); - } - - const decimalShape = new Text.Text("."); - const decimalGeometry = Number.extractGeometry(decimalShape); - geometryMap.set(".", decimalGeometry); - - return geometryMap; - } -} - -export { Indicator, Angle, RightAngle, CongruentLine, CongruentAngle, Number }; +/** + * This module provides various diagrammatic elements for use with Three.js. + * It re-exports classes that have been refactored into the src/diagram/ directory. + */ +export * from "./diagram/index.js"; diff --git a/src/diagram/CongruentAngle.ts b/src/diagram/CongruentAngle.ts index 142ecab..b7410a1 100644 --- a/src/diagram/CongruentAngle.ts +++ b/src/diagram/CongruentAngle.ts @@ -1,31 +1,36 @@ import * as THREE from "three"; import * as Geometry from "../geometry/index.js"; import Angle from "./Angle.js"; -import Shape from "../geometry/Shape.js"; +import { Shape } from "../geometry/index.js"; -export default class CongruentAngle extends Shape { +// Define config interface combining Style and specific properties +interface CongruentAngleConfig extends Geometry.Style { + minRadius?: number; + spacing?: number; +} + +// Define the CongruentAngle class +class CongruentAngle extends Shape { constructor( public arcs: number, public point1: THREE.Vector3, public point2: THREE.Vector3, public point3: THREE.Vector3, - public config: Geometry.Style & { - minRadius?: number; - spacing?: number; - } = {}, + config: CongruentAngleConfig = {}, ) { - config = { - minRadius: 0.4, - spacing: 0.15, - ...config, - }; - - super(); + // Resolve defaults for properties used locally + const minRadius = config.minRadius ?? 0.4; + const spacing = config.spacing ?? 0.15; + // Pass the original config to super, disabling stroke/fill for the container + super([], { stroke: false, fill: false, ...config }); + // Create the group for child arcs this.intrinsicChildren = new THREE.Group(); for (let i = 0; i < arcs; i++) { + // Use resolved defaults when creating child Angles const arc = new Angle(point1, point2, point3, { - radius: config.minRadius + i * config.spacing, + radius: minRadius + i * spacing, + // Pass the original config down to the child Angles for styling ...config, }); this.intrinsicChildren.add(arc); @@ -34,12 +39,17 @@ export default class CongruentAngle extends Shape { this.add(this.intrinsicChildren); } + // Method to get attributes getAttributes() { return { arcs: this.arcs, point1: this.point1, point2: this.point2, point3: this.point3, + // Note: config is not typically returned in getAttributes unless needed }; } } + +export { CongruentAngle }; // Export the class +export type { CongruentAngleConfig }; // Export the type diff --git a/src/diagram/CongruentLine.ts b/src/diagram/CongruentLine.ts index bda41b8..b086955 100644 --- a/src/diagram/CongruentLine.ts +++ b/src/diagram/CongruentLine.ts @@ -1,38 +1,50 @@ import * as THREE from "three"; import * as Geometry from "../geometry/index.js"; +// Define config interface combining Style and specific properties +interface CongruentLineConfig extends Geometry.Style { + tickLength?: number; + spacing?: number; +} -export default class CongruentLine extends THREE.Group { - constructor( - ticks: number, - start: THREE.Vector3, - end: THREE.Vector3, - config: Geometry.Style & { - tickLength?: number; - spacing?: number; - } = {}, - ) { - config = Object.assign({ tickLength: 0.25, spacing: 0.15 }, config); - super(); - const left = -(config.spacing * (ticks - 1)) / 2; - for (let i = 0; i < ticks; i++) { - const pos = left + config.spacing * i; - const tick = new Geometry.Line( - new THREE.Vector3(pos, -config.tickLength / 2, 0), - new THREE.Vector3(pos, config.tickLength / 2, 0), - config, - ); - this.add(tick); - } - - this.moveToSegment(start, end); - } - - moveToSegment(start: THREE.Vector3, end: THREE.Vector3) { - const center = new THREE.Vector3().addVectors(start, end).divideScalar(2); - this.position.copy(center); - - const segmentVector = new THREE.Vector3().subVectors(end, start); - this.rotation.z = Math.atan2(segmentVector.y, segmentVector.x); +// Define the CongruentLine class +class CongruentLine extends THREE.Group { + constructor( + ticks: number, + start: THREE.Vector3, + end: THREE.Vector3, + config: CongruentLineConfig = {}, + ) { + // Establish defaults for tickLength and spacing + const tickLength = config.tickLength ?? 0.25; + const spacing = config.spacing ?? 0.15; + // Use a combined config object for passing to Geometry.Line + const lineConfig = { ...config, tickLength, spacing }; + + super(); + const left = -(spacing * (ticks - 1)) / 2; + for (let i = 0; i < ticks; i++) { + const pos = left + spacing * i; + const tick = new Geometry.Line( + new THREE.Vector3(pos, -tickLength / 2, 0), + new THREE.Vector3(pos, tickLength / 2, 0), + lineConfig, // Pass the merged config + ); + this.add(tick); } - } \ No newline at end of file + + this.moveToSegment(start, end); + } + + // Move the group to align with the segment defined by start and end points + moveToSegment(start: THREE.Vector3, end: THREE.Vector3): void { + const center = new THREE.Vector3().addVectors(start, end).divideScalar(2); + this.position.copy(center); + + const segmentVector = new THREE.Vector3().subVectors(end, start); + this.rotation.z = Math.atan2(segmentVector.y, segmentVector.x); + } +} + +export { CongruentLine }; // Export the class +export type { CongruentLineConfig }; // Export the type \ No newline at end of file diff --git a/src/diagram/Indicator.ts b/src/diagram/Indicator.ts index b71da02..2a4f7c2 100644 --- a/src/diagram/Indicator.ts +++ b/src/diagram/Indicator.ts @@ -1,69 +1,83 @@ import * as THREE from "three"; +import { Animation } from "../animation/index.js"; import * as Geometry from "../geometry/index.js"; import * as Utils from "../utils.js"; -import { Animation } from "../animation/index.js"; +// Define config interface for Indicator interface IndicatorConfig { tickLength?: number; } -export default class Indicator extends THREE.Group { - public startTick: Geometry.Line; - public endTick: Geometry.Line; - public stem; - - constructor( - public start: THREE.Vector3, - public end: THREE.Vector3, - config: IndicatorConfig & Geometry.Style = {}, - ) { - const { tickLength = 0.4 } = config; - - super(); - this.stem = Geometry.Line.centeredLine(start, end, config); - - const tickVector = new THREE.Vector3() - .subVectors(end, start) - .normalize() - .applyAxisAngle(Utils.OUT, Math.PI / 2) - .multiplyScalar(tickLength / 2); - const negativeTickVector = tickVector.clone().multiplyScalar(-1); - - this.startTick = Geometry.Line.centeredLine( - new THREE.Vector3().addVectors(start, tickVector), - new THREE.Vector3().addVectors(start, negativeTickVector), - config, - ); - - this.endTick = Geometry.Line.centeredLine( - new THREE.Vector3().addVectors(end, tickVector), - new THREE.Vector3().addVectors(end, negativeTickVector), - config, - ); - - const center = new THREE.Vector3().addVectors(start, end).divideScalar(2); - for (const mesh of [this.stem, this.startTick, this.endTick]) { - mesh.position.sub(center); - this.add(mesh); - } - this.position.copy(center); +// Define the Indicator class +class Indicator extends THREE.Group { + public startTick: Geometry.Line; + public endTick: Geometry.Line; + public stem: Geometry.Line; // Assuming Geometry.Line creates a stroke + + constructor( + public start: THREE.Vector3, + public end: THREE.Vector3, + config: IndicatorConfig & Geometry.Style = {}, + ) { + const { tickLength = 0.4 } = config; + + super(); + this.stem = Geometry.Line.centeredLine(start, end, config); + + const tickVector = new THREE.Vector3() + .subVectors(end, start) + .normalize() + .applyAxisAngle(Utils.OUT, Math.PI / 2) + .multiplyScalar(tickLength / 2); + const negativeTickVector = tickVector.clone().multiplyScalar(-1); + + this.startTick = Geometry.Line.centeredLine( + new THREE.Vector3().addVectors(start, tickVector), + new THREE.Vector3().addVectors(start, negativeTickVector), + config, + ); + + this.endTick = Geometry.Line.centeredLine( + new THREE.Vector3().addVectors(end, tickVector), + new THREE.Vector3().addVectors(end, negativeTickVector), + config, + ); + + const center = new THREE.Vector3().addVectors(start, end).divideScalar(2); + for (const mesh of [this.stem, this.startTick, this.endTick]) { + mesh.position.sub(center); + this.add(mesh); + } + this.position.copy(center); + } + + // Grow animation for the indicator + grow(config?: ConstructorParameters[1]): Animation { + const vec = new THREE.Vector3().subVectors(this.end, this.start); + this.startTick.position.set(0, 0, 0); + this.endTick.position.set(0, 0, 0); + const drawRange = (this.stem?.stroke?.material as THREE.ShaderMaterial | undefined)?.uniforms?.drawRange?.value; + if (drawRange) { + drawRange.set(0.5, 0.5); } - grow(config?): Animation { - const vec = new THREE.Vector3().subVectors(this.end, this.start); - this.startTick.position.set(0, 0, 0); - this.endTick.position.set(0, 0, 0); - this.stem.stroke.material.uniforms.drawRange.value.set(0.5, 0.5); - return new Animation( - (elapsedTime: number) => { - const halfTime = elapsedTime / 2; - this.stem.stroke.material.uniforms.drawRange.value.set( + + return new Animation( + (elapsedTime: number) => { + const halfTime = elapsedTime / 2; + const currentDrawRange = (this.stem?.stroke?.material as THREE.ShaderMaterial | undefined)?.uniforms?.drawRange?.value; + if (currentDrawRange) { + currentDrawRange.set( 0.5 - halfTime, 0.5 + halfTime, ); - this.startTick.position.set(0, 0, 0).addScaledVector(vec, halfTime); - this.endTick.position.set(0, 0, 0).addScaledVector(vec, -halfTime); - }, - { object: this, ...config }, - ); - } - } + } + this.startTick.position.set(0, 0, 0).addScaledVector(vec, halfTime); + this.endTick.position.set(0, 0, 0).addScaledVector(vec, -halfTime); + }, + { object: this, ...config }, + ); + } +} + +export { Indicator }; // Export the class +export type { IndicatorConfig }; // Export the type separately diff --git a/src/diagram/Number.ts b/src/diagram/Number.ts index 7e17662..7e7eda2 100644 --- a/src/diagram/Number.ts +++ b/src/diagram/Number.ts @@ -1,113 +1,166 @@ import * as THREE from "three"; -import * as Text from "../text.js"; import * as Utils from "../utils.js"; +import * as Text from "../text.js"; + +// Define config interface for Number +interface NumberConfig { + color?: THREE.ColorRepresentation; + decimals?: number; +} +// Define the Number class +class Number extends THREE.Group { + static geometries = Number.initializeGeometries(); + meshes: THREE.Mesh[] = []; + material = new THREE.MeshBasicMaterial({ color: "black" }); + decimals: number; + centerData = { + center: new THREE.Vector3(), + box: new THREE.Box3(), + offset: new THREE.Vector3(), + worldPosition: new THREE.Vector3(), + }; -export default class Number extends THREE.Group { - static geometries = Number.initializeGeometries(); - meshes: THREE.Mesh[] = []; - material = new THREE.MeshBasicMaterial({ color: "black" }); - decimals: number; - centerData = { - center: new THREE.Vector3(), - box: new THREE.Box3(), - offset: new THREE.Vector3(), - worldPosition: new THREE.Vector3(), + constructor( + value = 0, + config: NumberConfig = {}, + ) { + const fullConfig = { + color: config.color ?? "black", + decimals: config.decimals ?? 2, }; - - constructor( - value = 0, - config: { color?: THREE.ColorRepresentation; decimals?: number } = {}, - ) { - const fullConfig = { - color: config.color ?? "black", - decimals: config.decimals ?? 2, - }; - - super(); - this.material.color = new THREE.Color(fullConfig.color); - this.decimals = fullConfig.decimals; - this.scale.set(0.0008, -0.0008, 0.0008); - this.updateFromValue(value); - } - - reshape( - value: number, - config: { color?: THREE.ColorRepresentation; decimals?: number } = {}, - ) { - const fullConfig = { - color: config.color ?? "black", - decimals: config.decimals ?? 2, - }; - this.material.color = new THREE.Color(fullConfig.color); - this.decimals = fullConfig.decimals; - this.clear(); - this.updateFromValue(value); - } - - updateFromValue(value: number) { - const characters = value.toFixed(this.decimals).split(""); - for (let i = 0; i < characters.length; i++) { - const character = characters[i]; - - if (character === undefined) { - throw new Error(`No character at index ${i}`); - } - - const geometry = Number.geometries.get(character); - if (geometry === undefined) { - throw new Error(`Character ${character} isn't supported in Number.`); - } - - let mesh = this.meshes[i]; - if (mesh !== undefined) { - mesh.geometry = geometry; - } else { - mesh = new THREE.Mesh(geometry, this.material); - this.meshes.push(mesh); + + super(); + this.material.color = new THREE.Color(fullConfig.color); + this.decimals = fullConfig.decimals; + this.scale.set(0.0008, -0.0008, 0.0008); + this.updateFromValue(value); + } + + reshape( + value: number, + config: NumberConfig = {}, + ) { + const fullConfig = { + color: config.color ?? "black", + decimals: config.decimals ?? 2, + }; + this.material.color = new THREE.Color(fullConfig.color); + this.decimals = fullConfig.decimals; + this.clear(); // Clear previous meshes before adding new ones + this.updateFromValue(value); + } + + updateFromValue(value: number) { + const characters = value.toFixed(this.decimals).split(""); + // Remove excess meshes if the new value has fewer characters + while (this.meshes.length > characters.length) { + const mesh = this.meshes.pop(); + if (mesh) { + this.remove(mesh); + // Optionally dispose geometry/material if not shared/reused } - this.add(mesh); + } + this.children.length = this.meshes.length; // Sync children array + + for (let i = 0; i < characters.length; i++) { + const character = characters[i]; + + if (character === undefined) { + // This check might be redundant due to the loop condition + console.warn(`Character at index ${i} is unexpectedly undefined.`); + continue; + } + + const geometry = Number.geometries.get(character); + if (geometry === undefined) { + console.error(`Character ${character} isn't supported in Number.`); + continue; // Skip unsupported characters } - - for (let i = 1; i < this.children.length; i++) { - const previousChild = this.children[i - 1]; - const currentChild = this.children[i]; - currentChild.moveNextTo(previousChild, Utils.RIGHT, 0.025); + + let mesh = this.meshes[i]; + if (mesh !== undefined) { + mesh.geometry = geometry; + } else { + mesh = new THREE.Mesh(geometry, this.material); + this.meshes.push(mesh); // Add to meshes array first + this.add(mesh); // Then add to the group's children } - - this.centerData.worldPosition.copy(this.position); - this.localToWorld(this.centerData.worldPosition); - - this.centerData.box.setFromObject(this).getCenter(this.centerData.center); - this.centerData.center.y *= -1; - - this.centerData.offset - .subVectors(this.centerData.worldPosition, this.centerData.center) - .multiplyScalar(1 / 0.0008); - - this.children.forEach((child) => - child.position.add(this.centerData.offset), - ); } - - static extractGeometry(textShape: Text.Text) { - return textShape.children[0].children[0].children[0] - .geometry as THREE.ShapeGeometry; + + // Adjust positions relative to each other + for (let i = 1; i < this.children.length; i++) { + const previousChild = this.children[i - 1]; + const currentChild = this.children[i]; + // Ensure both children exist before calling moveNextTo + if (previousChild && currentChild && typeof (currentChild as any).moveNextTo === 'function') { + (currentChild as any).moveNextTo(previousChild, Utils.RIGHT, 0.025); + } else { + console.warn("Missing child or moveNextTo function for positioning number characters"); + } + } + + // Recalculate center and offset + this.centerData.worldPosition.copy(this.position); + this.localToWorld(this.centerData.worldPosition); + + // Ensure object has children before calculating bounding box + if (this.children.length > 0) { + this.centerData.box.setFromObject(this).getCenter(this.centerData.center); + // Y might need adjustment depending on coordinate system + // this.centerData.center.y *= -1; // Keep original logic if intended + + this.centerData.offset + .subVectors(this.centerData.worldPosition, this.centerData.center) + .multiplyScalar(1 / 0.0008); // Use the scale factor + + this.children.forEach((child) => + child.position.add(this.centerData.offset), + ); + } else { + // Reset center and offset if there are no children + this.centerData.center.set(0,0,0); + this.centerData.offset.set(0,0,0); + } + } + + // Safely extract geometry using optional chaining and type checking + static extractGeometry(textShape: Text.Text): THREE.ShapeGeometry | undefined { + const mesh = textShape.children[0]?.children[0]?.children[0]; + // Check if it's a Mesh and has ShapeGeometry + if (mesh instanceof THREE.Mesh && mesh.geometry instanceof THREE.ShapeGeometry) { + return mesh.geometry; } - - static initializeGeometries() { - const geometryMap = new Map(); - - for (let i = 0; i < 10; i++) { - const numberShape = new Text.Text(i.toString()); - const numberGeometry = Number.extractGeometry(numberShape); + return undefined; + } + + static initializeGeometries(): Map { + const geometryMap = new Map(); + + for (let i = 0; i < 10; i++) { + const numberShape = new Text.Text(i.toString()); + const numberGeometry = Number.extractGeometry(numberShape); + if (numberGeometry) { geometryMap.set(i.toString(), numberGeometry); } - - const decimalShape = new Text.Text("."); - const decimalGeometry = Number.extractGeometry(decimalShape); - geometryMap.set(".", decimalGeometry); - - return geometryMap; } - } \ No newline at end of file + + const decimalShape = new Text.Text("."); + const decimalGeometry = Number.extractGeometry(decimalShape); + if (decimalGeometry) { + geometryMap.set(".", decimalGeometry); + } + + return geometryMap; + } + + // Override clear to also clear the meshes array + clear(): this { + super.clear(); + this.meshes = []; + return this; + } +} + +export { Number }; // Export the class +export type { NumberConfig }; // Export the type \ No newline at end of file diff --git a/src/diagram/RightAngle.ts b/src/diagram/RightAngle.ts index 966e1c5..dfa63f4 100644 --- a/src/diagram/RightAngle.ts +++ b/src/diagram/RightAngle.ts @@ -1,28 +1,43 @@ import * as THREE from "three"; import * as Geometry from "../geometry/index.js"; -export default class RightAngle extends Geometry.Polyline { - constructor( - point1: THREE.Vector3, - point2: THREE.Vector3, - point3: THREE.Vector3, - config: Geometry.Style & { sideLength?: number } = {}, - ) { - config = { sideLength: 0.35, ...config }; - const vector21 = new THREE.Vector3() - .subVectors(point1, point2) - .setLength(config.sideLength); - const vector23 = new THREE.Vector3() - .subVectors(point3, point2) - .setLength(config.sideLength); - - super( - [ - new THREE.Vector3().addVectors(point2, vector21), - new THREE.Vector3().add(point2).add(vector21).add(vector23), - new THREE.Vector3().addVectors(point2, vector23), - ], - config, - ); - } - } \ No newline at end of file +// Define config interface combining Style and specific properties +interface RightAngleConfig extends Geometry.Style { + sideLength?: number; +} + +// Define the RightAngle class +class RightAngle extends Geometry.Polyline { + constructor( + point1: THREE.Vector3, + point2: THREE.Vector3, + point3: THREE.Vector3, + config: RightAngleConfig = {}, + ) { + // Establish default for sideLength + const sideLength = config.sideLength ?? 0.35; + // Create a config object for super call, including the resolved sideLength + // but sideLength is not a direct Polyline/Shape property, so don't pass it to super + const polylineConfig = { ...config }; + + const vector21 = new THREE.Vector3() + .subVectors(point1, point2) + .setLength(sideLength); // Use the resolved sideLength + const vector23 = new THREE.Vector3() + .subVectors(point3, point2) + .setLength(sideLength); // Use the resolved sideLength + + // Call super with the calculated points and the filtered config + super( + [ + new THREE.Vector3().addVectors(point2, vector21), + new THREE.Vector3().add(point2).add(vector21).add(vector23), + new THREE.Vector3().addVectors(point2, vector23), + ], + polylineConfig, // Pass the config intended for Polyline/Shape + ); + } +} + +export { RightAngle }; // Export the class +export type { RightAngleConfig }; // Export the type \ No newline at end of file diff --git a/src/diagram/index.ts b/src/diagram/index.ts index d116c96..31b05e6 100644 --- a/src/diagram/index.ts +++ b/src/diagram/index.ts @@ -1,6 +1,6 @@ -export { default as Indicator } from "./Indicator.js"; -export { default as Angle } from "./Angle.js"; -export { default as CongruentAngle } from "./CongruentAngle.js"; -export { default as RightAngle } from "./RightAngle.js"; -export { default as CongruentLine } from "./CongruentLine.js"; -export { default as Number } from "./Number.js"; +export * from './Indicator.js'; +export * from './CongruentLine.js'; +export * from './CongruentAngle.js'; +export * from './RightAngle.js'; +export * from './Number.js'; +// export * from './Angle.js'; // Uncomment if Angle was moved