Merge pull request #2557 from SiboVG/issue-2019

[#2019] Highlight selected component in CA dialog in rocket panel  & Add per-instance CD column to component analysis dialog
This commit is contained in:
Sibo Van Gool 2024-09-10 23:30:45 +02:00 committed by GitHub
commit 6b88a998d4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 234 additions and 50 deletions

View File

@ -234,6 +234,10 @@ public class AerodynamicForces implements Cloneable, Monitorable {
modID = new ModID(); modID = new ModID();
} }
/**
* Get the drag coefficient <b>per instance</b>.
* @return The drag coefficient.
*/
public double getCD() { public double getCD() {
if (component == null) if (component == null)
return CD; return CD;
@ -245,6 +249,10 @@ public class AerodynamicForces implements Cloneable, Monitorable {
return CD; return CD;
} }
public double getCDTotal() {
return getCD() * component.getInstanceCount();
}
public void setPressureCD(double pressureCD) { public void setPressureCD(double pressureCD) {
if (this.pressureCD == pressureCD) if (this.pressureCD == pressureCD)
return; return;

View File

@ -39,8 +39,10 @@ public class CADataType implements Comparable<CADataType>, Groupable<CADataTypeG
"CD,base", UnitGroup.UNITS_NONE, CADataTypeGroup.DRAG, 21); "CD,base", UnitGroup.UNITS_NONE, CADataTypeGroup.DRAG, 21);
public static final CADataType FRICTION_CD = new CADataType(trans.get("ComponentAnalysisGeneralTab.dragTableModel.Col.friction"), public static final CADataType FRICTION_CD = new CADataType(trans.get("ComponentAnalysisGeneralTab.dragTableModel.Col.friction"),
"CD,friction", UnitGroup.UNITS_NONE, CADataTypeGroup.DRAG, 22); "CD,friction", UnitGroup.UNITS_NONE, CADataTypeGroup.DRAG, 22);
public static final CADataType PER_INSTANCE_CD = new CADataType(trans.get("ComponentAnalysisGeneralTab.dragTableModel.Col.perInstance"),
"CD,instance", UnitGroup.UNITS_NONE, CADataTypeGroup.DRAG, 23);
public static final CADataType TOTAL_CD = new CADataType(trans.get("ComponentAnalysisGeneralTab.dragTableModel.Col.total"), public static final CADataType TOTAL_CD = new CADataType(trans.get("ComponentAnalysisGeneralTab.dragTableModel.Col.total"),
"CD,total", UnitGroup.UNITS_NONE, CADataTypeGroup.DRAG, 23); "CD,total", UnitGroup.UNITS_NONE, CADataTypeGroup.DRAG, 24);
//// Roll //// Roll
public static final CADataType ROLL_FORCING_COEFFICIENT = new CADataType(trans.get("ComponentAnalysisGeneralTab.rollTableModel.Col.rollforc"), public static final CADataType ROLL_FORCING_COEFFICIENT = new CADataType(trans.get("ComponentAnalysisGeneralTab.rollTableModel.Col.rollforc"),
@ -53,7 +55,7 @@ public class CADataType implements Comparable<CADataType>, Groupable<CADataTypeG
public static final CADataType[] ALL_TYPES = { public static final CADataType[] ALL_TYPES = {
CP_X, CNa, CP_X, CNa,
PRESSURE_CD, BASE_CD, FRICTION_CD, TOTAL_CD, PRESSURE_CD, BASE_CD, FRICTION_CD, PER_INSTANCE_CD, TOTAL_CD,
ROLL_FORCING_COEFFICIENT, ROLL_DAMPING_COEFFICIENT, TOTAL_ROLL_COEFFICIENT ROLL_FORCING_COEFFICIENT, ROLL_DAMPING_COEFFICIENT, TOTAL_ROLL_COEFFICIENT
}; };
@ -124,9 +126,14 @@ public class CADataType implements Comparable<CADataType>, Groupable<CADataTypeG
return false; return false;
} }
// Doesn't make sense to calculate per-instance drag for rockets
if (component instanceof Rocket && type.equals(CADataType.PER_INSTANCE_CD)) {
return false;
}
if (type.equals(CADataType.CP_X) || type.equals(CADataType.CNa) || if (type.equals(CADataType.CP_X) || type.equals(CADataType.CNa) ||
type.equals(CADataType.PRESSURE_CD) || type.equals(CADataType.BASE_CD) || type.equals(CADataType.PRESSURE_CD) || type.equals(CADataType.BASE_CD) ||
type.equals(CADataType.FRICTION_CD) || type.equals(CADataType.TOTAL_CD)) { type.equals(CADataType.FRICTION_CD) || type.equals(CADataType.PER_INSTANCE_CD) || type.equals(CADataType.TOTAL_CD)) {
return true; return true;
} else if (type.equals(CADataType.ROLL_FORCING_COEFFICIENT) || type.equals(CADataType.ROLL_DAMPING_COEFFICIENT) || } else if (type.equals(CADataType.ROLL_FORCING_COEFFICIENT) || type.equals(CADataType.ROLL_DAMPING_COEFFICIENT) ||
type.equals(CADataType.TOTAL_ROLL_COEFFICIENT)) { type.equals(CADataType.TOTAL_ROLL_COEFFICIENT)) {

View File

@ -129,7 +129,8 @@ public class CAParameterSweep {
dataBranch.setValue(CADataType.PRESSURE_CD, component, forces.getPressureCD()); dataBranch.setValue(CADataType.PRESSURE_CD, component, forces.getPressureCD());
dataBranch.setValue(CADataType.BASE_CD, component, forces.getBaseCD()); dataBranch.setValue(CADataType.BASE_CD, component, forces.getBaseCD());
dataBranch.setValue(CADataType.FRICTION_CD, component, forces.getFrictionCD()); dataBranch.setValue(CADataType.FRICTION_CD, component, forces.getFrictionCD());
dataBranch.setValue(CADataType.TOTAL_CD, component, forces.getCD()); dataBranch.setValue(CADataType.PER_INSTANCE_CD, component, forces.getCD());
dataBranch.setValue(CADataType.TOTAL_CD, component, forces.getCDTotal());
} }
if (component instanceof FinSet) { if (component instanceof FinSet) {
@ -154,7 +155,8 @@ public class CAParameterSweep {
dataBranch.setValue(CADataType.PRESSURE_CD, rocket, totalForces.getPressureCD()); dataBranch.setValue(CADataType.PRESSURE_CD, rocket, totalForces.getPressureCD());
dataBranch.setValue(CADataType.BASE_CD, rocket, totalForces.getBaseCD()); dataBranch.setValue(CADataType.BASE_CD, rocket, totalForces.getBaseCD());
dataBranch.setValue(CADataType.FRICTION_CD, rocket, totalForces.getFrictionCD()); dataBranch.setValue(CADataType.FRICTION_CD, rocket, totalForces.getFrictionCD());
dataBranch.setValue(CADataType.TOTAL_CD, rocket, totalForces.getCD()); dataBranch.setValue(CADataType.PER_INSTANCE_CD, rocket, totalForces.getCD());
dataBranch.setValue(CADataType.TOTAL_CD, rocket, totalForces.getCDTotal());
} }
} }

View File

@ -940,6 +940,7 @@ ComponentAnalysisGeneralTab.dragTableModel.Col.Component = Component
ComponentAnalysisGeneralTab.dragTableModel.Col.Pressure = <html>Pressure C<sub>D</sub> ComponentAnalysisGeneralTab.dragTableModel.Col.Pressure = <html>Pressure C<sub>D</sub>
ComponentAnalysisGeneralTab.dragTableModel.Col.Base = <html>Base C<sub>D</sub> ComponentAnalysisGeneralTab.dragTableModel.Col.Base = <html>Base C<sub>D</sub>
ComponentAnalysisGeneralTab.dragTableModel.Col.friction = <html>Friction C<sub>D</sub> ComponentAnalysisGeneralTab.dragTableModel.Col.friction = <html>Friction C<sub>D</sub>
ComponentAnalysisGeneralTab.dragTableModel.Col.perInstance = <html>Per instance C<sub>D</sub>
ComponentAnalysisGeneralTab.dragTableModel.Col.total = <html>Total C<sub>D</sub> ComponentAnalysisGeneralTab.dragTableModel.Col.total = <html>Total C<sub>D</sub>
ComponentAnalysisGeneralTab.dragTabchar = Drag characteristics ComponentAnalysisGeneralTab.dragTabchar = Drag characteristics
ComponentAnalysisGeneralTab.dragTabchar.ttip = Drag characteristics ComponentAnalysisGeneralTab.dragTabchar.ttip = Drag characteristics

View File

@ -33,7 +33,7 @@ public class CAPlotTypeSelector extends PlotTypeSelector<CADataType, CADataTypeG
componentSelector = new JComboBox<>(componentsForType.toArray(new RocketComponent[0])); componentSelector = new JComboBox<>(componentsForType.toArray(new RocketComponent[0]));
componentSelector.setSelectedItem(selectedComponent); componentSelector.setSelectedItem(selectedComponent);
configuration.setPlotDataComponent(plotIndex, selectedComponent); configuration.setPlotDataComponent(plotIndex, selectedComponent);
this.add(componentSelector, "gapright para"); this.add(componentSelector, "growx, gapright para");
addRemoveButton(); addRemoveButton();

View File

@ -38,6 +38,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.swing.BorderFactory; import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JList; import javax.swing.JList;
import javax.swing.JPanel; import javax.swing.JPanel;
@ -50,11 +51,19 @@ import javax.swing.SwingConstants;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener; 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.DefaultTableCellRenderer;
import javax.swing.table.TableModel;
import java.awt.BasicStroke;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Font; import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Window; import java.awt.Window;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
@ -112,8 +121,6 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
this.theta = new DoubleModel(parameters, "Theta", UnitGroup.UNITS_ANGLE, 0, 2 * Math.PI); this.theta = new DoubleModel(parameters, "Theta", UnitGroup.UNITS_ANGLE, 0, 2 * Math.PI);
this.roll = new DoubleModel(parameters, "RollRate", UnitGroup.UNITS_ROLL); this.roll = new DoubleModel(parameters, "RollRate", UnitGroup.UNITS_ROLL);
JTable table;
//// Wind direction: //// Wind direction:
this.add(new JLabel(trans.get("ComponentAnalysisGeneralTab.lbl.winddir"))); this.add(new JLabel(trans.get("ComponentAnalysisGeneralTab.lbl.winddir")));
EditableSpinner spinner = new EditableSpinner(theta.getSpinnerModel()); EditableSpinner spinner = new EditableSpinner(theta.getSpinnerModel());
@ -200,7 +207,7 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
// Create the Longitudinal Stability (CM vs CP) data table // Create the Longitudinal Stability (CM vs CP) data table
this.longitudeStabilityTableModel = new ColumnTableModel( this.longitudeStabilityTableModel = new CAColumnTableModel(
//// Component //// Component
new Column(trans.get("ComponentAnalysisGeneralTab.TabStability.Col.Component")) { new Column(trans.get("ComponentAnalysisGeneralTab.TabStability.Col.Component")) {
@ -267,15 +274,24 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
public int getRowCount() { public int getRowCount() {
return stabData.size(); return stabData.size();
} }
@Override
public RocketComponent getComponentForRow(int row) {
if (row < 0 || row >= getRowCount()) {
return null;
}
Object source = stabData.get(row).source;
return source instanceof RocketComponent ? (RocketComponent) source : null;
}
}; };
table = new ColumnTable(longitudeStabilityTableModel); final JTable stabilityTable = new ColumnTable(longitudeStabilityTableModel);
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); stabilityTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
this.longitudeStabilityTableModel.setColumnWidths(table.getColumnModel()); this.longitudeStabilityTableModel.setColumnWidths(stabilityTable.getColumnModel());
table.setDefaultRenderer(Object.class, new StabilityCellRenderer()); stabilityTable.setDefaultRenderer(Object.class, new StabilityCellRenderer());
JScrollPane scrollpane = new JScrollPane(table); JScrollPane scrollpane = new JScrollPane(stabilityTable);
scrollpane.setPreferredSize(new Dimension(600, 200)); scrollpane.setPreferredSize(new Dimension(600, 200));
//// Stability and Stability information //// Stability and Stability information
@ -285,7 +301,7 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
// Create the drag data table // Create the drag data table
this.dragTableModel = new ColumnTableModel( this.dragTableModel = new CAColumnTableModel(
//// Component //// Component
new Column(trans.get("ComponentAnalysisGeneralTab.dragTableModel.Col.Component")) { new Column(trans.get("ComponentAnalysisGeneralTab.dragTableModel.Col.Component")) {
@Override @Override
@ -323,11 +339,18 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
return dragData.get(row).getFrictionCD(); return dragData.get(row).getFrictionCD();
} }
}, },
//// <html>Per instance C<sub>D</sub>
new Column(trans.get("ComponentAnalysisGeneralTab.dragTableModel.Col.perInstance")) {
@Override
public Object getValueAt(int row) {
return dragData.get(row).getCD();
}
},
//// <html>Total C<sub>D</sub> //// <html>Total C<sub>D</sub>
new Column(trans.get("ComponentAnalysisGeneralTab.dragTableModel.Col.total")) { new Column(trans.get("ComponentAnalysisGeneralTab.dragTableModel.Col.total")) {
@Override @Override
public Object getValueAt(int row) { public Object getValueAt(int row) {
return dragData.get(row).getCD(); return dragData.get(row).getCDTotal();
} }
} }
) { ) {
@ -340,16 +363,24 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
public int getRowCount() { public int getRowCount() {
return dragData.size(); return dragData.size();
} }
@Override
public RocketComponent getComponentForRow(int row) {
if (row < 0 || row >= getRowCount()) {
return null;
}
return dragData.get(row).getComponent();
}
}; };
table = new JTable(dragTableModel); final JTable dragTable = new JTable(dragTableModel);
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); dragTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
this.dragTableModel.setColumnWidths(table.getColumnModel()); this.dragTableModel.setColumnWidths(dragTable.getColumnModel());
table.setDefaultRenderer(Object.class, new DragCellRenderer()); dragTable.setDefaultRenderer(Object.class, new DragCellRenderer());
scrollpane = new JScrollPane(table); scrollpane = new JScrollPane(dragTable);
scrollpane.setPreferredSize(new Dimension(600, 200)); scrollpane.setPreferredSize(new Dimension(600, 200));
//// Drag characteristics and Drag characteristics tooltip //// Drag characteristics and Drag characteristics tooltip
@ -360,7 +391,7 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
// Create the roll data table // Create the roll data table
this.rollTableModel = new ColumnTableModel( this.rollTableModel = new CAColumnTableModel(
//// Component //// Component
new Column(trans.get("ComponentAnalysisGeneralTab.rollTableModel.Col.component")) { new Column(trans.get("ComponentAnalysisGeneralTab.rollTableModel.Col.component")) {
@Override @Override
@ -403,14 +434,22 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
public int getRowCount() { public int getRowCount() {
return rollData.size(); return rollData.size();
} }
@Override
public RocketComponent getComponentForRow(int row) {
if (row < 0 || row >= getRowCount()) {
return null;
}
return rollData.get(row).getComponent();
}
}; };
table = new JTable(rollTableModel); final JTable rollTable = new JTable(rollTableModel);
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); rollTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
table.setDefaultRenderer(Object.class, new RollDynamicsCellRenderer()); rollTable.setDefaultRenderer(Object.class, new RollDynamicsCellRenderer());
scrollpane = new JScrollPane(table); scrollpane = new JScrollPane(rollTable);
scrollpane.setPreferredSize(new Dimension(600, 200)); scrollpane.setPreferredSize(new Dimension(600, 200));
//// Roll dynamics and Roll dynamics tooltip //// Roll dynamics and Roll dynamics tooltip
@ -459,6 +498,11 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
this.roll.addChangeListener(this); this.roll.addChangeListener(this);
this.stateChanged(null); this.stateChanged(null);
// Add listeners to highlight the selected component in the rocket panel
stabilityTable.getSelectionModel().addListSelectionListener(new CAListSelectionListener(rocketPanel, stabilityTable));
dragTable.getSelectionModel().addListSelectionListener(new CAListSelectionListener(rocketPanel, dragTable));
rollTable.getSelectionModel().addListSelectionListener(new CAListSelectionListener(rocketPanel, rollTable));
// Remove listeners when closing window // Remove listeners when closing window
parent.addWindowListener(new WindowAdapter() { parent.addWindowListener(new WindowAdapter() {
@Override @Override
@ -635,7 +679,8 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
private final List<?> data; private final List<?> data;
protected final int decimalPlaces; protected final int decimalPlaces;
private static Color backgroundColor; protected static Color backgroundColor;
protected static Color foregroundColor;
static { static {
initColors(); initColors();
@ -656,6 +701,7 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
private static void updateColors() { private static void updateColors() {
backgroundColor = GUIUtil.getUITheme().getBackgroundColor(); backgroundColor = GUIUtil.getUITheme().getBackgroundColor();
foregroundColor = GUIUtil.getUITheme().getTextColor();
} }
@Override @Override
@ -717,9 +763,13 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
private class DragCellRenderer extends CustomCellRenderer { private class DragCellRenderer extends CustomCellRenderer {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final StripedLabelUI stripedUI;
private final LabelUI defaultUI;
public DragCellRenderer() { public DragCellRenderer() {
super(dragData, 3); super(dragData, 3);
this.stripedUI = new StripedLabelUI(1, 12);
this.defaultUI = new BasicLabelUI();
} }
@Override @Override
@ -727,7 +777,17 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
boolean isSelected, boolean hasFocus, int row, int column) { boolean isSelected, boolean hasFocus, int row, int column) {
JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (!isSelected && (value instanceof Double)) { // Reset to default UI
label.setUI(defaultUI);
// Handle selection coloring
if (isSelected) {
label.setBackground(table.getSelectionBackground());
label.setForeground(table.getSelectionForeground());
label.setOpaque(true);
} else {
// Non-selected styling
if (value instanceof Double) {
double cd = (Double) value; double cd = (Double) value;
float r = (float) (cd / 1.5); float r = (float) (cd / 1.5);
@ -737,12 +797,33 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
label.setBackground(Color.getHSBColor(hue, sat, val)); label.setBackground(Color.getHSBColor(hue, sat, val));
label.setForeground(Color.BLACK); label.setForeground(Color.BLACK);
} else {
label.setBackground(table.getBackground());
label.setForeground(table.getForeground());
}
label.setOpaque(true);
} }
if ((row < 0) || (row >= dragData.size())) // Special handling for column 4
return label; if (column == 4) {
if (row == 0) {
label.setText("");
label.setUI(stripedUI);
if (!isSelected) {
label.setBackground(table.getBackground());
label.setForeground(table.getForeground());
}
} else {
label.setText(decimalFormat(dragData.get(row).getCD()));
}
}
if ((dragData.get(row).getComponent() instanceof Rocket) || (column == 4)) { // Set font
if ((row < 0) || (row >= dragData.size())) {
return label;
}
if ((dragData.get(row).getComponent() instanceof Rocket) || (column == 5)) {
label.setFont(boldFont); label.setFont(boldFont);
} else { } else {
label.setFont(normalFont); label.setFont(normalFont);
@ -755,10 +836,46 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
protected String formatDouble(Double cd) { protected String formatDouble(Double cd) {
final double totalCD = dragData.get(0).getCD(); final double totalCD = dragData.get(0).getCD();
DecimalFormat df = new DecimalFormat("0." + "#".repeat(Math.max(0, decimalPlaces))); String cdFormatted = decimalFormat(cd);
String cdFormatted = df.format(cd);
return String.format(cdFormatted + " (%.0f%%)", 100 * cd / totalCD); 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 { private class RollDynamicsCellRenderer extends CustomCellRenderer {
@ -767,8 +884,7 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
} }
} }
private class LongitudinalStabilityRow { private static class LongitudinalStabilityRow {
public String name; public String name;
public Object source; public Object source;
public double eachMass; public double eachMass;
@ -784,6 +900,34 @@ public class ComponentAnalysisGeneralPanel extends JPanel implements StateChange
cpx = Double.NaN; cpx = Double.NaN;
cna = Double.NaN; cna = Double.NaN;
} }
}
private abstract static class CAColumnTableModel extends ColumnTableModel {
public CAColumnTableModel(Column... columns) {
super(columns);
}
public RocketComponent getComponentForRow(int row) {
throw new RuntimeException("Not implemented");
}
}
private static class CAListSelectionListener implements ListSelectionListener {
private final RocketPanel rocketPanel;
private final JTable table;
public CAListSelectionListener(RocketPanel rocketPanel, JTable table) {
this.rocketPanel = rocketPanel;
this.table = table;
}
@Override
public void valueChanged(ListSelectionEvent e) {
TableModel model = table.getModel();
if (model instanceof CAColumnTableModel) {
RocketComponent component = ((CAColumnTableModel) model).getComponentForRow(table.getSelectedRow());
rocketPanel.setSelectedComponent(component);
}
}
} }
} }

View File

@ -541,6 +541,15 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
valueChanged((TreeSelectionEvent) null); // updates FigureParameters valueChanged((TreeSelectionEvent) null); // updates FigureParameters
} }
public void setSelectedComponent(RocketComponent component) {
if (component == null) {
selectionModel.setSelectionPath(null);
return;
}
TreePath path = ComponentTreeModel.makeTreePath(component);
selectionModel.setSelectionPath(path);
}
/** /**
* Return the angle of attack used in CP calculation. NaN signifies the default value * Return the angle of attack used in CP calculation. NaN signifies the default value
* of zero. * of zero.
@ -672,8 +681,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
if (newClick) { if (newClick) {
for (RocketComponent rocketComponent : clicked) { for (RocketComponent rocketComponent : clicked) {
if (!selectedComponents.contains(rocketComponent)) { if (!selectedComponents.contains(rocketComponent)) {
TreePath path = ComponentTreeModel.makeTreePath(rocketComponent); setSelectedComponent(rocketComponent);
selectionModel.setSelectionPath(path);
} }
} }
} }

View File

@ -130,20 +130,34 @@ public class GroupableAndSearchableComboBox<G extends Group, T extends Groupable
} }
private Map<G, List<T>> constructItemGroupMapFromList(List<T> items) { private Map<G, List<T>> constructItemGroupMapFromList(List<T> items) {
Map<G, List<T>> itemGroupMap = new TreeMap<>(new Comparator<G>() { Map<G, List<T>> itemGroupMap = new TreeMap<>(Comparator.comparing(Group::getPriority));
@Override
public int compare(G g1, G g2) {
return Integer.compare(g1.getPriority(), g2.getPriority());
}
});
for (T item : items) { for (T item : items) {
G group = item.getGroup(); G group = item.getGroup();
itemGroupMap.computeIfAbsent(group, k -> new ArrayList<>()).add(item); itemGroupMap.computeIfAbsent(group, k -> new ArrayList<>()).add(item);
} }
// Sort items within each group
for (List<T> groupItems : itemGroupMap.values()) {
groupItems.sort(new ItemComparator());
}
return itemGroupMap; return itemGroupMap;
} }
private class ItemComparator implements Comparator<T> {
@Override
@SuppressWarnings("unchecked")
public int compare(T item1, T item2) {
if (item1 instanceof Comparable && item2 instanceof Comparable) {
return ((Comparable<T>) item1).compareTo(item2);
} else {
// Fall back to alphabetical sorting using the display string
return getDisplayString(item1).compareToIgnoreCase(getDisplayString(item2));
}
}
}
public void setupMainRenderer() { public void setupMainRenderer() {
setRenderer(new DefaultListCellRenderer() { setRenderer(new DefaultListCellRenderer() {
@Override @Override