Parse
File Parse gearbox-planets.js
This tree is parsed live from the source file.
Classes
-
{{ item.name }}
- {{ key }}
Not Classes
{{ getTree() }}
Comments
{{ getTreeComments() }}
Source
/*
# Planetary Gears Extension
An optional extension for the GearBox that adds support for planetary gear systems.
## Features
- **Sun Gear**: Central motor gear that drives the system
- **Planet Gears**: Orbit around the sun while rotating on their own axes
- **Ring Gear**: Outer internal gear that planets mesh with
- **Carrier**: Virtual mechanism that controls orbital motion
## Usage
### Loading
Include `gearbox-planets.js` after loading `gearbox.js`:
```javascript
// In HTML
<script src="point_src/gearbox.js"></script>
<script src="point_src/gearbox-planets.js"></script>
// Or in theatre file:
files:
...
../point_src/gearbox.js
../point_src/gearbox-planets.js
```
### Creating a Planetary System
```javascript
let gearbox = new GearBox();
let planetary = gearbox.createPlanetarySystem({
sunXY: { x: 400, y: 300 }, // Center position
sunRadius: 60, // Sun gear size
planetRadius: 25, // Planet gear size
ringRadius: 150, // Ring gear size (optional, auto-calculated)
planetCount: 3, // Number of planet gears (3-5 recommended)
sunMotor: 1 // Motor speed in degrees per frame
});
```
### Accessing Components
```javascript
planetary.sun // The sun gear (Point)
planetary.planets // Array of planet gears [Point, Point, ...]
planetary.ring // The ring gear (Point)
```
### Control Methods
```javascript
// Change sun motor speed
planetary.setSunMotor(2.0)
// Lock/unlock the carrier (prevents orbital motion)
planetary.setCarrierLocked(true)
// Lock/unlock the ring gear
planetary.setRingLocked(true)
// Make the ring gear rotate
planetary.ring.motor = 1
```
### Properties
```javascript
planetary.carrierAngle // Current orbital angle (degrees)
planetary.carrierVelocity // Current orbital velocity (degrees/frame)
planetary.orbitRadius // Distance from sun to planet centers
```
## How It Works
1. The sun gear rotates (motor-driven)
2. Sun meshes with planet gears → planets rotate
3. Planets mesh with ring gear → more rotation force
4. Planet rotation creates tangential velocity
5. Tangential velocity drives the carrier to orbit
6. Carrier updates planet XY positions each frame
The system automatically integrates with the GearBox touch detection and gear meshing logic.
## Examples
See `theatre/gearbox-planetary.js` for a complete working example.
## Notes
- All angles are in **degrees** (polypoint convention), not radians
- Motor speeds are in degrees per frame
- Ring radius is auto-calculated if not provided to ensure proper meshing
- Planets can be dragged but will fight with their orbital motion
- Sun and ring gears can be freely dragged
*/
class PlanetaryGearSystem {
/*
* Manages a planetary gear system with:
* - Sun gear (center, typically the input/motor)
* - Planet gears (orbit around sun while rotating)
* - Ring gear (outer, internal gear that planets mesh with)
* - Carrier (virtual mechanism that controls orbital motion)
*/
constructor(gearbox, options={}) {
this.gearbox = gearbox
this.planets = []
this.carrierAngle = options.carrierAngle || 0
this.carrierVelocity = 0
// Configuration
let planetCount = options.planetCount || 3
let sunRadius = options.sunRadius || 50
let planetRadius = options.planetRadius || 30
let sunXY = options.sunXY || {x: 400, y: 300}
// Calculate orbit radius (distance from sun center to planet center)
// Planets touch the sun, so orbit radius = sun radius + planet radius
this.orbitRadius = sunRadius + planetRadius
// Ring radius must touch the planets from outside
// Ring inner edge = orbit radius + planet radius
let ringRadius = options.ringRadius || (this.orbitRadius + planetRadius + 2)
// Create sun gear (motor)
this.sun = gearbox.createMotor({
x: sunXY.x,
y: sunXY.y,
radius: sunRadius,
motor: options.sunMotor !== undefined ? options.sunMotor : 0.02
})
// Create ring gear (internal, stationary by default)
this.ring = gearbox.createGear({
x: sunXY.x,
y: sunXY.y,
radius: ringRadius,
internal: true
})
// Create planet gears evenly distributed around sun
for(let i = 0; i < planetCount; i++) {
let angle = (360 / planetCount) * i // degrees
let planet = this.createPlanet(angle, planetRadius)
this.planets.push(planet)
}
// Register with gearbox for automatic updates
gearbox.registerPlanetarySystem(this)
}
createPlanet(angle, radius) {
/* Create a single planet gear at the specified angle (in degrees) */
let pos = this.calculatePlanetPosition(angle)
let planet = this.gearbox.createGear({
x: pos.x,
y: pos.y,
radius: radius,
angularVelocity: 0
})
// Mark as orbital so we can identify it
planet.isOrbital = true
planet.orbitAngle = angle
planet.orbitCenter = this.sun
planet.orbitRadius = this.orbitRadius
return planet
}
calculatePlanetPosition(angle) {
/* Calculate XY position for a planet at the given orbital angle
* Angle is in degrees (polypoint convention)
*/
let radians = angle * Math.PI / 180
let x = this.sun.x + Math.cos(radians) * this.orbitRadius
let y = this.sun.y + Math.sin(radians) * this.orbitRadius
return {x, y}
}
updateOrbitalPositions() {
/* Update planet positions based on carrier rotation
* This is called each frame by the gearbox
* Working in DEGREES (polypoint convention)
*/
// Calculate carrier velocity from planet motion
// The carrier rotates when planets are pushed by the sun or ring
this.updateCarrierVelocity()
// Update carrier angle (in degrees)
this.carrierAngle += this.carrierVelocity
// Keep angle in 0-360 range
if(this.carrierAngle > 360) this.carrierAngle -= 360
if(this.carrierAngle < 0) this.carrierAngle += 360
// Position each planet along its orbit
this.planets.forEach((planet, i) => {
// Evenly space planets around the orbit (in degrees)
let baseAngle = (360 / this.planets.length) * i
let angle = baseAngle + this.carrierAngle
let pos = this.calculatePlanetPosition(angle)
planet.x = pos.x
planet.y = pos.y
planet.orbitAngle = angle
// Update XY binding if exists (for pinion wheels)
if(this.gearbox.bindMap.bindMap.has(planet)) {
let boundGears = this.gearbox.bindMap.bindMap.get(planet)
boundGears.forEach(bound => {
bound.x = pos.x
bound.y = pos.y
})
}
})
// Dampen carrier velocity (less damping than before)
this.carrierVelocity *= 0.95
}
updateCarrierVelocity() {
/* Calculate carrier rotation based on planet velocities
* When planets rotate, they "walk" around the sun/ring, driving the carrier
*
* Working in DEGREES (not radians) since polypoint uses degrees
*/
if(this.planets.length === 0) return
// Average the tangential velocity of all planets
let avgTangentialVelocity = 0
this.planets.forEach(planet => {
// Tangential velocity = angular velocity (degrees) * radius
// Convert degrees to "arc length" movement
let tangentialContribution = planet.angularVelocity * (Math.PI / 180) * planet.radius
avgTangentialVelocity += tangentialContribution
})
avgTangentialVelocity /= this.planets.length
// Convert tangential velocity to carrier angular velocity (in degrees)
// This is how much the carrier rotates based on planet "walking"
let carrierContribution = (avgTangentialVelocity / this.orbitRadius) * (180 / Math.PI)
// Blend with existing velocity for smooth motion
// Reduce influence significantly to prevent runaway speed
this.carrierVelocity = this.carrierVelocity * 0.85 + carrierContribution * 0.15
}
setCarrierLocked(locked) {
/* Lock or unlock the carrier (prevents orbital motion) */
this.carrierLocked = locked
if(locked) {
this.carrierVelocity = 0
}
}
setRingLocked(locked) {
/* Lock or unlock the ring gear */
if(locked) {
this.ring.motor = 0
this.ring.angularVelocity = 0
}
this.ringLocked = locked
}
setSunMotor(velocity) {
/* Change the sun gear motor speed */
this.sun.motor = velocity
}
}
copy