package net.sf.openrocket.aerodynamics; import java.util.Map; import net.sf.openrocket.rocketcomponent.Configuration; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.util.Coordinate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * An abstract aerodynamic calculator implementation, that offers basic implementation * of some methods and methods for cache validation and purging. * * @author Sampo Niskanen <sampo.niskanen@iki.fi> */ public abstract class AbstractAerodynamicCalculator implements AerodynamicCalculator { private static final Logger log = LoggerFactory.getLogger(AbstractAerodynamicCalculator.class); /** Number of divisions used when calculating worst CP. */ public static final int DIVISIONS = 360; /** * A <code>WarningSet</code> that can be used if <code>null</code> is passed * to a calculation method. */ protected WarningSet ignoreWarningSet = new WarningSet(); /** The aerodynamic modification ID of the latest rocket */ private int rocketAeroModID = -1; private int rocketTreeModID = -1; //////////////// Aerodynamic calculators //////////////// @Override public abstract Coordinate getCP(Configuration configuration, FlightConditions conditions, WarningSet warnings); @Override public abstract Map<RocketComponent, AerodynamicForces> getForceAnalysis(Configuration configuration, FlightConditions conditions, WarningSet warnings); @Override public abstract AerodynamicForces getAerodynamicForces(Configuration configuration, FlightConditions conditions, WarningSet warnings); /* * The worst theta angle is stored in conditions. */ @Override public Coordinate getWorstCP(Configuration configuration, FlightConditions conditions, WarningSet warnings) { FlightConditions cond = conditions.clone(); Coordinate worst = new Coordinate(Double.MAX_VALUE); Coordinate cp; double theta = 0; for (int i = 0; i < DIVISIONS; i++) { cond.setTheta(2 * Math.PI * i / DIVISIONS); cp = getCP(configuration, cond, warnings); if (cp.x < worst.x) { worst = cp; theta = cond.getTheta(); } } conditions.setTheta(theta); return worst; } /** * Check the current cache consistency. This method must be called by all * methods that may use any cached data before any other operations are * performed. If the rocket has changed since the previous call to * <code>checkCache()</code>, then {@link #voidAerodynamicCache()} is called. * <p> * This method performs the checking based on the rocket's modification IDs, * so that these method may be called from listeners of the rocket itself. * * @param configuration the configuration of the current call */ protected final void checkCache(Configuration configuration) { if (rocketAeroModID != configuration.getRocket().getAerodynamicModID() || rocketTreeModID != configuration.getRocket().getTreeModID()) { rocketAeroModID = configuration.getRocket().getAerodynamicModID(); rocketTreeModID = configuration.getRocket().getTreeModID(); log.debug("Voiding the aerodynamic cache"); voidAerodynamicCache(); } } /** * Void cached aerodynamic data. This method is called whenever a change occurs in * the rocket structure that affects the aerodynamics of the rocket and when a new * Rocket is set. This method must be overridden to void any cached data * necessary. The method must call <code>super.voidAerodynamicCache()</code> during * its execution. */ protected void voidAerodynamicCache() { // No-op } }