diff --git a/pom.xml b/pom.xml
index 4b2e48f..cc587ba 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.github.KrumSpace
SpacePlane
- 0.2
+ 0.3
19
diff --git a/src/main/java/main/graphics/Window.java b/src/main/java/main/graphics/Window.java
index 1623f7c..0ac1000 100644
--- a/src/main/java/main/graphics/Window.java
+++ b/src/main/java/main/graphics/Window.java
@@ -1,50 +1,49 @@
package main.graphics;
-import java.awt.Point;
-import java.awt.Dimension;
-import java.awt.Color;
+import java.awt.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
-import vehicles.Vehicle;
import vehicleGraphics.VehiclePanelData;
import vehicleGraphics.VehiclePanel;
import vehicles.VehicleAdapter;
//
public class Window extends JFrame {
- private static final int[] SIZE_CORRECTION = new int[] {17, 40};
+ private static final Dimension PANEL_SIZE = new Dimension(1000, 900);
+ private static final int[] WINDOW_SIZE_CORRECTION = new int[] {17, 40};
private static final Point LOCATION = new Point(600, 50);
private static final String WINDOW_TITLE = "Vehicle viewer";
private static final Color BACKGROUND = Color.BLACK;
+ private final VehiclePanelData vehiclePanelData;
+
//
public Window(VehicleAdapter vehicleAdapter) {
super();
setWindowConfig();
- setContents(vehicleAdapter);
- setResizable(false);
+ vehiclePanelData = new VehiclePanelData(vehicleAdapter, PANEL_SIZE);
+ setContents();
+ //setResizable(false);
setVisible(true);
}
private void setWindowConfig() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocation(LOCATION);
-
- Dimension optimalSize = VehiclePanelData.getOptimalSize();
setSize(new Dimension(
- optimalSize.width + SIZE_CORRECTION[0],
- optimalSize.height + SIZE_CORRECTION[1]));
+ PANEL_SIZE.width + WINDOW_SIZE_CORRECTION[0],
+ PANEL_SIZE.height + WINDOW_SIZE_CORRECTION[1]));
setTitle(WINDOW_TITLE);
}
- private void setContents(VehicleAdapter vehicleAdapter) {
+ private void setContents() {
setBackground(BACKGROUND);
- add(getVehiclePanel(vehicleAdapter));
+ add(getVehiclePanel());
}
- private JPanel getVehiclePanel(VehicleAdapter vehicleAdapter) {
- return new VehiclePanel(new VehiclePanelData(vehicleAdapter));
+ private JPanel getVehiclePanel() {
+ return new VehiclePanel(vehiclePanelData);
}
}
\ No newline at end of file
diff --git a/src/main/java/mathUtilities/Circles.java b/src/main/java/mathUtilities/Circles.java
new file mode 100644
index 0000000..b8b8d87
--- /dev/null
+++ b/src/main/java/mathUtilities/Circles.java
@@ -0,0 +1,24 @@
+package mathUtilities;
+
+//
+public class Circles {
+ // C = 2 * pi * R
+ public static double getCircumference(double radius) {
+ return 2 * Math.PI * radius;
+ }
+
+ // R = C / 2 / pi
+ public static double getRadiusFromCircumference(double circumference) {
+ return circumference / 2 / Math.PI;
+ }
+
+ // S = pi * R ^ 2
+ public static double getArea(double radius) {
+ return Math.PI * Math.pow(radius, 2);
+ }
+
+ // R = sqrt(S / pi)
+ public static double getRadiusFromArea(double area) {
+ return Math.sqrt(area / Math.PI);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/mathUtilities/Cylinders.java b/src/main/java/mathUtilities/Cylinders.java
new file mode 100644
index 0000000..7155aba
--- /dev/null
+++ b/src/main/java/mathUtilities/Cylinders.java
@@ -0,0 +1,18 @@
+package mathUtilities;
+
+//
+public class Cylinders {
+ //
+ public static double getVolume(double diameter, double length) {
+ return getCrossSection(diameter) * length;
+ }
+
+ //
+ public static double getLength(double volume, double diameter) {
+ return volume / getCrossSection(diameter);
+ }
+
+ private static double getCrossSection(double diameter) {
+ return Circles.getArea(diameter / 2);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/vehicleGraphics/BorderPainter.java b/src/main/java/vehicleGraphics/BorderPainter.java
index 3ae5f48..3a2f385 100644
--- a/src/main/java/vehicleGraphics/BorderPainter.java
+++ b/src/main/java/vehicleGraphics/BorderPainter.java
@@ -6,22 +6,21 @@
//
class BorderPainter implements PainterInterface {
private final VehiclePanelData panelData;
- private final Dimension size;
//
- protected BorderPainter(VehiclePanelData panelData, Dimension size) {
+ protected BorderPainter(VehiclePanelData panelData) {
this.panelData = panelData;
- this.size = size;
}
//
@Override
public void paint(Graphics g, int[] drawLocation) {
- g.setColor(panelData.getBorderColor());
+ Dimension size = panelData.getPanelSize();
int
offset = panelData.getBorderOffset(),
x = size.width,
y = size.height;
+ g.setColor(panelData.getBorderColor());
g.drawRect(offset, offset, Math.max(0, x - 2 * offset), Math.max(0, y - 2 * offset));
}
}
\ No newline at end of file
diff --git a/src/main/java/vehicleGraphics/VehiclePainter.java b/src/main/java/vehicleGraphics/VehiclePainter.java
index f91031c..cc0b49f 100644
--- a/src/main/java/vehicleGraphics/VehiclePainter.java
+++ b/src/main/java/vehicleGraphics/VehiclePainter.java
@@ -1,7 +1,5 @@
package vehicleGraphics;
-import java.util.List;
-
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
@@ -9,10 +7,10 @@
import vehicles.Vehicle;
import vehicles.VehiclePart;
+import vehicles.VehiclePartLocationInfo;
//
class VehiclePainter implements PainterInterface {
- private static final int[] VEHICLE_DRAW_SIZE = new int[] {980, 880};
private static final Color
PART_COLOR = new Color(100, 100, 100),
PART_BORDER_COLOR = new Color(150, 150, 150),
@@ -20,126 +18,176 @@ class VehiclePainter implements PainterInterface {
private static final Font
PART_TITLE_FONT = new Font("Verdana", Font.BOLD, 18),
PART_DETAILS_FONT = new Font(null, Font.BOLD, 15);
- private static final int
- PART_MASS_DECIMAL_DIGITS = 2,
- PART_LENGTH_DECIMAL_DIGITS = 1;
+
private final ImageObserver imageObserver;
private final VehiclePanelData panelData;
- private final int[]
- panelDrawSize,
- vehicleDrawSize;
+ private final double angle; // in radians
//
- protected VehiclePainter(ImageObserver imageObserver, VehiclePanelData panelData) {
+ protected VehiclePainter(ImageObserver imageObserver, VehiclePanelData panelData,
+ double angle) {
this.imageObserver = imageObserver;
this.panelData = panelData;
-
- Dimension size = VehiclePanelData.getOptimalSize();
- panelDrawSize = new int[] {size.width, size.height};
- vehicleDrawSize = VEHICLE_DRAW_SIZE;
+ this.angle = Math.toRadians(angle);
}
//
@Override
public final void paint(Graphics g, int[] drawLocation) {
- BufferedImage image = new BufferedImage(
- panelDrawSize[0], panelDrawSize[1],
- BufferedImage.TYPE_INT_ARGB);
- Graphics2D g2 = image.createGraphics();
- double angle = -20;
- g2.transform(getTransform(
- angle, new int[] {
- vehicleDrawSize[0] / 2,
- vehicleDrawSize[1] / 2}));
- paintVehicle(g2, vehicleDrawSize);
- int[] vehicleDrawLocation = new int[] {
- drawLocation[0] + (panelDrawSize[0] - vehicleDrawSize[0]) / 2,
- drawLocation[1] + (panelDrawSize[1] - vehicleDrawSize[1]) / 2};
- g.drawImage(image, vehicleDrawLocation[0], vehicleDrawLocation[1], imageObserver);
- }
+ Dimension panelSize = panelData.getPanelSize();
+ double[] drawCenter = new double[] {
+ (double) panelSize.width / 2,
+ (double) panelSize.height / 2};
- //for rotation
- private AffineTransform getTransform(double angle, int[] center) {
- return new AffineTransform() {{
- translate(center[0], center[1]);
- rotate(Math.toRadians(angle));
- translate(-center[0], -center[1]);
- }};
+ //vehicle & scale
+ Vehicle vehicle = panelData.getVehicle();
+ int maxDrawLength = Math.min(
+ (int) (panelSize.width / Math.sin(angle)),
+ (int) (panelSize.height / Math.cos(angle)));
+ double scaleTestCoefficient = 1;
+ //actual * scale = draw
+ double scale = vehicle.getLength() / maxDrawLength * scaleTestCoefficient;
+
+ //paint part images
+ for (VehiclePart part : vehicle.getParts()) {
+ VehiclePartLocationInfo partInfo = vehicle.getPartInfo(part);
+ paintPartImage(
+ g, part,
+ getDrawLoc(partInfo.getLocation(), drawCenter, scale),
+ scale);
+ }
+
+ //paint part info
+ for (VehiclePart part : vehicle.getParts()) {
+ VehiclePartLocationInfo partInfo = vehicle.getPartInfo(part);
+ paintPartInfo(
+ g, part,
+ getDrawLoc(partInfo.getLocation(), drawCenter, scale));
+ }
}
- private void paintVehicle(Graphics2D g2, int[] drawSize) {
- int[] drawCenter = new int[] {drawSize[0] / 2, drawSize[1] / 2};
+ private double[] getDrawLoc(double[] actualLoc, double[] drawCenter, double scale) {
+ double[] relativeOrthogonalLoc = new double[] { //actual loc scaled to drawable for g2
+ -1 * actualLoc[1] / scale, //draw x = actual -y
+ -1 * actualLoc[0] / scale}; //draw y = actual -x
+
+ return new double[] { //use trigonometry here
+ drawCenter[0]
+ + relativeOrthogonalLoc[0] * Math.cos(angle)
+ - relativeOrthogonalLoc[1] * Math.sin(angle),
+ drawCenter[1]
+ + relativeOrthogonalLoc[1] * Math.cos(angle)
+ - relativeOrthogonalLoc[0] * Math.sin(angle)};
+ }
- //vehicle
- Vehicle vehicle = panelData.getVehicle();
- double scale = vehicle.getLength() / drawSize[0];
-
- //parts
- List parts = vehicle.getParts();
- int xPart = 0;
- for (int i = 0; i < parts.size(); i++) {
- VehiclePart part = parts.get(i);
- double[]
- scaledPartSize = new double[] {
- part.getLength() / scale,
- part.getDiameter() / scale},
- partDrawLoc = new double[] {
- drawCenter[0] + (double) drawSize[0] / 2 - scaledPartSize[0] - xPart / scale,
- drawCenter[1] - scaledPartSize[1] / 2};
- paintVehiclePart(g2, part, partDrawLoc, scaledPartSize);
- xPart += part.getLength();
- }
+ private void paintPartImage(Graphics g, VehiclePart part,
+ double[] partDrawLoc, double scale) {
+ double[] scaledPartSize = new double[] {
+ part.getDiameter() / scale,
+ part.getLength() / scale};
+ int bufferedImageMaxSize = (int) Math.hypot(
+ scaledPartSize[0],
+ scaledPartSize[1]);
+
+ //buffered image stuff
+ int[]
+ bufferedImageSize = new int[] {
+ bufferedImageMaxSize,
+ bufferedImageMaxSize},
+ bufferedImageCenter = new int[] {
+ bufferedImageSize[0] / 2,
+ bufferedImageSize[1] / 2};
+
+ //create image and apply transform
+ BufferedImage image = new BufferedImage(
+ bufferedImageSize[0], bufferedImageSize[1],
+ BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g2 = image.createGraphics();
+ g2.transform(getTransform(angle, bufferedImageCenter));
+
+ //paint on image
+ paintPartOnImage(g2, bufferedImageCenter, scale, part);
+
+ //paint image
+ int[] bufferedImageLocation = new int[] {
+ (int) partDrawLoc[0] - bufferedImageSize[0] / 2,
+ (int) partDrawLoc[1] - bufferedImageSize[1] / 2};
+ g.drawImage(image, bufferedImageLocation[0], bufferedImageLocation[1], imageObserver);
+
+ //test square
+ g.setColor(Color.red);
+ g.drawRect(
+ bufferedImageLocation[0], bufferedImageLocation[1],
+ bufferedImageSize[0], bufferedImageSize[1]);
}
- private void paintVehiclePart(Graphics2D g2, VehiclePart part,
- double[] partDrawLoc, double[] scaledPartSize) {
+ private void paintPartOnImage(Graphics2D g2, int[] imageCenter, double scale, VehiclePart part) {
+ double[] scaledPartSize = new double[] {
+ part.getDiameter() / scale,
+ part.getLength() / scale};
g2.setColor(PART_COLOR);
g2.fillRect(
- (int) partDrawLoc[0], (int) partDrawLoc[1],
+ (int) (imageCenter[0] - scaledPartSize[0] / 2),
+ (int) (imageCenter[1] - scaledPartSize[1] / 2),
(int) scaledPartSize[0], (int) scaledPartSize[1]);
g2.setColor(PART_BORDER_COLOR);
g2.drawRect(
- (int) partDrawLoc[0], (int) partDrawLoc[1],
+ (int) (imageCenter[0] - scaledPartSize[0] / 2),
+ (int) (imageCenter[1] - scaledPartSize[1] / 2),
(int) scaledPartSize[0], (int) scaledPartSize[1]);
- paintPartInfo(g2, part, partDrawLoc, scaledPartSize);
}
- private void paintPartInfo(Graphics2D g2, VehiclePart part,
- double[] partDrawLoc, double[] scaledPartSize) {
- g2.setColor(PART_TEXT_COLOR);
+ private void paintPartInfo(Graphics g, VehiclePart part, double[] partDrawLoc) {
int[] textOffset = new int[] {-35, 5};
+ g.setColor(PART_TEXT_COLOR);
- //title
- g2.setFont(PART_TITLE_FONT);
- g2.drawString(
+ //part title
+ g.setFont(PART_TITLE_FONT);
+ g.drawString(
part.getName(),
- (int) (partDrawLoc[0] + scaledPartSize[0] / 2 + textOffset[0]),
- (int) (partDrawLoc[1] + scaledPartSize[1] / 2 + textOffset[1]));
-
- //details
- g2.setFont(PART_DETAILS_FONT);
- String massString = "M: " + convertMassToTonnes(part.getMass()) + " t";
- g2.drawString(
- massString,
- (int) (partDrawLoc[0] + scaledPartSize[0] / 2 + textOffset[0]),
- (int) (partDrawLoc[1] + scaledPartSize[1] / 2 + textOffset[1]) + 20);
-
- String sizeString = "L: " + roundLength(part.getLength()) + " m";
- g2.drawString(
- sizeString,
- (int) (partDrawLoc[0] + scaledPartSize[0] / 2 + textOffset[0]),
- (int) (partDrawLoc[1] + scaledPartSize[1] / 2 + textOffset[1]) + 40);
+ (int) partDrawLoc[0] + textOffset[0],
+ (int) partDrawLoc[1] + textOffset[1]);
+
+ //part details
+ g.setFont(PART_DETAILS_FONT);
+ String massString = "M: " + ValueConvertor.convertMassToTonnes(part.getMass()) + " t";
+ g.drawString(massString,
+ (int) partDrawLoc[0] + textOffset[0],
+ (int) partDrawLoc[1] + textOffset[1] + 15);
+ String sizeString = "L: " + ValueConvertor.roundLength(part.getLength()) + " m";
+ g.drawString(sizeString,
+ (int) partDrawLoc[0] + textOffset[0],
+ (int) partDrawLoc[1] + textOffset[1] + 30);
}
- private double convertMassToTonnes(double mass) {
- return roundToDecimalDigits(mass / 1000, PART_MASS_DECIMAL_DIGITS);
+ //for rotation
+ private AffineTransform getTransform(double angle, int[] center) {
+ return new AffineTransform() {{
+ translate(center[0], center[1]);
+ rotate(angle); //already in radians
+ translate(-center[0], -center[1]);
+ }};
}
- private double roundLength(double length) {
- return roundToDecimalDigits(length, PART_LENGTH_DECIMAL_DIGITS);
- }
+ //
+ static class ValueConvertor {
+ private static final int
+ PART_MASS_DECIMAL_DIGITS = 2,
+ PART_LENGTH_DECIMAL_DIGITS = 1;
+
+ //
+ static double convertMassToTonnes(double mass) {
+ return roundToDecimalDigits(mass / 1000, PART_MASS_DECIMAL_DIGITS);
+ }
+
+ //
+ static double roundLength(double length) {
+ return roundToDecimalDigits(length, PART_LENGTH_DECIMAL_DIGITS);
+ }
- private double roundToDecimalDigits(double value, int decimalDigits) {
- return ((int) (value * Math.pow(10, decimalDigits))) / Math.pow(10.0, decimalDigits);
+ //
+ static double roundToDecimalDigits(double value, int decimalDigits) {
+ return ((int) (value * Math.pow(10, decimalDigits))) / Math.pow(10.0, decimalDigits);
+ }
}
}
\ No newline at end of file
diff --git a/src/main/java/vehicleGraphics/VehiclePanel.java b/src/main/java/vehicleGraphics/VehiclePanel.java
index e876fcc..54d801f 100644
--- a/src/main/java/vehicleGraphics/VehiclePanel.java
+++ b/src/main/java/vehicleGraphics/VehiclePanel.java
@@ -1,10 +1,12 @@
package vehicleGraphics;
+import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
//
public class VehiclePanel extends JPanel {
+ private static final double VEHICLE_DRAW_ANGLE = 33;
private final VehiclePanelData panelData;
private final PainterInterface
borderPainter,
@@ -16,9 +18,18 @@ public VehiclePanel(VehiclePanelData panelData) {
super();
this.panelData = panelData;
setBackground(panelData.getBackgroundColor());
- borderPainter = new BorderPainter(panelData, VehiclePanelData.getOptimalSize());
+
+ borderPainter = new BorderPainter(panelData);
rotationTestPainter = new RotationTestPainter(this);
- vehiclePainter = new VehiclePainter(this, this.panelData);
+ vehiclePainter = new VehiclePainter(this, this.panelData, VEHICLE_DRAW_ANGLE);
+ }
+
+ //
+ @Override
+ public void update(Graphics g) {
+ super.update(g);
+ Dimension size = getSize();
+ panelData.setPanelSize(size.width, size.height);
}
//
diff --git a/src/main/java/vehicleGraphics/VehiclePanelData.java b/src/main/java/vehicleGraphics/VehiclePanelData.java
index 81c445d..f029578 100644
--- a/src/main/java/vehicleGraphics/VehiclePanelData.java
+++ b/src/main/java/vehicleGraphics/VehiclePanelData.java
@@ -7,23 +7,29 @@
//
public class VehiclePanelData {
- private static final Dimension
- OPTIMAL_SIZE = new Dimension(1000, 900);
private static final Color
BACKGROUND = Color.BLACK,
BORDER_COLOR = Color.RED;
private static final int BORDER_OFFSET = 0;
+ private final VehicleAdapter vehicleAdapter;
+ private final Dimension panelSize;
+
//
- public static Dimension getOptimalSize() {
- return OPTIMAL_SIZE;
+ public VehiclePanelData(VehicleAdapter vehicleAdapter, Dimension panelSize) {
+ this.vehicleAdapter = vehicleAdapter;
+ this.panelSize = panelSize;
}
- private final VehicleAdapter vehicleAdapter;
+ //
+ public void setPanelSize(int width, int height) {
+ panelSize.width = width;
+ panelSize.height = height;
+ }
//
- public VehiclePanelData(VehicleAdapter vehicleAdapter) {
- this.vehicleAdapter = vehicleAdapter;
+ public Dimension getPanelSize() {
+ return panelSize;
}
//
diff --git a/src/main/java/vehicles/Cockpit.java b/src/main/java/vehicles/Cockpit.java
new file mode 100644
index 0000000..a31350b
--- /dev/null
+++ b/src/main/java/vehicles/Cockpit.java
@@ -0,0 +1,41 @@
+package vehicles;
+
+import mathUtilities.Cylinders;
+
+//
+class Cockpit extends VehiclePart {
+ private static final String PART_NAME = "Cockpit";
+ private static final double
+ MINIMUM_LENGTH = 1.0, // arbitrary
+ MINIMUM_DIAMETER = 3.0, // arbitrary
+ VOLUME_PER_PASSENGER = 3.0, // arbitrary
+ PART_DENSITY = 500; // arbitrary
+
+ //use this for creating new cockpit
+ @SuppressWarnings("SameParameterValue")
+ static Cockpit newCockpit(double targetDiameter, int passengerCount) {
+ double
+ finalDiameter = calculateFinalDiameter(targetDiameter),
+ finalLength = calculateFinalLength(finalDiameter, passengerCount),
+ mass = calculateMass(finalDiameter, finalLength);
+ return new Cockpit(mass, finalLength, finalDiameter);
+ }
+
+ private Cockpit(double mass, double length, double diameter) {
+ super(PART_NAME, mass, length, diameter);
+ }
+
+ private static double calculateFinalDiameter(double targetDiameter) {
+ return Math.max(targetDiameter, MINIMUM_DIAMETER);
+ }
+
+ private static double calculateFinalLength(double diameter, int passengerCount) {
+ return Math.max(
+ Cylinders.getLength(passengerCount * VOLUME_PER_PASSENGER, diameter),
+ MINIMUM_LENGTH);
+ }
+
+ private static double calculateMass(double diameter, double length) {
+ return PART_DENSITY * Cylinders.getVolume(diameter, length);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/vehicles/SimpleVehicle.java b/src/main/java/vehicles/SimpleVehicle.java
index d80c0d7..e3583ce 100644
--- a/src/main/java/vehicles/SimpleVehicle.java
+++ b/src/main/java/vehicles/SimpleVehicle.java
@@ -3,8 +3,17 @@
import java.util.List;
import java.util.ArrayList;
+import vehicles.engines.EngineType;
+import vehicles.engines.Engine;
+
//
public class SimpleVehicle extends Vehicle {
+ private static final int PASSENGER_COUNT = 5;
+ private static final EngineType ENGINE_TYPE = EngineType.CHEMICAL;
+ private static final double
+ ENGINE_THRUST_KN = 1000, // in kN; arbitrary
+ TARGET_DIAMETER = 3.0;
+
//
public SimpleVehicle() {
super();
@@ -13,43 +22,46 @@ public SimpleVehicle() {
//
@Override
List getInitialParts() {
+ double thrust_SI = ENGINE_THRUST_KN * 1000;
return new ArrayList<>() {{
- add(new Cockpit());
- add(new Storage());
- add(new FuelTanks());
- add(new Engines());
+ add(Cockpit.newCockpit(TARGET_DIAMETER, PASSENGER_COUNT));
+ add(new Storage(TARGET_DIAMETER));
+ add(new FuelTanks(TARGET_DIAMETER));
+ add(Engine.newEngine(ENGINE_TYPE, thrust_SI));
}};
}
//
- private static class Cockpit extends VehiclePart {
+ private static class Storage extends VehiclePart {
+ private static final String PART_NAME = "Storage";
+ private static final double
+ LENGTH = 5,
+ MASS = 10;
+
//
- Cockpit() {
- super("Cockpit", 10, 2, 3);
+ Storage(double diameter) {
+ super(PART_NAME, calculateMass(), LENGTH, diameter);
}
- }
- //
- private static class Storage extends VehiclePart {
- //
- Storage() {
- super("Storage", 10, 5, 3);
+ private static double calculateMass() {
+ return MASS;
}
}
//
private static class FuelTanks extends VehiclePart {
+ private static final String PART_NAME = "Fuel tanks";
+ private static final double
+ LENGTH = 20,
+ MASS = 10;
+
//
- FuelTanks() {
- super("Fuel Tanks", 10, 20, 3);
+ FuelTanks(double diameter) {
+ super(PART_NAME, calculateMass(), LENGTH, diameter);
}
- }
- //
- private static class Engines extends VehiclePart {
- //
- Engines() {
- super("Engines", 10, 1, 3);
+ private static double calculateMass() {
+ return MASS;
}
}
}
\ No newline at end of file
diff --git a/src/main/java/vehicles/Vehicle.java b/src/main/java/vehicles/Vehicle.java
index 82058f5..4fb5543 100644
--- a/src/main/java/vehicles/Vehicle.java
+++ b/src/main/java/vehicles/Vehicle.java
@@ -1,14 +1,29 @@
package vehicles;
import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+
+import vehicles.engines.Engine;
//
public abstract class Vehicle {
private final List parts;
+ private final Map partLocationMap;
+ private final double exhaustVelocity;
//
public Vehicle() {
parts = getInitialParts();
+ partLocationMap = new HashMap<>();
+ setPartLocationInfo();
+
+ try {
+ Engine engine = getEngine();
+ exhaustVelocity = engine.getExhaustVelocity();
+ } catch (NoEngineFoundException e) {
+ throw new RuntimeException(e);
+ }
}
//must be ordered for now
@@ -18,19 +33,56 @@ public List getParts() {
return parts;
}
+ private void setPartLocationInfo() {
+ double x = getLength() / 2; // {0, 0} at amidship
+ for (VehiclePart part : parts) {
+ double partLength = part.getLength();
+ partLocationMap.put(part, new VehiclePartLocationInfo(new double[] {
+ x - partLength / 2,
+ 0}
+ ));
+ x -= partLength;
+ }
+ }
+
+ public VehiclePartLocationInfo getPartInfo(VehiclePart part) {
+ return partLocationMap.get(part);
+ }
+
+ //
public double getMass() {
double total = 0;
- for(VehiclePart part : parts) {
+ for (VehiclePart part : parts) {
total += part.getMass();
}
return total;
}
+ //
public double getLength() {
double total = 0;
- for(VehiclePart part : parts) {
+ for (VehiclePart part : parts) {
total += part.getLength();
}
return total;
}
+
+ private Engine getEngine() throws NoEngineFoundException {
+ for (VehiclePart part : parts) {
+ if (part instanceof Engine) {
+ return (Engine) part;
+ }
+ }
+ throw new NoEngineFoundException();
+ }
+
+ private static class NoEngineFoundException extends Exception {
+ NoEngineFoundException() {
+ super("No engine found!");
+ }
+ }
+
+ public double getExhaustVelocity() {
+ return exhaustVelocity;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/vehicles/VehiclePart.java b/src/main/java/vehicles/VehiclePart.java
index adb29a0..ed0a248 100644
--- a/src/main/java/vehicles/VehiclePart.java
+++ b/src/main/java/vehicles/VehiclePart.java
@@ -9,7 +9,7 @@ public class VehiclePart {
diameter;
//
- VehiclePart(String name, int mass, int length, int diameter) {
+ public VehiclePart(String name, double mass, double length, double diameter) {
this.name = name;
this.mass = mass;
this.length = length;
diff --git a/src/main/java/vehicles/VehiclePartLocationInfo.java b/src/main/java/vehicles/VehiclePartLocationInfo.java
new file mode 100644
index 0000000..3e1c28f
--- /dev/null
+++ b/src/main/java/vehicles/VehiclePartLocationInfo.java
@@ -0,0 +1,16 @@
+package vehicles;
+
+//
+public class VehiclePartLocationInfo {
+ private final double[] location;
+
+ //
+ public VehiclePartLocationInfo(double[] location) {
+ this.location = location;
+ }
+
+ //
+ public double[] getLocation() {
+ return location;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/vehicles/engines/Engine.java b/src/main/java/vehicles/engines/Engine.java
new file mode 100644
index 0000000..f4218e7
--- /dev/null
+++ b/src/main/java/vehicles/engines/Engine.java
@@ -0,0 +1,39 @@
+package vehicles.engines;
+
+import vehicles.VehiclePart;
+import vehicles.engines.chemical.ChemicalEngine;
+
+//
+public abstract class Engine extends VehiclePart {
+ private static final String PART_NAME = "Engine";
+
+ //use this for creating new engines
+ @SuppressWarnings({"SameParameterValue", "UnnecessaryDefault"})
+ public static Engine newEngine(EngineType type, double thrust) {
+ System.out.println("Creating new " + type + " engine.");
+ System.out.println("Thrust: " + (((int) (thrust / 1000 / 10)) * 10.0) + " kN");
+
+ try {
+ Engine engine = switch (type) {
+ case CHEMICAL -> ChemicalEngine.newChemicalEngine(thrust);
+ case ION -> IonEngine.newIonEngine(thrust);
+ default -> throw new UnsupportedEngineTypeException();
+ };
+ double
+ lengthRounded = ((int) (engine.getLength() * 1000)) / 1000.0,
+ diameterRounded = ((int) (engine.getDiameter() * 1000)) / 1000.0;
+ System.out.println("Total engine size: " + lengthRounded + " m x " + diameterRounded + " m");
+ return engine;
+ } catch (UnsupportedEngineTypeException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ //
+ protected Engine(double mass, double length, double diameter) {
+ super(PART_NAME, mass, length, diameter);
+ }
+
+ //
+ public abstract double getExhaustVelocity();
+}
\ No newline at end of file
diff --git a/src/main/java/vehicles/engines/EngineType.java b/src/main/java/vehicles/engines/EngineType.java
new file mode 100644
index 0000000..6093f94
--- /dev/null
+++ b/src/main/java/vehicles/engines/EngineType.java
@@ -0,0 +1,7 @@
+package vehicles.engines;
+
+//TODO: add javadoc
+public enum EngineType {
+ CHEMICAL,
+ ION
+}
\ No newline at end of file
diff --git a/src/main/java/vehicles/engines/Exhaust.java b/src/main/java/vehicles/engines/Exhaust.java
new file mode 100644
index 0000000..6ec6177
--- /dev/null
+++ b/src/main/java/vehicles/engines/Exhaust.java
@@ -0,0 +1,33 @@
+package vehicles.engines;
+
+import mathUtilities.Circles;
+
+//
+public class Exhaust {
+ private final double
+ crossSection,
+ diameter,
+ massFlowRate;
+
+ //
+ public Exhaust(double thrust, double velocity, double propellantDensity) {
+ crossSection = thrust / (propellantDensity * Math.pow(velocity, 2));
+ diameter = Circles.getRadiusFromArea(crossSection) * 2;
+ massFlowRate = velocity * propellantDensity * crossSection;
+ }
+
+ //
+ public double getCrossSection() {
+ return crossSection;
+ }
+
+ //
+ public double getDiameter() {
+ return diameter;
+ }
+
+ //
+ public double getMassFlowRate() {
+ return massFlowRate;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/vehicles/engines/IonEngine.java b/src/main/java/vehicles/engines/IonEngine.java
new file mode 100644
index 0000000..380583c
--- /dev/null
+++ b/src/main/java/vehicles/engines/IonEngine.java
@@ -0,0 +1,34 @@
+package vehicles.engines;
+
+import vehicles.engines.propellants.Propellant;
+import vehicles.engines.propellants.SodiumPropellant;
+
+//
+public class IonEngine extends Engine {
+ private static final double
+ EXHAUST_VELOCITY = 20000, // arbitrary
+ EXHAUST_TEMPERATURE = 5000, // arbitrary
+ EXHAUST_PRESSURE = 0.1 * 10000, // arbitrary
+ HARDCODED_LENGTH = 2, // arbitrary
+ HARDCODED_MASS = 10000; // arbitrary
+ private static final Propellant PROPELLANT = new SodiumPropellant();
+
+ //
+ protected static IonEngine newIonEngine(double thrust) {
+ Exhaust exhaust = new Exhaust(
+ thrust, EXHAUST_VELOCITY,
+ PROPELLANT.getDensity(EXHAUST_TEMPERATURE, EXHAUST_PRESSURE));
+ return new IonEngine(HARDCODED_MASS, HARDCODED_LENGTH, exhaust.getDiameter());
+ }
+
+ //
+ private IonEngine(double mass, double length, double diameter) {
+ super(mass, length, diameter);
+ }
+
+ //
+ @Override
+ public double getExhaustVelocity() {
+ return EXHAUST_VELOCITY;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/vehicles/engines/UnsupportedEngineTypeException.java b/src/main/java/vehicles/engines/UnsupportedEngineTypeException.java
new file mode 100644
index 0000000..db06015
--- /dev/null
+++ b/src/main/java/vehicles/engines/UnsupportedEngineTypeException.java
@@ -0,0 +1,8 @@
+package vehicles.engines;
+
+//TODO: add javadoc
+class UnsupportedEngineTypeException extends Exception {
+ protected UnsupportedEngineTypeException() {
+ super("Unsupported engine type!");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/vehicles/engines/chemical/ChemicalEngine.java b/src/main/java/vehicles/engines/chemical/ChemicalEngine.java
new file mode 100644
index 0000000..15415a4
--- /dev/null
+++ b/src/main/java/vehicles/engines/chemical/ChemicalEngine.java
@@ -0,0 +1,58 @@
+package vehicles.engines.chemical;
+
+import vehicles.engines.Engine;
+import vehicles.engines.propellants.Propellant;
+import vehicles.engines.propellants.WaterPropellant;
+
+//
+public class ChemicalEngine extends Engine {
+ private static final double
+ EXHAUST_VELOCITY = 5000, // average, approximate
+ EXHAUST_TEMPERATURE = 2000, // arbitrary
+ EXHAUST_PRESSURE = 0.7 * 10000, // arbitrary
+ HARDCODED_MASS = 5000; // arbitrary
+ private static final Propellant PROPELLANT = new WaterPropellant();
+
+ private final CombustionChamber combustionChamber;
+ private final Nozzle nozzle;
+
+ //use this for creating new chemical engine
+ public static ChemicalEngine newChemicalEngine(double thrust) {
+ Nozzle nozzle = new Nozzle(
+ PROPELLANT,
+ thrust, EXHAUST_VELOCITY,
+ EXHAUST_TEMPERATURE, EXHAUST_PRESSURE);
+ double throatDiameter = nozzle.getThroatDiameter();
+ CombustionChamber combustionChamber = new CombustionChamber(throatDiameter);
+
+ double
+ finalDiameter = Math.max(combustionChamber.getDiameter(), nozzle.getDiameter()),
+ finalLength = combustionChamber.getDiameter() + nozzle.getLength();
+ return new ChemicalEngine(
+ HARDCODED_MASS, finalLength, finalDiameter,
+ combustionChamber, nozzle);
+ }
+
+ private ChemicalEngine(double mass, double length, double diameter,
+ CombustionChamber combustionChamber, Nozzle nozzle) {
+ super(mass, length, diameter);
+ this.combustionChamber = combustionChamber;
+ this.nozzle = nozzle;
+ }
+
+ //
+ @Override
+ public double getExhaustVelocity() {
+ return EXHAUST_VELOCITY;
+ }
+
+ //
+ public CombustionChamber getCombustionChamber() {
+ return combustionChamber;
+ }
+
+ //
+ public Nozzle getNozzle() {
+ return nozzle;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/vehicles/engines/chemical/CombustionChamber.java b/src/main/java/vehicles/engines/chemical/CombustionChamber.java
new file mode 100644
index 0000000..bdc0090
--- /dev/null
+++ b/src/main/java/vehicles/engines/chemical/CombustionChamber.java
@@ -0,0 +1,26 @@
+package vehicles.engines.chemical;
+
+//a spherical combustion chamber
+public class CombustionChamber {
+ private static final double DIAMETER_THROAT_RATIO = 2.0; // arbitrary
+
+ private final double diameter;
+
+ //
+ protected CombustionChamber(double throatDiameter) {
+ diameter = throatDiameter * DIAMETER_THROAT_RATIO;
+ printParametersToConsole(diameter);
+ }
+
+ private static void printParametersToConsole(double diameter) {
+ double
+ diameter_mm = diameter * 1000,
+ diameter_mm_rounded = ((int) (diameter_mm * 100)) / 100.0;
+ System.out.println("Combustion chamber diameter: " + diameter_mm_rounded + " mm");
+ }
+
+ //
+ public double getDiameter() {
+ return diameter;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/vehicles/engines/chemical/Nozzle.java b/src/main/java/vehicles/engines/chemical/Nozzle.java
new file mode 100644
index 0000000..dbf6860
--- /dev/null
+++ b/src/main/java/vehicles/engines/chemical/Nozzle.java
@@ -0,0 +1,64 @@
+package vehicles.engines.chemical;
+
+import vehicles.engines.Exhaust;
+import vehicles.engines.propellants.Propellant;
+
+//
+public class Nozzle {
+ private static final double
+ DIAMETER_THROAT_RATIO = 50.0, // arbitrary
+ LENGTH_DIAMETER_RATIO = 1 / Math.tan(Math.toRadians(15)); // approximate
+ private final double
+ diameter,
+ length;
+
+ //
+ protected Nozzle(Propellant propellant,
+ double thrust, double exhaustVelocity,
+ double exhaustTemperature, double exhaustPressure) {
+ double propellantDensityAtExhaust = propellant.getDensity(exhaustTemperature, exhaustPressure);
+ Exhaust exhaust = new Exhaust(thrust, exhaustVelocity, propellantDensityAtExhaust);
+ diameter = exhaust.getDiameter();
+ length = diameter * LENGTH_DIAMETER_RATIO;
+
+ printParametersToConsole(
+ propellantDensityAtExhaust,
+ length, diameter,
+ getThroatDiameter());
+ }
+
+ private static void printParametersToConsole(double propellantDensityAtExhaust,
+ double length, double diameter,
+ double throatDiameter) {
+ //propellant density
+ double propellantDensity_rounded = ((int) (propellantDensityAtExhaust * 1000)) / 1000.0;
+ System.out.println("Propellant density at exhaust: " + propellantDensity_rounded + " kg/m3");
+
+ //length x diameter
+ double
+ length_rounded = ((int) (length * 1000)) / 1000.0,
+ diameter_rounded = ((int) (diameter * 1000)) / 1000.0;
+ System.out.println("Nozzle size: " + length_rounded + " m x " + diameter_rounded + " m");
+
+ //throat diameter
+ double
+ throatDiameter_mm = throatDiameter * 1000,
+ throatDiameter_mm_rounded = ((int) (throatDiameter_mm * 100)) / 100.0;
+ System.out.println("Throat diameter: " + throatDiameter_mm_rounded + " mm");
+ }
+
+ //
+ public double getDiameter() {
+ return diameter;
+ }
+
+ //
+ public double getLength() {
+ return length;
+ }
+
+ //
+ public double getThroatDiameter() {
+ return diameter / DIAMETER_THROAT_RATIO;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/vehicles/engines/propellants/CarbonDioxidePropellant.java b/src/main/java/vehicles/engines/propellants/CarbonDioxidePropellant.java
new file mode 100644
index 0000000..175bb5b
--- /dev/null
+++ b/src/main/java/vehicles/engines/propellants/CarbonDioxidePropellant.java
@@ -0,0 +1,11 @@
+package vehicles.engines.propellants;
+
+//for chemical engine
+public class CarbonDioxidePropellant extends Propellant {
+ private static final double MOLAR_MASS = 44.0 / 1000; // approximate
+
+ //
+ public CarbonDioxidePropellant() {
+ super(MOLAR_MASS);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/vehicles/engines/propellants/PotassiumPropellant.java b/src/main/java/vehicles/engines/propellants/PotassiumPropellant.java
new file mode 100644
index 0000000..aeb8bfd
--- /dev/null
+++ b/src/main/java/vehicles/engines/propellants/PotassiumPropellant.java
@@ -0,0 +1,11 @@
+package vehicles.engines.propellants;
+
+//for ion engine
+public class PotassiumPropellant extends Propellant {
+ private static final double MOLAR_MASS = 39.0 / 1000; // approximate
+
+ //
+ public PotassiumPropellant() {
+ super(MOLAR_MASS);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/vehicles/engines/propellants/Propellant.java b/src/main/java/vehicles/engines/propellants/Propellant.java
new file mode 100644
index 0000000..ae73f4e
--- /dev/null
+++ b/src/main/java/vehicles/engines/propellants/Propellant.java
@@ -0,0 +1,17 @@
+package vehicles.engines.propellants;
+
+//
+public class Propellant {
+ private static final double GAS_CONSTANT = 8.31;
+ private final double molarMass;
+
+ //
+ public Propellant(double molarMass) {
+ this.molarMass = molarMass;
+ }
+
+ // ro = M * p / (R * T)
+ public double getDensity(double temperature, double pressure) {
+ return molarMass * pressure / (GAS_CONSTANT * temperature);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/vehicles/engines/propellants/SodiumPropellant.java b/src/main/java/vehicles/engines/propellants/SodiumPropellant.java
new file mode 100644
index 0000000..0896086
--- /dev/null
+++ b/src/main/java/vehicles/engines/propellants/SodiumPropellant.java
@@ -0,0 +1,11 @@
+package vehicles.engines.propellants;
+
+//for ion engine
+public class SodiumPropellant extends Propellant {
+ private static final double MOLAR_MASS = 23.0 / 1000; // approximate
+
+ //
+ public SodiumPropellant() {
+ super(MOLAR_MASS);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/vehicles/engines/propellants/WaterPropellant.java b/src/main/java/vehicles/engines/propellants/WaterPropellant.java
new file mode 100644
index 0000000..a57d473
--- /dev/null
+++ b/src/main/java/vehicles/engines/propellants/WaterPropellant.java
@@ -0,0 +1,11 @@
+package vehicles.engines.propellants;
+
+//for chemical engine
+public class WaterPropellant extends Propellant {
+ private static final double MOLAR_MASS = 18.0 / 1000; // approximate
+
+ //
+ public WaterPropellant() {
+ super(MOLAR_MASS);
+ }
+}
\ No newline at end of file