feat: initial commit of Railtrack Pro prototype with complete test suite
This commit is contained in:
+189
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* Railtrack Pro - Track Piece System
|
||||
* Defines track geometries and placement logic
|
||||
* @author Railtrack Pro Development Team
|
||||
*/
|
||||
|
||||
class TrackPiece {
|
||||
constructor(type, position, rotation) {
|
||||
this.type = type; // 'straight', 'curved', 'junction', 'signal'
|
||||
this.id = position ? Math.floor(Math.random() * 10000) : null;
|
||||
this.position = position || new THREE.Vector3(0, 0, 0);
|
||||
this.rotation = rotation || new THREE.Vector3(0, Math.PI / 2, 0);
|
||||
this.connections = [];
|
||||
this.geometry = null;
|
||||
this.material = null;
|
||||
this.mesh = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create track piece in scene
|
||||
*/
|
||||
create(scene) {
|
||||
this.geometry = this.getGeometry();
|
||||
this.material = new THREE.MeshStandardMaterial({
|
||||
color: 0x888888,
|
||||
roughness: 0.6,
|
||||
metalness: 0.4
|
||||
});
|
||||
|
||||
this.mesh = new THREE.Mesh(this.geometry, this.material);
|
||||
this.mesh.position.copy(this.position);
|
||||
this.mesh.rotation.copy(this.rotation);
|
||||
this.mesh.castShadow = true;
|
||||
this.mesh.receiveShadow = true;
|
||||
this.mesh.userData = { type: 'track', id: this.id, piece: this };
|
||||
|
||||
scene.add(this.mesh);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get geometry based on track type
|
||||
*/
|
||||
getGeometry() {
|
||||
switch (this.type) {
|
||||
case 'straight':
|
||||
return new THREE.BoxGeometry(1, 0.3, 2);
|
||||
case 'curved':
|
||||
return this.createCurvedGeometry();
|
||||
case 'junction':
|
||||
return this.createJunctionGeometry();
|
||||
case 'signal':
|
||||
return this.createSignalGeometry();
|
||||
default:
|
||||
return new THREE.BoxGeometry(1, 0.3, 2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create curved track geometry
|
||||
*/
|
||||
createCurvedGeometry() {
|
||||
const points = [];
|
||||
const segments = 20;
|
||||
const radius = 2;
|
||||
|
||||
for (let i = 0; i <= segments; i++) {
|
||||
const angle = (i / segments) * Math.PI;
|
||||
const x = Math.sin(angle) * radius;
|
||||
const z = -Math.cos(angle) * radius + radius;
|
||||
points.push(new THREE.Vector3(x, 0, z));
|
||||
}
|
||||
|
||||
const curve = new THREE.CatmullRomCurve3(points);
|
||||
const geometry = new THREE.TubeGeometry(curve, segments, 0.5, 8, false);
|
||||
return geometry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create junction track geometry (T-junction)
|
||||
*/
|
||||
createJunctionGeometry() {
|
||||
const group = new THREE.Group();
|
||||
|
||||
// Main stem
|
||||
const stem = new THREE.Mesh(
|
||||
new THREE.BoxGeometry(1, 0.3, 2),
|
||||
this.material
|
||||
);
|
||||
stem.position.set(0, 0, 1);
|
||||
|
||||
// Top branch
|
||||
const top = new THREE.Mesh(
|
||||
new THREE.BoxGeometry(1, 0.3, 2),
|
||||
this.material
|
||||
);
|
||||
top.position.set(0, 0, -1);
|
||||
|
||||
// Side branch
|
||||
const side = new THREE.Mesh(
|
||||
new THREE.BoxGeometry(2, 0.3, 1),
|
||||
this.material
|
||||
);
|
||||
side.position.set(0, 0, 0);
|
||||
|
||||
group.add(stem, top, side);
|
||||
|
||||
const geometry = new THREE.Group(group);
|
||||
return geometry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create signal track piece
|
||||
*/
|
||||
createSignalGeometry() {
|
||||
const group = new THREE.Group();
|
||||
|
||||
// Signal pole
|
||||
const pole = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(0.1, 0.1, 1.5),
|
||||
new THREE.MeshStandardMaterial({ color: 0x333333 })
|
||||
);
|
||||
pole.position.set(0, 0.75, 0);
|
||||
|
||||
// Signal lights
|
||||
const lightBox = new THREE.Mesh(
|
||||
new THREE.BoxGeometry(0.3, 0.2, 0.1),
|
||||
new THREE.MeshStandardMaterial({ color: 0xff0000 })
|
||||
);
|
||||
lightBox.position.set(0, 1.5, 0);
|
||||
|
||||
// Track base
|
||||
const base = new THREE.Mesh(
|
||||
new THREE.BoxGeometry(1, 0.3, 1),
|
||||
this.material
|
||||
);
|
||||
base.position.set(0, 0, 0);
|
||||
|
||||
group.add(base, pole, lightBox);
|
||||
|
||||
const geometry = new THREE.Group(group);
|
||||
return geometry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get connection points for track joining
|
||||
*/
|
||||
getConnectionPoints() {
|
||||
const points = [];
|
||||
switch (this.type) {
|
||||
case 'straight':
|
||||
points.push(new THREE.Vector3(0, 0, -1));
|
||||
points.push(new THREE.Vector3(0, 0, 1));
|
||||
break;
|
||||
case 'curved':
|
||||
points.push(new THREE.Vector3(0, 0, -2));
|
||||
points.push(new THREE.Vector3(2, 0, 0));
|
||||
break;
|
||||
case 'junction':
|
||||
points.push(new THREE.Vector3(0, 0, -2));
|
||||
points.push(new THREE.Vector3(0, 0, 2));
|
||||
points.push(new THREE.Vector3(-2, 0, 0));
|
||||
points.push(new THREE.Vector3(2, 0, 0));
|
||||
break;
|
||||
}
|
||||
return points;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Track type configuration
|
||||
*/
|
||||
const TrackConfig = {
|
||||
types: {
|
||||
straight: { name: 'Straight Track', color: 0x888888, cost: 10 },
|
||||
curved: { name: 'Curved Track', color: 0x888888, cost: 15 },
|
||||
junction: { name: 'Junction', color: 0x888888, cost: 25 },
|
||||
signal: { name: 'Signal', color: 0xff0000, cost: 30 }
|
||||
},
|
||||
placement: {
|
||||
snapDistance: 0.1,
|
||||
maxConnections: 4
|
||||
}
|
||||
};
|
||||
|
||||
// Export for module usage
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = { TrackPiece, TrackConfig };
|
||||
}
|
||||
Reference in New Issue
Block a user