diff --git a/core/src/main/java/info/openrocket/core/aerodynamics/AerodynamicForces.java b/core/src/main/java/info/openrocket/core/aerodynamics/AerodynamicForces.java
index 2aff94005..fd19e0f9f 100644
--- a/core/src/main/java/info/openrocket/core/aerodynamics/AerodynamicForces.java
+++ b/core/src/main/java/info/openrocket/core/aerodynamics/AerodynamicForces.java
@@ -234,6 +234,10 @@ public class AerodynamicForces implements Cloneable, Monitorable {
modID = new ModID();
}
+ /**
+ * Get the drag coefficient per instance.
+ * @return The drag coefficient.
+ */
public double getCD() {
if (component == null)
return CD;
@@ -245,6 +249,10 @@ public class AerodynamicForces implements Cloneable, Monitorable {
return CD;
}
+ public double getCDTotal() {
+ return getCD() * component.getInstanceCount();
+ }
+
public void setPressureCD(double pressureCD) {
if (this.pressureCD == pressureCD)
return;
diff --git a/core/src/main/java/info/openrocket/core/componentanalysis/CADataType.java b/core/src/main/java/info/openrocket/core/componentanalysis/CADataType.java
index c18005a17..d6639f05e 100644
--- a/core/src/main/java/info/openrocket/core/componentanalysis/CADataType.java
+++ b/core/src/main/java/info/openrocket/core/componentanalysis/CADataType.java
@@ -39,8 +39,10 @@ public class CADataType implements Comparable, Groupable, Groupable, GroupablePressure CD
ComponentAnalysisGeneralTab.dragTableModel.Col.Base = Base CD
ComponentAnalysisGeneralTab.dragTableModel.Col.friction = Friction CD
+ComponentAnalysisGeneralTab.dragTableModel.Col.perInstance = Per instance CD
ComponentAnalysisGeneralTab.dragTableModel.Col.total = Total CD
ComponentAnalysisGeneralTab.dragTabchar = Drag characteristics
ComponentAnalysisGeneralTab.dragTabchar.ttip = Drag characteristics
diff --git a/swing/src/main/java/info/openrocket/swing/gui/dialogs/componentanalysis/CAPlotTypeSelector.java b/swing/src/main/java/info/openrocket/swing/gui/dialogs/componentanalysis/CAPlotTypeSelector.java
index 9eb4c1bf6..f860750e9 100644
--- a/swing/src/main/java/info/openrocket/swing/gui/dialogs/componentanalysis/CAPlotTypeSelector.java
+++ b/swing/src/main/java/info/openrocket/swing/gui/dialogs/componentanalysis/CAPlotTypeSelector.java
@@ -33,7 +33,7 @@ public class CAPlotTypeSelector extends PlotTypeSelector(componentsForType.toArray(new RocketComponent[0]));
componentSelector.setSelectedItem(selectedComponent);
configuration.setPlotDataComponent(plotIndex, selectedComponent);
- this.add(componentSelector, "gapright para");
+ this.add(componentSelector, "growx, gapright para");
addRemoveButton();
diff --git a/swing/src/main/java/info/openrocket/swing/gui/dialogs/componentanalysis/ComponentAnalysisGeneralPanel.java b/swing/src/main/java/info/openrocket/swing/gui/dialogs/componentanalysis/ComponentAnalysisGeneralPanel.java
index 9979c317b..8d52a0338 100644
--- a/swing/src/main/java/info/openrocket/swing/gui/dialogs/componentanalysis/ComponentAnalysisGeneralPanel.java
+++ b/swing/src/main/java/info/openrocket/swing/gui/dialogs/componentanalysis/ComponentAnalysisGeneralPanel.java
@@ -38,6 +38,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.BorderFactory;
+import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
@@ -52,12 +53,17 @@ import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
+import javax.swing.plaf.LabelUI;
+import javax.swing.plaf.basic.BasicLabelUI;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableModel;
+import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@@ -333,11 +339,18 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
return dragData.get(row).getFrictionCD();
}
},
+ //// Per instance CD
+ new Column(trans.get("ComponentAnalysisGeneralTab.dragTableModel.Col.perInstance")) {
+ @Override
+ public Object getValueAt(int row) {
+ return dragData.get(row).getCD();
+ }
+ },
//// Total CD
new Column(trans.get("ComponentAnalysisGeneralTab.dragTableModel.Col.total")) {
@Override
public Object getValueAt(int row) {
- return dragData.get(row).getCD();
+ return dragData.get(row).getCDTotal();
}
}
) {
@@ -666,7 +679,8 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
private final List> data;
protected final int decimalPlaces;
- private static Color backgroundColor;
+ protected static Color backgroundColor;
+ protected static Color foregroundColor;
static {
initColors();
@@ -687,6 +701,7 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
private static void updateColors() {
backgroundColor = GUIUtil.getUITheme().getBackgroundColor();
+ foregroundColor = GUIUtil.getUITheme().getTextColor();
}
@Override
@@ -748,9 +763,12 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
private class DragCellRenderer extends CustomCellRenderer {
private static final long serialVersionUID = 1L;
+ private final StripedLabelUI stripedUI;
+ private final LabelUI defaultUI = new BasicLabelUI();
public DragCellRenderer() {
super(dragData, 3);
+ this.stripedUI = new StripedLabelUI(1, 12);
}
@Override
@@ -758,6 +776,9 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
boolean isSelected, boolean hasFocus, int row, int column) {
JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
+ // Reset to default UI
+ label.setUI(defaultUI);
+
if (!isSelected && (value instanceof Double)) {
double cd = (Double) value;
float r = (float) (cd / 1.5);
@@ -770,10 +791,26 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
label.setForeground(Color.BLACK);
}
+ // For the per instance CD, we want to use a different formatting, because the relative percentages
+ // don't matter, and the total instance CD for the rocket makes no sense.
+ if (column == 4) {
+ if (row == 0) {
+ label.setText("");
+ label.setOpaque(true);
+ label.setBackground(backgroundColor);
+ label.setForeground(foregroundColor);
+ label.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
+ label.setUI(stripedUI);
+ } else {
+ label.setText(decimalFormat(dragData.get(row).getCD()));
+ }
+ return label;
+ }
+
if ((row < 0) || (row >= dragData.size()))
return label;
- if ((dragData.get(row).getComponent() instanceof Rocket) || (column == 4)) {
+ if ((dragData.get(row).getComponent() instanceof Rocket) || (column == 5)) {
label.setFont(boldFont);
} else {
label.setFont(normalFont);
@@ -786,10 +823,46 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
protected String formatDouble(Double cd) {
final double totalCD = dragData.get(0).getCD();
- DecimalFormat df = new DecimalFormat("0." + "#".repeat(Math.max(0, decimalPlaces)));
- String cdFormatted = df.format(cd);
+ String cdFormatted = decimalFormat(cd);
return String.format(cdFormatted + " (%.0f%%)", 100 * cd / totalCD);
}
+
+ private String decimalFormat(Double cd) {
+ DecimalFormat df = new DecimalFormat("0." + "#".repeat(Math.max(0, decimalPlaces)));
+ return df.format(cd);
+ }
+
+ private static class StripedLabelUI extends BasicLabelUI {
+ private final int lineWidth;
+ private final int lineSpacing;
+
+ public StripedLabelUI(int lineWidth, int lineSpacing) {
+ this.lineWidth = lineWidth;
+ this.lineSpacing = lineSpacing;
+ }
+
+ @Override
+ public void paint(Graphics g, JComponent c) {
+ Graphics2D g2d = (Graphics2D) g.create();
+ int width = c.getWidth();
+ int height = c.getHeight();
+
+ // Paint background
+ g2d.setColor(c.getBackground());
+ g2d.fillRect(0, 0, width, height);
+
+ // Paint thin lines
+ g2d.setColor(c.getForeground());
+ g2d.setStroke(new BasicStroke(lineWidth));
+ for (int x = -height; x < width; x += (lineWidth + lineSpacing)) {
+ g2d.drawLine(x, height, x + height, 0);
+ }
+
+ g2d.dispose();
+
+ super.paint(g, c);
+ }
+ }
}
private class RollDynamicsCellRenderer extends CustomCellRenderer {