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