package com.momega.spacesimulator.builder;
import java.util.List;
import org.joda.time.DateTime;
import org.joda.time.DateTimeConstants;
import org.joda.time.DateTimeZone;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import com.momega.spacesimulator.model.Camera;
import com.momega.spacesimulator.model.CartesianState;
import com.momega.spacesimulator.model.CelestialBody;
import com.momega.spacesimulator.model.KeplerianElements;
import com.momega.spacesimulator.model.KeplerianOrbit;
import com.momega.spacesimulator.model.KeplerianTrajectory;
import com.momega.spacesimulator.model.Maneuver;
import com.momega.spacesimulator.model.Model;
import com.momega.spacesimulator.model.MovingObject;
import com.momega.spacesimulator.model.Orientation;
import com.momega.spacesimulator.model.PhysicalBody;
import com.momega.spacesimulator.model.Planet;
import com.momega.spacesimulator.model.PositionProvider;
import com.momega.spacesimulator.model.Ring;
import com.momega.spacesimulator.model.RotatingObject;
import com.momega.spacesimulator.model.Spacecraft;
import com.momega.spacesimulator.model.SpacecraftSubsystem;
import com.momega.spacesimulator.model.SphereOfInfluence;
import com.momega.spacesimulator.model.Timestamp;
import com.momega.spacesimulator.model.Trajectory;
import com.momega.spacesimulator.model.TrajectoryType;
import com.momega.spacesimulator.model.Vector3d;
import com.momega.spacesimulator.service.HistoryPointService;
import com.momega.spacesimulator.service.KeplerianPropagator;
import com.momega.spacesimulator.service.ManeuverService;
import com.momega.spacesimulator.service.SpacecraftService;
import com.momega.spacesimulator.service.SphereOfInfluenceService;
import com.momega.spacesimulator.service.TargetService;
import com.momega.spacesimulator.utils.MathUtils;
import com.momega.spacesimulator.utils.TimeUtils;
import com.momega.spacesimulator.utils.VectorUtils;
/**
* Super class for all model builders.
* Created by martin on 6/18/14.
*/
public abstract class AbstractModelBuilder implements ModelBuilder {
private static final Logger logger = LoggerFactory.getLogger(AbstractModelBuilder.class);
protected Model model = null;
private static final int MOVING_OBJECTS_START_INDEX = 10;
private int lastIndex = 0;
@Autowired
private HistoryPointService historyPointService;
@Autowired
private KeplerianPropagator keplerianPropagator;
@Autowired
private ManeuverService maneuverService;
@Autowired
private TargetService targetService;
@Autowired
private SpacecraftService spacecraftService;
@Autowired
private SphereOfInfluenceService sphereOfInfluenceService;
/**
* Initialize model and returns the instance
*/
public final Model build() {
model = new Model();
model.setName(getName());
initTime();
initPlanets();
initSpacecrafts();
initCamera();
sphereOfInfluenceService.clear(model);
logger.info("model initialized");
return model;
}
protected void initTime() {
model.setTime(TimeUtils.fromDateTime(new DateTime(2014, 9, 23, 12, 0, DateTimeZone.UTC)));
}
protected Timestamp getTime() {
return model.getTime();
}
protected void createCamera(PositionProvider positionProvider) {
Camera s = new Camera();
s.setTargetObject(positionProvider);
s.setDistance(100 * 1E6);
s.setOppositeOrientation(Orientation.createUnit());
model.setCamera(s);
}
protected void setCentralPoint(MovingObject movingObject) {
movingObject.getCartesianState().setPosition(Vector3d.ZERO);
movingObject.getCartesianState().setVelocity(Vector3d.ZERO);
KeplerianTrajectory trajectory = new KeplerianTrajectory();
trajectory.setType(TrajectoryType.STATIC);
movingObject.setTrajectory(trajectory);
}
/**
* Creates all planets and bary centres
*/
protected abstract void initPlanets();
/**
* Creates the camera
*/
protected abstract void initCamera();
/**
* Initializes the spacecraft instances
*/
protected abstract void initSpacecrafts();
/**
* Returns the central object of the system
* @return the moving object
*/
protected abstract MovingObject getCentralObject();
/**
* Register the moving object to the universe
* @param dp the instance of the dynamical point
*/
public void addMovingObject(MovingObject dp) {
dp.setTimestamp(model.getTime());
model.getMovingObjects().add(dp);
}
/**
* Creates the keplerian trajectory
* @param movingObject the moving point. Typically it is the planet, but could be also barycentre
* @param centralObject the central object
* @param semimajorAxis the semimajor axis
* @param eccentricity the eccentricity
* @param argumentOfPeriapsis the argument of periapsis in degrees (omega)
* @param period the period in days
* @param timeOfPeriapsis the time of the last periapsis in timestamp
* @param inclination the inclination in degrees
* @param ascendingNode the ascending node in degrees (OMEGA)
* @return new instance of the keplerian trajectory
*/
public KeplerianElements createKeplerianElements(MovingObject movingObject, MovingObject centralObject, double semimajorAxis, double eccentricity, double argumentOfPeriapsis, double period, double timeOfPeriapsis, double inclination, double ascendingNode) {
Assert.notNull(movingObject);
Assert.notNull(centralObject);
KeplerianOrbit orbit = new KeplerianOrbit();
orbit.setReferenceFrame(centralObject);
orbit.setSemimajorAxis(semimajorAxis);
orbit.setEccentricity(eccentricity);
orbit.setArgumentOfPeriapsis(Math.toRadians(argumentOfPeriapsis));
orbit.setInclination(Math.toRadians(inclination));
orbit.setAscendingNode(Math.toRadians(ascendingNode));
orbit.setPeriod(period * DateTimeConstants.SECONDS_PER_DAY);
orbit.setTimeOfPeriapsis(TimeUtils.fromJulianDay(timeOfPeriapsis));
KeplerianTrajectory trajectory = movingObject.getTrajectory();
if (trajectory == null) {
trajectory = new KeplerianTrajectory();
trajectory.setType(TrajectoryType.KEPLERIAN);
}
movingObject.setTrajectory(trajectory);
movingObject.setTimestamp(model.getTime());
Assert.notNull(movingObject.getName());
// initialize position
//keplerianPropagator.computePosition(movingObject, model.getTime());
KeplerianElements keplerianElements = KeplerianElements.fromTimestamp(orbit, model.getTime());
CartesianState cartesianState = keplerianElements.toCartesianState();
movingObject.setKeplerianElements(keplerianElements);
movingObject.setCartesianState(cartesianState);
return keplerianElements;
}
public Trajectory createTrajectory(MovingObject movingObjects, String trajectoryColor) {
double r = (double) Integer.parseInt(trajectoryColor.substring(1, 3), 16);
double g = (double) Integer.parseInt(trajectoryColor.substring(3, 5), 16);
double b = (double) Integer.parseInt(trajectoryColor.substring(5, 7), 16);
return createTrajectory(movingObjects, new double[] {r / 255,g / 255,b / 255});
}
public Trajectory createTrajectory(MovingObject movingObjects, double[] trajectoryColor) {
KeplerianTrajectory trajectory = movingObjects.getTrajectory();
if (trajectory == null) {
trajectory = new KeplerianTrajectory();
}
trajectory.setColor(trajectoryColor);
return trajectory;
}
/**
* Creates the satellite
* @param centralPoint the initial celestial body, the satellite is orbiting. It is also used for transforming coordinates (position and velocity)
* to the central body of the system
* @param name the name of the satellite
* @param position the position of the satellite
* @param velocity the initial velocity
* @param subsystems list of the subsystems
* @return new instance of the satellite
*/
public Spacecraft createSpacecraft(CelestialBody centralPoint, String name, Vector3d position, Vector3d velocity, int index, double[] color, List<SpacecraftSubsystem> subsystems) {
MovingObject centralBody = getCentralObject();
Spacecraft spacecraft = spacecraftService.createSpacecraft(centralPoint, centralBody, name, position, velocity, index, model.getTime(), color, subsystems);
addMovingObject(spacecraft);
return spacecraft;
}
private void updateDynamicalPoint(MovingObject dp, String name, double mass, double rotationPeriod, double radius, String wiki, String icon) {
dp.setName(name);
dp.setIndex(MOVING_OBJECTS_START_INDEX + lastIndex);
lastIndex++;
if (dp.getCartesianState() == null) {
dp.setCartesianState(new CartesianState());
}
if (dp instanceof PhysicalBody) {
PhysicalBody body = (PhysicalBody) dp;
body.setMass(mass * 1E24);
body.setOrientation(Orientation.createUnit());
if (dp instanceof RotatingObject) {
RotatingObject ro = (RotatingObject) dp;
ro.setRotationPeriod(rotationPeriod * DateTimeConstants.SECONDS_PER_DAY);
ro.setRadius(radius * 1E6);
if (dp instanceof CelestialBody) {
CelestialBody cb = (CelestialBody) dp;
cb.setWiki(wiki);
if (icon == null) {
cb.setIcon("/images/celestial.png");
} else {
cb.setIcon(icon);
}
}
}
}
}
/**
* Updates data about the dynamical point
* @param dp the already created dynamical point
* @param name the name
* @param mass the mass in 1E24 kilograms
* @param rotationPeriod rotation period in days
* @param radius radius in kilometers
* @param axialTilt axial tilt
* @param wiki the wiki page
* @param icon the icon
*/
protected void updateDynamicalPoint(MovingObject dp, String name, double mass, double rotationPeriod, double radius, double axialTilt, String wiki, String icon) {
updateDynamicalPoint(dp, name, mass, rotationPeriod, radius, wiki, icon);
if (dp instanceof RotatingObject) {
RotatingObject ro = (RotatingObject) dp;
ro.getOrientation().twist(Math.toRadians(axialTilt));
}
}
/**
* Updates data about the dynamical point
* @param dp the already created dynamical point
* @param name the name
* @param mass the mass in 1E24 kilograms
* @param rotationPeriod rotation period in days
* @param radius radius in thousand of kilometers
* @param ra right ascension RA of the north pole
* @param dec declination 0of the north pole
* @param wiki the wiki page
* @param icon the icon resource path
*/
protected void updateDynamicalPoint(PhysicalBody dp, String name, double mass, double rotationPeriod, double radius, double ra, double dec, String wiki, String icon) {
updateDynamicalPoint(dp, name, mass, rotationPeriod, radius, wiki, icon);
if (dp instanceof RotatingObject) {
Orientation orientation = VectorUtils.createOrientation(Math.toRadians(ra), Math.toRadians(dec), true);
dp.setOrientation(orientation);
}
}
/**
* Updates data about the dynamical point
* @param dp the already created dynamical point
* @param name the name
* @param mass the mass in 1E24 kilograms
* @param rotationPeriod rotation period in days
* @param radius radius in thousand of kilometers
* @param ra right ascension RA of the north pole
* @param dec declination 0of the north pole
* @param primeMeridianJd2000 prime meridian at JD2000 epoch
* @param icon the icon
*/
protected void updateDynamicalPoint(PhysicalBody dp, String name, double mass, double rotationPeriod, double radius, double ra, double dec, double primeMeridianJd2000, String wiki, String icon) {
updateDynamicalPoint(dp, name, mass, rotationPeriod, radius, ra, dec, wiki, icon);
if (dp instanceof RotatingObject) {
RotatingObject ro = (RotatingObject) dp;
ro.setPrimeMeridianJd2000(Math.toRadians(primeMeridianJd2000));
}
}
/**
* Adds the ring for the planet
* @param planet the planet
* @param min the minimum distance
* @param max the maximum distance
* @param textureFileName textures of the ring
* @return new instance of the ring
*/
public Ring addRing(Planet planet, double min, double max, String textureFileName, String transparencyFileName) {
Ring ring = new Ring();
ring.setMinDistance(min);
ring.setMaxDistance(max);
ring.setTextureFileName(textureFileName);
ring.setTransparencyFileName(transparencyFileName);
planet.getRings().add(ring);
return ring;
}
/**
* Creates maneuver
* @param spacecraft the spacecraft
* @param name the name of the maneuver
* @param startTime start of the maneuver in seconds from the start
* @param duration the duration of maneuver in seconds
* @param throttle the engine throttle as number from 0..1, 1 means 100%
* @param throttleAlpha
* @param throttleDelta
* @return the new maneuver instance
*/
public Maneuver addManeuver(Spacecraft spacecraft, String name, Double startTime, double duration, double throttle, double throttleAlpha, double throttleDelta) {
Maneuver maneuver = maneuverService.createManeuver(spacecraft, name, model.getTime(), startTime, duration, throttle, throttleAlpha, throttleDelta);
spacecraft.getManeuvers().add(maneuver);
return maneuver;
}
/**
* The method adds the planet to the SOI tree and calculate the radius of the planet soi. The trajectory comes directly
* from the planet trajectory
* @param celestialBody the planet
* @param parentSoi the parent soi
*/
public SphereOfInfluence addPlanetToSoiTree(final CelestialBody celestialBody, final SphereOfInfluence parentSoi) {
if (parentSoi == null) {
SphereOfInfluence soi = new SphereOfInfluence();
soi.setBody(celestialBody);
soi.setRadius(MathUtils.UNIVERSE_SIZE);
model.setRootSoi(soi);
return soi;
} else {
return addPlanetToSoiTree(celestialBody, parentSoi, celestialBody.getKeplerianElements());
}
}
/**
* The method adds the planet to the SOI tree and calculate the radius of the planet soi.
* @param celestialBody the planet
* @param parentSoi the parent soi
* @param keplerianElements the keplerian elements of the planet. It has to be specified when the the planet
* orbiting the bary-centre. In these cases it is not possible to calculate correctly sphere of influence. The example is
* Earth -> (Earth/Moon Barycentre) -> Sun
* @return new instance of the sphere of influence
*/
public SphereOfInfluence addPlanetToSoiTree(final CelestialBody celestialBody, final SphereOfInfluence parentSoi, KeplerianElements keplerianElements) {
SphereOfInfluence soi = new SphereOfInfluence();
double radius = Math.pow(celestialBody.getMass() / parentSoi.getBody().getMass(), 0.4d) * keplerianElements.getKeplerianOrbit().getSemimajorAxis();
soi.setRadius(radius);
soi.setBody(celestialBody);
soi.setParent(parentSoi);
parentSoi.getChildren().add(soi);
return soi;
}
/**
* Finds the dynamical point based on its name
* @param name the name of the dynamical point
* @return the instance of dynamical point or null
*/
public MovingObject findMovingObject(String name) {
for(MovingObject dp : model.getMovingObjects()) {
if (name.equals(dp.getName())) {
return dp;
}
}
return null;
}
protected void setTarget(Spacecraft spacecraft, CelestialBody celestialBody) {
targetService.createTarget(spacecraft, celestialBody);
}
}