Add coord transform to export panel

This commit is contained in:
SiboVG 2023-08-22 01:01:23 +02:00
parent cea393f202
commit e2111e9e8b
7 changed files with 251 additions and 23 deletions

View File

@ -1505,6 +1505,12 @@ OBJOptionChooser.lbl.Scaling = Scaling:
OBJOptionChooser.lbl.Scaling.ttip = <html>Scale the exported geometry by the given factor.<br>The default dimensions are in SI units (meters), but e.g. 3D printing slicer software often uses mm.<br>In that scenario, you can set the scale to '1000'.</html>
OBJOptionChooser.lbl.LevelOfDetail = Level of detail:
OBJOptionChooser.lbl.LevelOfDetail.ttip = Select the desired level of detail of the geometry export.
OBJOptionChooser.lbl.CoordinateTransform = Coordinate transform:
OBJOptionChooser.lbl.CoordinateTransform.ttip = Define how to OpenRocket axes are mapped to the OBJ axes.
OBJOptionChooser.lbl.CoordinateTransform.Axial = Axial (up) axis:
OBJOptionChooser.lbl.CoordinateTransform.Axial.ttip = Select the OBJ axis that should correspond to the axial (up) axis of the OpenRocket model.
OBJOptionChooser.lbl.CoordinateTransform.Forward = Forward axis:
OBJOptionChooser.lbl.CoordinateTransform.Forward.ttip = Select the OBJ axis that should correspond to the forward axis of the OpenRocket model.
! LevelOfDetail
LevelOfDetail.LOW_QUALITY = Low quality

View File

@ -46,4 +46,34 @@ public enum Axis {
default -> throw new IllegalArgumentException("Unknown axis: " + axis);
};
}
private static boolean isXAxis(Axis axis) {
return axis == X || axis == X_MIN;
}
private static boolean isYAxis(Axis axis) {
return axis == Y || axis == Y_MIN;
}
private static boolean isZAxis(Axis axis) {
return axis == Z || axis == Z_MIN;
}
/**
* Get the third axis given two axes, using a right-handed coordinate system.
* @param firstAxis The first axis.
* @param thirdAxis The third axis.
* @return The third axis.
*/
public static Axis getThirdAxis(Axis firstAxis, Axis thirdAxis) {
if (isXAxis(firstAxis) && isYAxis(thirdAxis) || isYAxis(firstAxis) && isXAxis(thirdAxis)) {
return Z;
} else if (isXAxis(firstAxis) && isZAxis(thirdAxis) || isZAxis(firstAxis) && isXAxis(thirdAxis)) {
return Y;
} else if (isYAxis(firstAxis) && isZAxis(thirdAxis) || isZAxis(firstAxis) && isYAxis(thirdAxis)) {
return X;
} else {
throw new IllegalArgumentException("Unknown axis: " + firstAxis + ", " + thirdAxis);
}
}
}

View File

@ -7,8 +7,8 @@ import net.sf.openrocket.util.Coordinate;
/**
* Interface for classes that can convert location and rotation coordinates from the OpenRocket coordinate system
* to a custom OBJ coordinate system.
* OpenRocket uses a left-handed coordinate system with the y-axis pointing up, the z-axis pointing away from the viewer,
* and the x-axis pointing to the right (in the side view). Its origin is also at the tip of the rocket.
* OpenRocket uses a left-handed coordinate system with the forward (y-)axis pointing up, the depth (z-)axis pointing away from the viewer,
* and the longitudinal (x-)axis pointing to the right (in the side view). Its origin is also at the tip of the rocket.
*
* @author Sibo Van Gool <sibo.vangool@hotmail.com>
*/
@ -17,7 +17,6 @@ public class CoordTransform {
protected final Axis xAxis;
protected final Axis yAxis;
protected final Axis zAxis;
protected final Axis axialAxis;
// Origin offsets
protected final double origXOffs;
@ -34,16 +33,13 @@ public class CoordTransform {
* of the OpenRocket x-axis), then you must pass Axis.X_MIN as the xAxis parameter.
* You must also add an offset to the origin of the transformed coordinate system, so that it starts
* at the bottom of the rocket => set origZOffs to the length of the rocket.
* @param axialAxis the axial/longitudinal axis <b>in the transformed coordinate system, with the direction
* relative to the OpenRocket x-axis !!</b>
* From the previous example, the longitudinal axis would be Axis.Z_MIN.
* @param origXOffs the x-offset of the origin of the OBJ coordinate system, <b>in the OpenRocket coordinate system</b>
* @param origYOffs the y-offset of the origin of the OBJ coordinate system, <b>in the OpenRocket coordinate system</b>
* @param origZOffs the z-offset of the origin of the OBJ coordinate system, <b>in the OpenRocket coordinate system</b>
*/
public CoordTransform(@NotNull Axis xAxis, @NotNull Axis yAxis, @NotNull Axis zAxis, @NotNull Axis axialAxis,
public CoordTransform(@NotNull Axis xAxis, @NotNull Axis yAxis, @NotNull Axis zAxis,
double origXOffs, double origYOffs, double origZOffs) {
if (xAxis == null || yAxis == null || zAxis == null || axialAxis == null) {
if (xAxis == null || yAxis == null || zAxis == null) {
throw new IllegalArgumentException("Axes cannot be null");
}
@ -54,13 +50,72 @@ public class CoordTransform {
this.xAxis = xAxis;
this.yAxis = yAxis;
this.zAxis = zAxis;
this.axialAxis = axialAxis;
this.origXOffs = origXOffs;
this.origYOffs = origYOffs;
this.origZOffs = origZOffs;
}
/**
* Create a new coordinate system converter.
* @param axialAxis the OBJ axis that corresponds to the OpenRocket axial (x-)axis
* @param forwardAxis the OBJ axis that corresponds to the OpenRocket forward (y-)axis
* @param origXOffs the x-offset of the origin of the OBJ coordinate system, <b>in the OBJ coordinate system</b>
* @param origYOffs the y-offset of the origin of the OBJ coordinate system, <b>in the OBJ coordinate system</b>
* @param origZOffs the z-offset of the origin of the OBJ coordinate system, <b>in the OBJ coordinate system</b>
*/
public static CoordTransform generateUsingLongitudinalAndForwardAxes(Axis axialAxis, Axis forwardAxis,
double origXOffs, double origYOffs, double origZOffs) {
if (axialAxis == null || forwardAxis == null) {
throw new IllegalArgumentException("Axes cannot be null");
}
if (axialAxis.isSameAxis(forwardAxis)) {
throw new IllegalArgumentException("Axes must be different");
}
Axis xAxis = null;
Axis yAxis = null;
Axis zAxis = null;
switch (axialAxis) {
case X -> xAxis = Axis.X;
case X_MIN -> xAxis = Axis.X_MIN;
case Y -> yAxis = Axis.X;
case Y_MIN -> yAxis = Axis.X_MIN;
case Z -> zAxis = Axis.X;
case Z_MIN -> zAxis = Axis.X_MIN;
}
switch (forwardAxis) {
case X -> xAxis = Axis.Y;
case X_MIN -> xAxis = Axis.Y_MIN;
case Y -> yAxis = Axis.Y;
case Y_MIN -> yAxis = Axis.Y_MIN;
case Z -> zAxis = Axis.Y;
case Z_MIN -> zAxis = Axis.Y_MIN;
}
Axis depthAxis = Axis.getThirdAxis(axialAxis, forwardAxis);
switch (depthAxis) {
case X -> xAxis = Axis.Z;
case X_MIN -> xAxis = Axis.Z_MIN;
case Y -> yAxis = Axis.Z;
case Y_MIN -> yAxis = Axis.Z_MIN;
case Z -> zAxis = Axis.Z;
case Z_MIN -> zAxis = Axis.Z_MIN;
}
if (xAxis == null || yAxis == null || zAxis == null) {
throw new IllegalStateException("Axes should not be null");
}
final double origXTrans = getTransformedOriginOffset(xAxis, origXOffs, origYOffs, origZOffs);
final double origYTrans = getTransformedOriginOffset(yAxis, origXOffs, origYOffs, origZOffs);
final double origZTrans = getTransformedOriginOffset(zAxis, origXOffs, origYOffs, origZOffs);
return new CoordTransform(xAxis, yAxis, zAxis, origXTrans, origYTrans, origZTrans);
}
private FloatTuple convertLoc(double x, double y, double z,
double origXOffs, double origYOffs, double origZOffs) {
@ -147,7 +202,7 @@ public class CoordTransform {
* @param z the z-coordinate in the OpenRocket coordinate system
* @return the coordinate in the transformed OBJ coordinate system
*/
private double getTransformedCoordinate(Axis axis, double x, double y, double z) {
private static double getTransformedCoordinate(Axis axis, double x, double y, double z) {
return switch (axis) {
case X -> x;
case X_MIN -> -x;
@ -164,7 +219,7 @@ public class CoordTransform {
* @param axis the axis to get the offset for
* @return the offset of the origin of the OBJ coordinate system for the given axis
*/
private double getTransformedOriginOffset(Axis axis, double origXOffs, double origYOffs, double origZOffs) {
private static double getTransformedOriginOffset(Axis axis, double origXOffs, double origYOffs, double origZOffs) {
return switch (axis) {
case X, X_MIN -> origXOffs;
case Y, Y_MIN -> origYOffs;
@ -181,7 +236,7 @@ public class CoordTransform {
* @param rotZ the rotation in radians around the OpenRocket z-axis
* @return the rotation in radians around the transformed OBJ axis
*/
private double getTransformedRotation(Axis axis, double rotX, double rotY, double rotZ) {
private static double getTransformedRotation(Axis axis, double rotX, double rotY, double rotZ) {
// OpenRocket uses left-handed coordinate system, we'll use right-handed
return switch (axis) {
case X -> -rotX;
@ -214,7 +269,61 @@ public class CoordTransform {
* @return the equivalent axis for the x-axis in the OpenRocket coordinate system (axial axis)
*/
public Axis getAxialAxis() {
return axialAxis;
switch (xAxis) {
case X -> {
return Axis.X;
}
case X_MIN -> {
return Axis.X_MIN;
}
}
switch (yAxis) {
case X -> {
return Axis.Y;
}
case X_MIN -> {
return Axis.Y_MIN;
}
}
switch (zAxis) {
case X -> {
return Axis.Z;
}
case X_MIN -> {
return Axis.Z_MIN;
}
}
throw new IllegalStateException("No axial axis found");
}
public Axis getForwardAxis() {
switch (xAxis) {
case Y -> {
return Axis.X;
}
case Y_MIN -> {
return Axis.X_MIN;
}
}
switch (yAxis) {
case Y -> {
return Axis.Y;
}
case Y_MIN -> {
return Axis.Y_MIN;
}
}
switch (zAxis) {
case Y -> {
return Axis.Z;
}
case Y_MIN -> {
return Axis.Z_MIN;
}
}
throw new IllegalStateException("No forward axis found");
}
public double getOrigXOffs() {

View File

@ -11,6 +11,6 @@ package net.sf.openrocket.file.wavefrontobj;
*/
public class DefaultCoordTransform extends CoordTransform {
public DefaultCoordTransform(double rocketLength) {
super(Axis.Y, Axis.Z, Axis.X_MIN, Axis.Z_MIN, 0, 0, rocketLength);
super(Axis.Y, Axis.Z, Axis.X_MIN, 0, 0, rocketLength);
}
}

View File

@ -133,7 +133,6 @@ public abstract class Preferences implements ChangeSource {
private static final String OBJ_X_AXIS = "xAxis";
private static final String OBJ_Y_AXIS = "yAxis";
private static final String OBJ_Z_AXIS = "zAxis";
private static final String OBJ_AXIAL_AXIS = "AxialAxis";
private static final String OBJ_ORIG_X_OFFS = "OrigXOffs";
private static final String OBJ_ORIG_Y_OFFS = "OrigYOffs";
private static final String OBJ_ORIG_Z_OFFS = "OrigZOffs";
@ -1056,7 +1055,6 @@ public abstract class Preferences implements ChangeSource {
coordTransformNode.put(OBJ_X_AXIS, transform.getXAxis().toString());
coordTransformNode.put(OBJ_Y_AXIS, transform.getYAxis().toString());
coordTransformNode.put(OBJ_Z_AXIS, transform.getZAxis().toString());
coordTransformNode.put(OBJ_AXIAL_AXIS, transform.getAxialAxis().toString());
coordTransformNode.putDouble(OBJ_ORIG_X_OFFS, transform.getOrigXOffs());
coordTransformNode.putDouble(OBJ_ORIG_Y_OFFS, transform.getOrigYOffs());
coordTransformNode.putDouble(OBJ_ORIG_Z_OFFS, transform.getOrigZOffs());
@ -1084,12 +1082,11 @@ public abstract class Preferences implements ChangeSource {
Axis xAxis = Axis.fromString(coordTransformNode.get(OBJ_X_AXIS, Axis.Y.toString()));
Axis yAxis = Axis.fromString(coordTransformNode.get(OBJ_Y_AXIS, Axis.Z.toString()));
Axis zAxis = Axis.fromString(coordTransformNode.get(OBJ_Z_AXIS, Axis.X_MIN.toString()));
Axis axialAxis = Axis.fromString(coordTransformNode.get(OBJ_AXIAL_AXIS, Axis.Z_MIN.toString()));
double origXOffs = coordTransformNode.getDouble(OBJ_ORIG_X_OFFS, 0.0);
double origYOffs = coordTransformNode.getDouble(OBJ_ORIG_Y_OFFS, 0.0);
double origZOffs = coordTransformNode.getDouble(OBJ_ORIG_Z_OFFS, rocket.getLength());
CoordTransform transform = new CoordTransform(xAxis, yAxis, zAxis, axialAxis, origXOffs, origYOffs, origZOffs);
CoordTransform transform = new CoordTransform(xAxis, yAxis, zAxis, origXOffs, origYOffs, origZOffs);
options.setTransformer(transform);
return options;

View File

@ -7,6 +7,7 @@ import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.rocketcomponent.ComponentAssembly;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.unit.UnitGroup;
@ -35,13 +36,19 @@ public class OBJOptionChooser extends JPanel {
private final JCheckBox sRGB;
private final JComboBox<ObjUtils.LevelOfDetail> LOD;
private final DoubleModel scalingModel;
private final JComboBox<Axis> axialCombo;
private final JComboBox<Axis> forwardCombo;
private final List<RocketComponent> selectedComponents;
private final Rocket rocket;
public OBJOptionChooser(OBJExportOptions opts, List<RocketComponent> selectedComponents) {
super(new MigLayout());
private boolean isProgrammaticallyChanging = false;
public OBJOptionChooser(OBJExportOptions opts, List<RocketComponent> selectedComponents, Rocket rocket) {
super(new MigLayout("hidemode 3"));
this.selectedComponents = selectedComponents;
this.rocket = rocket;
// ------------ Basic options ------------
//// Export children
@ -102,11 +109,48 @@ public class OBJOptionChooser extends JPanel {
advancedOptionsPanel.add(LODLabel, "spanx, split 2");
this.LOD = new JComboBox<>(ObjUtils.LevelOfDetail.values());
this.LOD.setToolTipText(trans.get("OBJOptionChooser.lbl.LevelOfDetail.ttip"));
advancedOptionsPanel.add(LOD, "growx, wrap para");
advancedOptionsPanel.add(LOD, "growx, wrap unrel");
//// Coordinate transformer
// TODO
JLabel coordTransLabel = new JLabel(trans.get("OBJOptionChooser.lbl.CoordinateTransform"));
coordTransLabel.setToolTipText(trans.get("OBJOptionChooser.lbl.CoordinateTransform.ttip"));
advancedOptionsPanel.add(coordTransLabel, "spanx, wrap");
////// Axial (up) axis
JLabel axialLabel = new JLabel(trans.get("OBJOptionChooser.lbl.CoordinateTransform.Axial"));
axialLabel.setToolTipText(trans.get("OBJOptionChooser.lbl.CoordinateTransform.Axial.ttip"));
advancedOptionsPanel.add(axialLabel, "gapleft 10lp");
this.axialCombo = new JComboBox<>(Axis.values());
this.axialCombo.setToolTipText(trans.get("OBJOptionChooser.lbl.CoordinateTransform.Axial.ttip"));
advancedOptionsPanel.add(axialCombo, "wrap");
////// Forward axis
JLabel forwardLabel = new JLabel(trans.get("OBJOptionChooser.lbl.CoordinateTransform.Forward"));
forwardLabel.setToolTipText(trans.get("OBJOptionChooser.lbl.CoordinateTransform.Forward.ttip"));
advancedOptionsPanel.add(forwardLabel, "gapleft 10lp");
this.forwardCombo = new JComboBox<>(Axis.values());
this.forwardCombo.setToolTipText(trans.get("OBJOptionChooser.lbl.CoordinateTransform.Forward.ttip"));
advancedOptionsPanel.add(forwardCombo, "wrap");
//// Set up the listeners for the coordinate transformer combo boxes
this.axialCombo.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (!isProgrammaticallyChanging) {
coordTransComboAction(e, forwardCombo);
}
}
});
// Let's just keep all the options for the axial axis, and only remove the forward axis options
/*this.forwardCombo.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (!isProgrammaticallyChanging) {
coordTransComboAction(e, axialCombo);
}
}
});*/
// Add action listener to the toggle button
@ -153,6 +197,10 @@ public class OBJOptionChooser extends JPanel {
this.scalingModel.setValue(opts.getScaling());
this.LOD.setSelectedItem(opts.getLOD());
CoordTransform transformer = opts.getTransformer();
this.axialCombo.setSelectedItem(transformer.getAxialAxis());
this.forwardCombo.setSelectedItem(transformer.getForwardAxis());
}
/**
@ -174,6 +222,11 @@ public class OBJOptionChooser extends JPanel {
opts.setUseSRGB(sRGB.isSelected());
opts.setScaling((float) scalingModel.getValue());
opts.setLOD((ObjUtils.LevelOfDetail) LOD.getSelectedItem());
CoordTransform transformer = CoordTransform.generateUsingLongitudinalAndForwardAxes(
(Axis) axialCombo.getSelectedItem(), (Axis) forwardCombo.getSelectedItem(),
rocket.getLength(), 0, 0);
opts.setTransformer(transformer);
}
private static boolean isOnlyComponentAssembliesSelected(List<RocketComponent> selectedComponents) {
@ -186,4 +239,37 @@ public class OBJOptionChooser extends JPanel {
}
return onlyComponentAssemblies;
}
private void coordTransComboAction(ItemEvent e, JComboBox<Axis> otherCombo) {
if (e.getStateChange() != ItemEvent.SELECTED) {
return;
}
Axis selected = (Axis) e.getItem();
Object otherAxis = otherCombo.getSelectedItem();
if (!(otherAxis instanceof Axis)) {
return;
}
// Set the flag to denote we're changing combo box items programmatically
this.isProgrammaticallyChanging = true;
// Change the combobox items to axes that don't conflict with the selected axis
otherCombo.removeAllItems();
for (Axis axis : Axis.values()) {
if (!axis.isSameAxis(selected)) {
otherCombo.addItem(axis);
}
}
// Select the first item in the combobox
if (!((Axis) otherAxis).isSameAxis(selected)) {
otherCombo.setSelectedItem(otherAxis);
} else {
otherCombo.setSelectedIndex(0);
}
// Reset the flag after changes are done
this.isProgrammaticallyChanging = false;
}
}

View File

@ -72,7 +72,7 @@ public class DesignFileSaveAsFileChooser extends SaveFileChooser {
defaultFilename = FileHelper.forceExtension(defaultFilename,"obj");
this.setDialogTitle(trans.get("saveAs.wavefront.title"));
OBJExportOptions initialOptions = prefs.loadOBJExportOptions(document.getRocket());
OBJOptionChooser objChooser = new OBJOptionChooser(initialOptions, selectedComponents);
OBJOptionChooser objChooser = new OBJOptionChooser(initialOptions, selectedComponents, document.getRocket());
this.setAccessory(objChooser);
this.addChoosableFileFilter(FileHelper.WAVEFRONT_OBJ_FILTER);
this.setFileFilter(FileHelper.WAVEFRONT_OBJ_FILTER);