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.FigTypeAct.SideView = Side view
RocketPanel.FigTypeAct.TopView = Top view
RocketPanel.FigTypeAct.BackView = Back view
RocketPanel.FigTypeAct.Figure3D = 3D Figure
RocketPanel.FigTypeAct.Finished = 3D Finished

View File

@ -66,7 +66,7 @@ public class FlightEventsTest extends BaseTestCase {
// Test that the event times are correct
for (int i = 0; i < expectedEventTimes.length; i++) {
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
for (int i = 0; i < expectedEventTimes.length; i++) {
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

View File

@ -31,7 +31,7 @@ public class VariableTableModel extends AbstractTableModel {
//Collections.addAll(types, FlightDataType.ALL_TYPES);
//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_SUFFIX = "Shapes";
public static final int VIEW_SIDE=0;
public static final int VIEW_BACK=1;
public static final int VIEW_TOP = 0;
public static final int VIEW_SIDE = 1;
public static final int VIEW_BACK = 2;
// Width for drawing normal and selected components
public static final double NORMAL_WIDTH = 1.0;
@ -130,10 +131,6 @@ public class RocketFigure extends AbstractScaleFigure {
return rotation;
}
public Transformation getRotateTransformation() {
return axialRotation;
}
public void setRotation(double rot) {
if (MathUtil.equals(rotation, rot))
return;
@ -142,14 +139,22 @@ public class RocketFigure extends AbstractScaleFigure {
updateFigure();
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;
}
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);
}
if (this.currentViewType == type)
@ -201,7 +206,7 @@ public class RocketFigure extends AbstractScaleFigure {
AffineTransform baseTransform = g2.getTransform();
PriorityQueue<RocketComponentShape> figureShapes;
if (currentViewType == RocketPanel.VIEW_TYPE.SideView)
if (currentViewType == RocketPanel.VIEW_TYPE.SideView || currentViewType == RocketPanel.VIEW_TYPE.TopView)
figureShapes = figureShapes_side;
else if (currentViewType == RocketPanel.VIEW_TYPE.BackView)
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() ));
// rotate by figure's axial rotation:
curMotorLocation = this.axialRotation.transform(curMotorLocation);
curMotorLocation = getFigureRotation().transform(curMotorLocation);
{
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,
(curMotorLocation.y - motorRadius),
motorLength,
@ -354,7 +359,7 @@ public class RocketFigure extends AbstractScaleFigure {
LinkedHashSet<RocketComponent> l = new LinkedHashSet<RocketComponent>();
PriorityQueue<RocketComponentShape> figureShapes;
if (currentViewType == RocketPanel.VIEW_TYPE.SideView)
if (currentViewType == RocketPanel.VIEW_TYPE.SideView || currentViewType == RocketPanel.VIEW_TYPE.TopView)
figureShapes = figureShapes_side;
else if (currentViewType == RocketPanel.VIEW_TYPE.BackView)
figureShapes = figureShapes_back;
@ -399,7 +404,7 @@ public class RocketFigure extends AbstractScaleFigure {
final ArrayList<InstanceContext> contextList = entry.getValue();
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);
}
}
@ -432,9 +437,10 @@ public class RocketFigure extends AbstractScaleFigure {
// Find the appropriate method
switch (viewType) {
case SideView:
case TopView:
m = Reflection.findMethod(ROCKET_FIGURE_PACKAGE, component, ROCKET_FIGURE_SUFFIX, "getShapesSide",
RocketComponent.class, Transformation.class);
break;
break;
case BackView:
m = Reflection.findMethod(ROCKET_FIGURE_PACKAGE, component, ROCKET_FIGURE_SUFFIX, "getShapesBack",
@ -503,6 +509,7 @@ public class RocketFigure extends AbstractScaleFigure {
switch (currentViewType) {
case SideView:
case TopView:
subjectBounds_m = new Rectangle2D.Double(bounds.min.x, -maxR, bounds.span().x, 2 * maxR);
break;
case BackView:
@ -528,9 +535,8 @@ public class RocketFigure extends AbstractScaleFigure {
if (currentViewType == RocketPanel.VIEW_TYPE.BackView){
final int newOriginX = mid_x;
final int newOriginY = borderThickness_px.height + getHeight() / 2;
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 newOriginY = Math.max(getHeight(), subjectHeight + 2*borderThickness_px.height )/ 2;
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.Component;
import java.awt.Dimension;
import java.awt.Font;
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 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 {
TopView(false, RocketFigure.VIEW_TOP),
SideView(false, RocketFigure.VIEW_SIDE),
BackView(false, RocketFigure.VIEW_BACK),
SEPARATOR(false, -248), // Horizontal combobox separator dummy item
Figure3D(true, RocketFigure3d.TYPE_FIGURE),
Unfinished(true, RocketFigure3d.TYPE_UNFINISHED),
Finished(true, RocketFigure3d.TYPE_FINISHED);
@ -108,9 +112,16 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
@Override
public String toString() {
if (type == -248) {
return VIEW_TYPE_SEPARATOR;
}
return trans.get("RocketPanel.FigTypeAct." + super.toString());
}
public static VIEW_TYPE getDefaultViewType() {
return SideView;
}
}
private boolean is3d;
@ -308,12 +319,16 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
JPanel ribbon = new JPanel(new MigLayout("insets 0, fill"));
// 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
public void setSelectedItem(Object o) {
super.setSelectedItem(o);
VIEW_TYPE v = (VIEW_TYPE) o;
if (v == VIEW_TYPE.SEPARATOR) {
return;
}
super.setSelectedItem(o);
if (v.is3d) {
figure3d.setType(v.type);
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 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
scaleSelector = new ScaleSelector(scrollPane);
@ -773,7 +790,8 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
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);
extraCG.setPosition(cgx, cgy);
} else {
@ -1061,40 +1079,34 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
figure3d.setSelection(components);
}
// /**
// * An <code>Action</code> that shows whether the figure type is the
// type
// * given in the constructor.
// *
// * @author Sampo Niskanen <sampo.niskanen@iki.fi>
// */
// private class FigureTypeAction extends AbstractAction implements
// StateChangeListener {
// private static final long serialVersionUID = 1L;
// private final VIEW_TYPE type;
//
// public FigureTypeAction(VIEW_TYPE type) {
// this.type = type;
// stateChanged(null);
// figure.addChangeListener(this);
// }
//
// @Override
// public void actionPerformed(ActionEvent e) {
// boolean state = (Boolean) getValue(Action.SELECTED_KEY);
// if (state == true) {
// // This view has been selected
// figure.setType(type);
// go2D();
// updateExtras();
// }
// stateChanged(null);
// }
//
// @Override
// public void stateChanged(EventObject e) {
// putValue(Action.SELECTED_KEY, figure.getType() == type && !is3d);
// }
// }
private static class ViewTypeComboBoxModel extends DefaultComboBoxModel<VIEW_TYPE> {
public ViewTypeComboBoxModel(VIEW_TYPE[] items, VIEW_TYPE initialItem) {
super(items);
super.setSelectedItem(initialItem);
}
}
/**
* Custom combobox renderer that supports the display of horizontal separators between items.
* ComboBox objects with the text {@link VIEW_TYPE_SEPARATOR} objects in the combobox are replaced by a separator object.
*/
private static class SeparatorComboBoxRenderer extends JLabel implements ListCellRenderer {
private final JSeparator separator;
private final ListCellRenderer defaultRenderer;
public SeparatorComboBoxRenderer(ListCellRenderer defaultRenderer) {
this.defaultRenderer = defaultRenderer;
this.separator = new JSeparator(JSeparator.HORIZONTAL);
}
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
String str = (value == null) ? "" : value.toString();
if (VIEW_TYPE_SEPARATOR.equals(str)) {
return separator;
};
return defaultRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
}
}
}