Merge pull request #1916 from hcraigmiller/Change-side-view-to-top-view

[#1893] Add top view option to 2D display
This commit is contained in:
Sibo Van Gool 2022-12-26 14:36:42 +01:00 committed by GitHub
commit 5b73925634
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 78 additions and 59 deletions

View File

@ -50,6 +50,7 @@ RocketActions.MoveDownAct.ttip.Movedown = Move this component downwards.
! RocketPanel ! RocketPanel
RocketPanel.FigTypeAct.SideView = Side view RocketPanel.FigTypeAct.SideView = Side view
RocketPanel.FigTypeAct.TopView = Top view
RocketPanel.FigTypeAct.BackView = Back view RocketPanel.FigTypeAct.BackView = Back view
RocketPanel.FigTypeAct.Figure3D = 3D Figure RocketPanel.FigTypeAct.Figure3D = 3D Figure
RocketPanel.FigTypeAct.Finished = 3D Finished RocketPanel.FigTypeAct.Finished = 3D Finished

View File

@ -66,7 +66,7 @@ public class FlightEventsTest extends BaseTestCase {
// Test that the event times are correct // Test that the event times are correct
for (int i = 0; i < expectedEventTimes.length; i++) { for (int i = 0; i < expectedEventTimes.length; i++) {
assertEquals(" Flight type " + expectedEventTypes[i] + " has wrong time", assertEquals(" Flight type " + expectedEventTypes[i] + " has wrong time",
expectedEventTimes[i], eventList.get(i).getTime(), 0.001); expectedEventTimes[i], eventList.get(i).getTime(), 0.002);
} }
@ -142,7 +142,7 @@ public class FlightEventsTest extends BaseTestCase {
// Test that the event times are correct // Test that the event times are correct
for (int i = 0; i < expectedEventTimes.length; i++) { for (int i = 0; i < expectedEventTimes.length; i++) {
assertEquals(" Flight type " + expectedEventTypes[i] + " has wrong time", assertEquals(" Flight type " + expectedEventTypes[i] + " has wrong time",
expectedEventTimes[i], eventList.get(i).getTime(), 0.001); expectedEventTimes[i], eventList.get(i).getTime(), 0.002);
} }
// Test that the event sources are correct // Test that the event sources are correct

View File

@ -31,7 +31,7 @@ public class VariableTableModel extends AbstractTableModel {
//Collections.addAll(types, FlightDataType.ALL_TYPES); //Collections.addAll(types, FlightDataType.ALL_TYPES);
//for (CustomExpression expression : doc.getCustomExpressions()){ //for (CustomExpression expression : doc.getCustomExpressions()){
// types.add(expression.getType()); // types.add(expression.getCurrentViewType());
//} //}
} }

View File

@ -58,9 +58,10 @@ public class RocketFigure extends AbstractScaleFigure {
private static final String ROCKET_FIGURE_PACKAGE = "net.sf.openrocket.gui.rocketfigure"; private static final String ROCKET_FIGURE_PACKAGE = "net.sf.openrocket.gui.rocketfigure";
private static final String ROCKET_FIGURE_SUFFIX = "Shapes"; private static final String ROCKET_FIGURE_SUFFIX = "Shapes";
public static final int VIEW_SIDE=0; public static final int VIEW_TOP = 0;
public static final int VIEW_BACK=1; public static final int VIEW_SIDE = 1;
public static final int VIEW_BACK = 2;
// Width for drawing normal and selected components // Width for drawing normal and selected components
public static final double NORMAL_WIDTH = 1.0; public static final double NORMAL_WIDTH = 1.0;
@ -130,10 +131,6 @@ public class RocketFigure extends AbstractScaleFigure {
return rotation; return rotation;
} }
public Transformation getRotateTransformation() {
return axialRotation;
}
public void setRotation(double rot) { public void setRotation(double rot) {
if (MathUtil.equals(rotation, rot)) if (MathUtil.equals(rotation, rot))
return; return;
@ -142,14 +139,22 @@ public class RocketFigure extends AbstractScaleFigure {
updateFigure(); updateFigure();
fireChangeEvent(); fireChangeEvent();
} }
private Transformation getFigureRotation() {
if (currentViewType == RocketPanel.VIEW_TYPE.TopView) {
return this.axialRotation.applyTransformation(Transformation.rotate_x(-Math.PI / 2));
} else {
return this.axialRotation;
}
}
public RocketPanel.VIEW_TYPE getType() { public RocketPanel.VIEW_TYPE getCurrentViewType() {
return currentViewType; return currentViewType;
} }
public void setType(final RocketPanel.VIEW_TYPE type) { public void setType(final RocketPanel.VIEW_TYPE type) {
if (type != RocketPanel.VIEW_TYPE.BackView && type != RocketPanel.VIEW_TYPE.SideView) { if (type != RocketPanel.VIEW_TYPE.BackView && type != RocketPanel.VIEW_TYPE.SideView && type != RocketPanel.VIEW_TYPE.TopView) {
throw new IllegalArgumentException("Illegal type: " + type); throw new IllegalArgumentException("Illegal type: " + type);
} }
if (this.currentViewType == type) if (this.currentViewType == type)
@ -201,7 +206,7 @@ public class RocketFigure extends AbstractScaleFigure {
AffineTransform baseTransform = g2.getTransform(); AffineTransform baseTransform = g2.getTransform();
PriorityQueue<RocketComponentShape> figureShapes; PriorityQueue<RocketComponentShape> figureShapes;
if (currentViewType == RocketPanel.VIEW_TYPE.SideView) if (currentViewType == RocketPanel.VIEW_TYPE.SideView || currentViewType == RocketPanel.VIEW_TYPE.TopView)
figureShapes = figureShapes_side; figureShapes = figureShapes_side;
else if (currentViewType == RocketPanel.VIEW_TYPE.BackView) else if (currentViewType == RocketPanel.VIEW_TYPE.BackView)
figureShapes = figureShapes_back; figureShapes = figureShapes_back;
@ -300,11 +305,11 @@ public class RocketFigure extends AbstractScaleFigure {
// System.err.println(String.format(" mount instance: %s => %s", curMountLocation.toString(), curMotorLocation.toString() )); // System.err.println(String.format(" mount instance: %s => %s", curMountLocation.toString(), curMotorLocation.toString() ));
// rotate by figure's axial rotation: // rotate by figure's axial rotation:
curMotorLocation = this.axialRotation.transform(curMotorLocation); curMotorLocation = getFigureRotation().transform(curMotorLocation);
{ {
Shape s; Shape s;
if (currentViewType == RocketPanel.VIEW_TYPE.SideView) { if (currentViewType == RocketPanel.VIEW_TYPE.SideView || currentViewType == RocketPanel.VIEW_TYPE.TopView) {
s = new Rectangle2D.Double(curMotorLocation.x, s = new Rectangle2D.Double(curMotorLocation.x,
(curMotorLocation.y - motorRadius), (curMotorLocation.y - motorRadius),
motorLength, motorLength,
@ -354,7 +359,7 @@ public class RocketFigure extends AbstractScaleFigure {
LinkedHashSet<RocketComponent> l = new LinkedHashSet<RocketComponent>(); LinkedHashSet<RocketComponent> l = new LinkedHashSet<RocketComponent>();
PriorityQueue<RocketComponentShape> figureShapes; PriorityQueue<RocketComponentShape> figureShapes;
if (currentViewType == RocketPanel.VIEW_TYPE.SideView) if (currentViewType == RocketPanel.VIEW_TYPE.SideView || currentViewType == RocketPanel.VIEW_TYPE.TopView)
figureShapes = figureShapes_side; figureShapes = figureShapes_side;
else if (currentViewType == RocketPanel.VIEW_TYPE.BackView) else if (currentViewType == RocketPanel.VIEW_TYPE.BackView)
figureShapes = figureShapes_back; figureShapes = figureShapes_back;
@ -399,7 +404,7 @@ public class RocketFigure extends AbstractScaleFigure {
final ArrayList<InstanceContext> contextList = entry.getValue(); final ArrayList<InstanceContext> contextList = entry.getValue();
for (InstanceContext context : contextList) { for (InstanceContext context : contextList) {
final Transformation currentTransform = this.axialRotation.applyTransformation(context.transform); final Transformation currentTransform = getFigureRotation().applyTransformation(context.transform);
allShapes = addThisShape(allShapes, this.currentViewType, comp, currentTransform); allShapes = addThisShape(allShapes, this.currentViewType, comp, currentTransform);
} }
} }
@ -432,9 +437,10 @@ public class RocketFigure extends AbstractScaleFigure {
// Find the appropriate method // Find the appropriate method
switch (viewType) { switch (viewType) {
case SideView: case SideView:
case TopView:
m = Reflection.findMethod(ROCKET_FIGURE_PACKAGE, component, ROCKET_FIGURE_SUFFIX, "getShapesSide", m = Reflection.findMethod(ROCKET_FIGURE_PACKAGE, component, ROCKET_FIGURE_SUFFIX, "getShapesSide",
RocketComponent.class, Transformation.class); RocketComponent.class, Transformation.class);
break; break;
case BackView: case BackView:
m = Reflection.findMethod(ROCKET_FIGURE_PACKAGE, component, ROCKET_FIGURE_SUFFIX, "getShapesBack", m = Reflection.findMethod(ROCKET_FIGURE_PACKAGE, component, ROCKET_FIGURE_SUFFIX, "getShapesBack",
@ -503,6 +509,7 @@ public class RocketFigure extends AbstractScaleFigure {
switch (currentViewType) { switch (currentViewType) {
case SideView: case SideView:
case TopView:
subjectBounds_m = new Rectangle2D.Double(bounds.min.x, -maxR, bounds.span().x, 2 * maxR); subjectBounds_m = new Rectangle2D.Double(bounds.min.x, -maxR, bounds.span().x, 2 * maxR);
break; break;
case BackView: case BackView:
@ -528,9 +535,8 @@ public class RocketFigure extends AbstractScaleFigure {
if (currentViewType == RocketPanel.VIEW_TYPE.BackView){ if (currentViewType == RocketPanel.VIEW_TYPE.BackView){
final int newOriginX = mid_x; final int newOriginX = mid_x;
final int newOriginY = borderThickness_px.height + getHeight() / 2; final int newOriginY = borderThickness_px.height + getHeight() / 2;
originLocation_px = new Point(newOriginX, newOriginY); originLocation_px = new Point(newOriginX, newOriginY);
}else if (currentViewType == RocketPanel.VIEW_TYPE.SideView){ } else if (currentViewType == RocketPanel.VIEW_TYPE.SideView || currentViewType == RocketPanel.VIEW_TYPE.TopView) {
final int newOriginX = mid_x - (subjectWidth / 2) - (int)(subjectBounds_m.getMinX() * scale); final int newOriginX = mid_x - (subjectWidth / 2) - (int)(subjectBounds_m.getMinX() * scale);
final int newOriginY = Math.max(getHeight(), subjectHeight + 2*borderThickness_px.height )/ 2; final int newOriginY = Math.max(getHeight(), subjectHeight + 2*borderThickness_px.height )/ 2;
originLocation_px = new Point(newOriginX, newOriginY); originLocation_px = new Point(newOriginX, newOriginY);

View File

@ -2,6 +2,7 @@ package net.sf.openrocket.gui.scalefigure;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Font; import java.awt.Font;
import java.awt.Point; import java.awt.Point;
@ -91,9 +92,12 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
private static final Translator trans = Application.getTranslator(); private static final Translator trans = Application.getTranslator();
private static final Logger log = LoggerFactory.getLogger(RocketPanel.class); private static final Logger log = LoggerFactory.getLogger(RocketPanel.class);
private static final String VIEW_TYPE_SEPARATOR = "__SEPARATOR__"; // Dummy string to indicate a horizontal separator item in the view type combobox
public enum VIEW_TYPE { public enum VIEW_TYPE {
TopView(false, RocketFigure.VIEW_TOP),
SideView(false, RocketFigure.VIEW_SIDE), SideView(false, RocketFigure.VIEW_SIDE),
BackView(false, RocketFigure.VIEW_BACK), BackView(false, RocketFigure.VIEW_BACK),
SEPARATOR(false, -248), // Horizontal combobox separator dummy item
Figure3D(true, RocketFigure3d.TYPE_FIGURE), Figure3D(true, RocketFigure3d.TYPE_FIGURE),
Unfinished(true, RocketFigure3d.TYPE_UNFINISHED), Unfinished(true, RocketFigure3d.TYPE_UNFINISHED),
Finished(true, RocketFigure3d.TYPE_FINISHED); Finished(true, RocketFigure3d.TYPE_FINISHED);
@ -108,9 +112,16 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
@Override @Override
public String toString() { public String toString() {
if (type == -248) {
return VIEW_TYPE_SEPARATOR;
}
return trans.get("RocketPanel.FigTypeAct." + super.toString()); return trans.get("RocketPanel.FigTypeAct." + super.toString());
} }
public static VIEW_TYPE getDefaultViewType() {
return SideView;
}
} }
private boolean is3d; private boolean is3d;
@ -308,12 +319,16 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
JPanel ribbon = new JPanel(new MigLayout("insets 0, fill")); JPanel ribbon = new JPanel(new MigLayout("insets 0, fill"));
// View Type drop-down // View Type drop-down
ComboBoxModel<VIEW_TYPE> cm = new DefaultComboBoxModel<VIEW_TYPE>(VIEW_TYPE.values()) { ComboBoxModel<VIEW_TYPE> cm = new ViewTypeComboBoxModel(VIEW_TYPE.values(), VIEW_TYPE.getDefaultViewType()) {
@Override @Override
public void setSelectedItem(Object o) { public void setSelectedItem(Object o) {
super.setSelectedItem(o);
VIEW_TYPE v = (VIEW_TYPE) o; VIEW_TYPE v = (VIEW_TYPE) o;
if (v == VIEW_TYPE.SEPARATOR) {
return;
}
super.setSelectedItem(o);
if (v.is3d) { if (v.is3d) {
figure3d.setType(v.type); figure3d.setType(v.type);
go3D(); go3D();
@ -325,7 +340,9 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
} }
}; };
ribbon.add(new JLabel(trans.get("RocketPanel.lbl.ViewType")), "cell 0 0"); ribbon.add(new JLabel(trans.get("RocketPanel.lbl.ViewType")), "cell 0 0");
ribbon.add(new JComboBox<VIEW_TYPE>(cm), "cell 0 1"); final JComboBox viewSelector = new JComboBox(cm);
viewSelector.setRenderer(new SeparatorComboBoxRenderer(viewSelector.getRenderer()));
ribbon.add(viewSelector, "cell 0 1");
// Zoom level selector // Zoom level selector
scaleSelector = new ScaleSelector(scrollPane); scaleSelector = new ScaleSelector(scrollPane);
@ -773,7 +790,8 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
figure3d.setCP(new Coordinate(Double.NaN, Double.NaN)); figure3d.setCP(new Coordinate(Double.NaN, Double.NaN));
} }
if (figure.getType() == RocketPanel.VIEW_TYPE.SideView && length > 0) { if (length > 0 &&
((figure.getCurrentViewType() == RocketPanel.VIEW_TYPE.TopView) || (figure.getCurrentViewType() == RocketPanel.VIEW_TYPE.SideView))) {
extraCP.setPosition(cpx, cpy); extraCP.setPosition(cpx, cpy);
extraCG.setPosition(cgx, cgy); extraCG.setPosition(cgx, cgy);
} else { } else {
@ -1061,40 +1079,34 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
figure3d.setSelection(components); figure3d.setSelection(components);
} }
// /** private static class ViewTypeComboBoxModel extends DefaultComboBoxModel<VIEW_TYPE> {
// * An <code>Action</code> that shows whether the figure type is the public ViewTypeComboBoxModel(VIEW_TYPE[] items, VIEW_TYPE initialItem) {
// type super(items);
// * given in the constructor. super.setSelectedItem(initialItem);
// * }
// * @author Sampo Niskanen <sampo.niskanen@iki.fi> }
// */
// private class FigureTypeAction extends AbstractAction implements /**
// StateChangeListener { * Custom combobox renderer that supports the display of horizontal separators between items.
// private static final long serialVersionUID = 1L; * ComboBox objects with the text {@link VIEW_TYPE_SEPARATOR} objects in the combobox are replaced by a separator object.
// private final VIEW_TYPE type; */
// private static class SeparatorComboBoxRenderer extends JLabel implements ListCellRenderer {
// public FigureTypeAction(VIEW_TYPE type) { private final JSeparator separator;
// this.type = type; private final ListCellRenderer defaultRenderer;
// stateChanged(null);
// figure.addChangeListener(this); public SeparatorComboBoxRenderer(ListCellRenderer defaultRenderer) {
// } this.defaultRenderer = defaultRenderer;
// this.separator = new JSeparator(JSeparator.HORIZONTAL);
// @Override }
// public void actionPerformed(ActionEvent e) {
// boolean state = (Boolean) getValue(Action.SELECTED_KEY); public Component getListCellRendererComponent(JList list, Object value,
// if (state == true) { int index, boolean isSelected, boolean cellHasFocus) {
// // This view has been selected String str = (value == null) ? "" : value.toString();
// figure.setType(type); if (VIEW_TYPE_SEPARATOR.equals(str)) {
// go2D(); return separator;
// updateExtras(); };
// } return defaultRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
// stateChanged(null); }
// } }
//
// @Override
// public void stateChanged(EventObject e) {
// putValue(Action.SELECTED_KEY, figure.getType() == type && !is3d);
// }
// }
} }