From ac2339b8b44ae0325a689b635a510144a519e86c Mon Sep 17 00:00:00 2001 From: Joe Pfeiffer Date: Tue, 21 Aug 2018 17:29:07 -0600 Subject: [PATCH 01/47] Fix Average Thrust Calculation (fixes issue #441) Remove test for short time interval before first data point in thrust curve. Comment said it was for numerical stability; multiplying by a small number and then adding doesn't introduce any instabilities I'm aware of in this code. Add parentheses to clarify that values are being multiplied by time intervals, not divided. --- .../net/sf/openrocket/motor/ThrustCurveMotor.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java b/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java index ac4b37a6b..3cf858a9c 100644 --- a/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java +++ b/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java @@ -321,23 +321,20 @@ public class ThrustCurveMotor implements Motor, Comparable, Se // portion from startTime through time[timeIndex] double avgImpulse = 0.0; - // For numeric stability. - if( time[timeIndex+1] - startTime > 0.001 ) { - avgImpulse = (MathUtil.map(startTime, time[timeIndex], time[timeIndex+1], thrust[timeIndex], thrust[timeIndex+1]) + thrust[timeIndex+1]) - / 2.0 * (time[timeIndex+1] - startTime); - } + avgImpulse = ((MathUtil.map(startTime, time[timeIndex], time[timeIndex+1], thrust[timeIndex], thrust[timeIndex+1]) + thrust[timeIndex+1]) + / 2.0) * (time[timeIndex+1] - startTime); // Now add the whole steps; timeIndex++; - while( timeIndex < time.length -1 && endTime >= time[timeIndex+1] ) { - avgImpulse += (thrust[timeIndex] + thrust[timeIndex+1]) / 2.0 * (time[timeIndex+1]-time[timeIndex]); + while ( timeIndex < time.length -1 && endTime >= time[timeIndex+1] ) { + avgImpulse += ((thrust[timeIndex] + thrust[timeIndex+1]) / 2.0) * (time[timeIndex+1]-time[timeIndex]); timeIndex++; } // Now add the bit after the last time index if ( timeIndex < time.length -1 ) { double endInstImpulse = MathUtil.map( endTime, time[timeIndex], time[timeIndex+1], thrust[timeIndex], thrust[timeIndex+1]); - avgImpulse += (thrust[timeIndex] + endInstImpulse) / 2.0 * (endTime - time[timeIndex]); + avgImpulse += ((thrust[timeIndex] + endInstImpulse) / 2.0) * (endTime - time[timeIndex]); } return avgImpulse / (endTime - startTime); From c089e3ecb777b47fe7f2da839eafd234a6ad9e02 Mon Sep 17 00:00:00 2001 From: Joe Pfeiffer Date: Sun, 26 Aug 2018 14:53:00 -0600 Subject: [PATCH 02/47] Code clarification; should make no difference to results --- .../sf/openrocket/motor/ThrustCurveMotor.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java b/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java index 3cf858a9c..8bb8afddd 100644 --- a/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java +++ b/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java @@ -313,28 +313,28 @@ public class ThrustCurveMotor implements Motor, Comparable, Se if ( endTime <= time[timeIndex+1] ) { // we are completely within this time slice so the computation of the average is pretty easy: - double avgImpulse = MathUtil.map(startTime, time[timeIndex], time[timeIndex+1], thrust[timeIndex], thrust[timeIndex+1]); - avgImpulse += MathUtil.map(endTime, time[timeIndex], time[timeIndex+1], thrust[timeIndex], thrust[timeIndex+1]); - avgImpulse /= 2.0; - return avgImpulse; + double startThrust = MathUtil.map(startTime, time[timeIndex], time[timeIndex+1], thrust[timeIndex], thrust[timeIndex+1]); + double endThrust = MathUtil.map(endTime, time[timeIndex], time[timeIndex+1], thrust[timeIndex], thrust[timeIndex+1]); + return (startThrust + endThrust) / 2.0; } - - // portion from startTime through time[timeIndex] + double avgImpulse = 0.0; - avgImpulse = ((MathUtil.map(startTime, time[timeIndex], time[timeIndex+1], thrust[timeIndex], thrust[timeIndex+1]) + thrust[timeIndex+1]) - / 2.0) * (time[timeIndex+1] - startTime); + + // portion from startTime through time[timeIndex+1] + double startThrust = MathUtil.map(startTime, time[timeIndex], time[timeIndex+1], thrust[timeIndex], thrust[timeIndex+1]); + avgImpulse = (time[timeIndex+1] - startTime) * (startThrust + thrust[timeIndex+1]) / 2.0; // Now add the whole steps; timeIndex++; while ( timeIndex < time.length -1 && endTime >= time[timeIndex+1] ) { - avgImpulse += ((thrust[timeIndex] + thrust[timeIndex+1]) / 2.0) * (time[timeIndex+1]-time[timeIndex]); + avgImpulse += (time[timeIndex+1] - time[timeIndex]) * (thrust[timeIndex] + thrust[timeIndex+1]) / 2.0; timeIndex++; } // Now add the bit after the last time index if ( timeIndex < time.length -1 ) { - double endInstImpulse = MathUtil.map( endTime, time[timeIndex], time[timeIndex+1], thrust[timeIndex], thrust[timeIndex+1]); - avgImpulse += ((thrust[timeIndex] + endInstImpulse) / 2.0) * (endTime - time[timeIndex]); + double endThrust = MathUtil.map( endTime, time[timeIndex], time[timeIndex+1], thrust[timeIndex], thrust[timeIndex+1]); + avgImpulse += ((thrust[timeIndex] + endThrust) / 2.0) * (endTime - time[timeIndex]); } return avgImpulse / (endTime - startTime); From c1d8bfda06e9e76d906fee7e5a37d2dcd301788f Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sun, 17 Jun 2018 14:19:46 -0400 Subject: [PATCH 03/47] [refactor] fixed warnings and made variable names more explicit in [Freeform]FinSetConfig Dialogs - de-duped writeCSVFile methods --- .../gui/configdialog/FinSetConfig.java | 36 +-- .../configdialog/FreeformFinSetConfig.java | 218 +++++++----------- 2 files changed, 104 insertions(+), 150 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/configdialog/FinSetConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/FinSetConfig.java index 96b01d893..42e761dfb 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/FinSetConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/FinSetConfig.java @@ -135,7 +135,7 @@ public abstract class FinSetConfig extends RocketComponentConfig { } - public JPanel finTabPanel() { + private JPanel finTabPanel() { JPanel panel = new JPanel( new MigLayout("align 50% 20%, fillx, gap rel unrel, ins 20lp 10% 20lp 10%", "[150lp::][65lp::][30lp::][200lp::]", "")); @@ -185,14 +185,14 @@ public abstract class FinSetConfig extends RocketComponentConfig { label.setToolTipText(trans.get("FinSetConfig.ttip.Tabheight")); panel.add(label, "gapleft para"); - final DoubleModel mth = new DoubleModel(component, "TabHeight", UnitGroup.UNITS_LENGTH, 0); - - spin = new JSpinner(mth.getSpinnerModel()); + final DoubleModel tabHeightModel = new DoubleModel(component, "TabHeight", UnitGroup.UNITS_LENGTH, 0); + component.addChangeListener( tabHeightModel ); + spin = new JSpinner(tabHeightModel.getSpinnerModel()); spin.setEditor(new SpinnerEditor(spin)); panel.add(spin, "growx"); - panel.add(new UnitSelector(mth), "growx"); - panel.add(new BasicSlider(mth.getSliderModel(DoubleModel.ZERO, length2)), + panel.add(new UnitSelector(tabHeightModel), "growx"); + panel.add(new BasicSlider(tabHeightModel.getSliderModel(DoubleModel.ZERO, length2)), "w 100lp, growx 5, wrap"); //// Tab position: @@ -202,7 +202,7 @@ public abstract class FinSetConfig extends RocketComponentConfig { panel.add(label, "gapleft para"); final DoubleModel mts = new DoubleModel(component, "TabShift", UnitGroup.UNITS_LENGTH); - + component.addChangeListener( mts); spin = new JSpinner(mts.getSpinnerModel()); spin.setEditor(new SpinnerEditor(spin)); panel.add(spin, "growx"); @@ -214,9 +214,10 @@ public abstract class FinSetConfig extends RocketComponentConfig { label = new JLabel(trans.get("FinSetConfig.lbl.relativeto")); panel.add(label, "right, gapright unrel"); - final EnumModel em = new EnumModel(component, "TabRelativePosition"); + + final EnumModel em = new EnumModel<>(component, "TabRelativePosition"); - JComboBox enumCombo = new JComboBox(em); + JComboBox enumCombo = new JComboBox<>(em); panel.add( enumCombo, "spanx 3, growx, wrap para"); @@ -233,7 +234,7 @@ public abstract class FinSetConfig extends RocketComponentConfig { try { document.startUndo("Compute fin tabs"); - List rings = new ArrayList(); + List rings = new ArrayList<>(); //Do deep recursive iteration Iterator iter = parent.iterator(false); while (iter.hasNext()) { @@ -244,8 +245,8 @@ public abstract class FinSetConfig extends RocketComponentConfig { double depth = ((Coaxial) parent).getOuterRadius() - it.getOuterRadius(); //Set fin tab depth if (depth >= 0.0d) { - mth.setValue(depth); - mth.setCurrentUnit(UnitGroup.UNITS_LENGTH.getDefaultUnit()); + tabHeightModel.setValue(depth); + tabHeightModel.setCurrentUnit(UnitGroup.UNITS_LENGTH.getDefaultUnit()); } } } else if (rocketComponent instanceof CenteringRing) { @@ -254,8 +255,8 @@ public abstract class FinSetConfig extends RocketComponentConfig { } //Figure out position and length of the fin tab if (!rings.isEmpty()) { - FinSet.TabRelativePosition temp = (FinSet.TabRelativePosition) em.getSelectedItem(); - em.setSelectedItem(FinSet.TabRelativePosition.FRONT); + AxialMethod temp = (AxialMethod) em.getSelectedItem(); + em.setSelectedItem(AxialMethod.TOP); double len = computeFinTabLength(rings, component.asPositionValue(AxialMethod.TOP), component.getLength(), mts, parent); mtl.setValue(len); @@ -506,10 +507,11 @@ public abstract class FinSetConfig extends RocketComponentConfig { label.setToolTipText(trans.get("RocketCompCfg.lbl.ttip.componentmaterialaffects")); filletPanel.add(label, "spanx 4, wrap rel"); - JComboBox combo = new JComboBox<>(new MaterialModel(filletPanel, component, Material.Type.BULK, "FilletMaterial")); + JComboBox materialCombo = new JComboBox(new MaterialModel(filletPanel, component, Material.Type.BULK, "FilletMaterial")); + //// The component material affects the weight of the component. - combo.setToolTipText(trans.get("RocketCompCfg.combo.ttip.componentmaterialaffects")); - filletPanel.add(combo, "spanx 4, growx, wrap paragraph"); + materialCombo.setToolTipText(trans.get("RocketCompCfg.combo.ttip.componentmaterialaffects")); + filletPanel.add( materialCombo, "spanx 4, growx, wrap paragraph"); filletPanel.setToolTipText(tip); return filletPanel; } diff --git a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java index 92dfeafa5..0f989e526 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java @@ -8,11 +8,10 @@ import java.awt.event.MouseEvent; import java.awt.geom.Point2D; import java.io.BufferedWriter; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; + import java.io.OutputStreamWriter; -import java.io.UnsupportedEncodingException; import java.io.Writer; import java.util.List; @@ -63,8 +62,9 @@ import net.sf.openrocket.startup.Application; import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.util.Coordinate; +@SuppressWarnings("serial") public class FreeformFinSetConfig extends FinSetConfig { - private static final long serialVersionUID = 2504130276828826021L; + private static final Logger log = LoggerFactory.getLogger(FreeformFinSetConfig.class); private static final Translator trans = Application.getTranslator(); @@ -146,7 +146,7 @@ public class FreeformFinSetConfig extends FinSetConfig { //// Position relative to: panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.Posrelativeto"))); - JComboBox positionCombo = new JComboBox( new EnumModel(component, "AxialMethod", AxialMethod.axialOffsetMethods )); + JComboBox positionCombo = new JComboBox<>( new EnumModel<>(component, "AxialMethod", AxialMethod.axialOffsetMethods )); panel.add(positionCombo, "spanx 3, growx, wrap"); //// plus panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.plus")), "right"); @@ -159,10 +159,7 @@ public class FreeformFinSetConfig extends FinSetConfig { panel.add(new UnitSelector(m), "growx"); panel.add(new BasicSlider(m.getSliderModel(new DoubleModel(component.getParent(), "Length", -1.0, UnitGroup.UNITS_NONE), new DoubleModel(component.getParent(), "Length"))), "w 100lp, wrap"); - - - - + mainPanel.add(panel, "aligny 20%"); mainPanel.add(new JSeparator(SwingConstants.VERTICAL), "growy, height 150lp"); @@ -170,12 +167,10 @@ public class FreeformFinSetConfig extends FinSetConfig { panel = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::]", "")); - - //// Cross section //// Fin cross section: panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.FincrossSection")), "span, split"); - JComboBox sectionCombo = new JComboBox(new EnumModel(component, "CrossSection")); + JComboBox sectionCombo = new JComboBox<>(new EnumModel(component, "CrossSection")); panel.add(sectionCombo, "growx, wrap unrel"); @@ -211,9 +206,7 @@ public class FreeformFinSetConfig extends FinSetConfig { // Create the figure figure = new FinPointFigure(finset); - ScaleScrollPane figurePane = new FinPointScrollPane(); - figurePane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); - figurePane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + ScaleScrollPane figurePane = new FinPointScrollPane( figure); // Create the table tableModel = new FinPointTableModel(); @@ -244,50 +237,16 @@ public class FreeformFinSetConfig extends FinSetConfig { public void actionPerformed(ActionEvent e) { log.info(Markers.USER_MARKER, "Export CSV free-form fin"); - JFileChooser c = new JFileChooser(); - // Demonstrate "Save" dialog: - int rVal = c.showSaveDialog(FreeformFinSetConfig.this); - if (rVal == JFileChooser.APPROVE_OPTION) { - File myFile = c.getSelectedFile(); + JFileChooser chooser = new JFileChooser(); + // Demonstrate "Save" dialog: - Writer writer = null; - int nRow = table.getRowCount(); - int nCol = table.getColumnCount(); - try{ - try { - writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(myFile.getAbsoluteFile()), "utf-8")); + if (JFileChooser.APPROVE_OPTION == chooser.showSaveDialog(FreeformFinSetConfig.this)){ + File selectedFile= chooser.getSelectedFile(); - //write the header information - StringBuffer bufferHeader = new StringBuffer(); - for (int j = 0; j < nCol; j++) { - bufferHeader.append(table.getColumnName(j)); - if (j!=nCol) bufferHeader.append(", "); - } - writer.write(bufferHeader.toString() + "\r\n"); - - //write row information - for (int i = 0 ; i < nRow ; i++){ - StringBuffer buffer = new StringBuffer(); - for (int j = 0 ; j < nCol ; j++){ - buffer.append(table.getValueAt(i,j)); - if (j!=nCol) buffer.append(", "); - } - writer.write(buffer.toString() + "\r\n"); - } - }finally { - writer.close(); - } - } catch (UnsupportedEncodingException e1) { - e1.printStackTrace(); - } catch (FileNotFoundException e1) { - e1.printStackTrace(); - } catch (IOException e1) { - e1.printStackTrace(); - } - - } - } - }); + FreeformFinSetConfig.writeCSVFile(table, selectedFile); + } + } + }); panel.add(tablePane, "growy, width 100lp:100lp:, height 100lp:250lp:"); panel.add(figurePane, "gap unrel, spanx, spany 3, growx, growy 1000, height 100lp:250lp:, wrap"); @@ -297,7 +256,8 @@ public class FreeformFinSetConfig extends FinSetConfig { panel.add(scaleButton, "spany 2, alignx 50%, aligny 50%"); panel.add(exportCsvButton, "spany 2, alignx 50%, aligny 50%"); - panel.add(new ScaleSelector(figurePane), "spany 2, aligny 50%"); + ScaleSelector selector = new ScaleSelector(figurePane); + panel.add( selector, "spany 2, aligny 50%"); JButton importButton = new JButton(trans.get("CustomFinImport.button.label")); importButton.addActionListener(new ActionListener() { @@ -314,36 +274,39 @@ public class FreeformFinSetConfig extends FinSetConfig { return panel; } - - public void writeCSVfile(JTable table, String filename) throws IOException{ - Writer writer = null; - int nRow = table.getRowCount(); - int nCol = table.getColumnCount(); - try { - writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename), "utf-8")); - //write the header information - StringBuffer bufferHeader = new StringBuffer(); - for (int j = 0; j < nCol; j++) { - bufferHeader.append(table.getColumnName(j)); - if (j!=nCol) bufferHeader.append(", "); - } - writer.write(bufferHeader.toString() + "\r\n"); + private static void writeCSVFile(JTable table, final File outputFile){ + int nRow = table.getRowCount(); + int nCol = table.getColumnCount(); + + try { + final Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile), "utf-8")); + + //write the header information + StringBuilder bufferHeader = new StringBuilder(); + for (int j = 0; j < nCol; j++) { + bufferHeader.append(table.getColumnName(j)); + bufferHeader.append(", "); + } + writer.write(bufferHeader.toString() + "\r\n"); + + //write row information + for (int i = 0; i < nRow; i++) { + StringBuilder buffer = new StringBuilder(); + for (int j = 0; j < nCol; j++) { + buffer.append(table.getValueAt(i, j)); + buffer.append(", "); + } + writer.write(buffer.toString() + "\r\n"); + } + writer.close(); + + } catch (IOException e1) { + e1.printStackTrace(); + } + } + - //write row information - for (int i = 0 ; i < nRow ; i++){ - StringBuffer buffer = new StringBuffer(); - for (int j = 0 ; j < nCol ; j++){ - buffer.append(table.getValueAt(i,j)); - if (j!=nCol) buffer.append(", "); - } - writer.write(buffer.toString() + "\r\n"); - } - } finally { - writer.close(); - } - } - private void importImage() { JFileChooser chooser = new JFileChooser(); chooser.setFileFilter(FileHelper.getImageFileFilter()); @@ -361,7 +324,7 @@ public class FreeformFinSetConfig extends FinSetConfig { CustomFinImporter importer = new CustomFinImporter(); List points = importer.getPoints(chooser.getSelectedFile()); document.startUndo(trans.get("CustomFinImport.undo")); - finset.setPoints(points); + finset.setPoints( points); } catch (IllegalFinPointException e) { log.warn("Error storing fin points", e); JOptionPane.showMessageDialog(this, trans.get("CustomFinImport.error.badimage"), @@ -386,22 +349,23 @@ public class FreeformFinSetConfig extends FinSetConfig { tableModel.fireTableDataChanged(); } if (figure != null) { - figure.updateFigure(); + figure.updateFigure(); } + + revalidate(); + repaint(); } - private class FinPointScrollPane extends ScaleScrollPane { - private static final long serialVersionUID = 2232218393756983666L; private static final int ANY_MASK = (MouseEvent.ALT_DOWN_MASK | MouseEvent.ALT_GRAPH_DOWN_MASK | MouseEvent.META_DOWN_MASK | MouseEvent.CTRL_DOWN_MASK | MouseEvent.SHIFT_DOWN_MASK); private int dragIndex = -1; - public FinPointScrollPane() { - super(figure, false); // Disallow fitting as it's buggy + private FinPointScrollPane( final FinPointFigure _figure) { + super( _figure, true); } @Override @@ -413,26 +377,30 @@ public class FreeformFinSetConfig extends FinSetConfig { return; } - int index = getPoint(event); - if (index >= 0) { - dragIndex = index; + int pointIndex = getPoint(event); + if ( pointIndex >= 0) { + dragIndex = pointIndex; return; } - index = getSegment(event); - if (index >= 0) { + + int segmentIndex = getSegment(event); + if (segmentIndex >= 0) { Point2D.Double point = getCoordinates(event); - finset.addPoint(index); - try { - finset.setPoint(index, point.x, point.y); - } catch (IllegalFinPointException ignore) { - } - dragIndex = index; + finset.addPoint(segmentIndex ); + try { + finset.setPoint(dragIndex, point.x, point.y); + } catch (IllegalFinPointException ignore) { + // no-op + } + dragIndex = segmentIndex; + + updateFields(); + return; } super.mousePressed(event); - return; } @@ -444,12 +412,14 @@ public class FreeformFinSetConfig extends FinSetConfig { return; } Point2D.Double point = getCoordinates(event); - + try { - finset.setPoint(dragIndex, point.x, point.y); + finset.setPoint(dragIndex, point.x, point.y); } catch (IllegalFinPointException ignore) { - log.debug("Ignoring IllegalFinPointException while dragging, dragIndex=" + dragIndex + " x=" + point.x + " y=" + point.y); - } + log.debug("Ignoring IllegalFinPointException while dragging, dragIndex=" + dragIndex + " x=" + point.x + " y=" + point.y); + } + + updateFields(); } @@ -507,28 +477,10 @@ public class FreeformFinSetConfig extends FinSetConfig { return figure.convertPoint(x, y); } - } - - - private enum Columns { - // NUMBER { - // @Override - // public String toString() { - // return "#"; - // } - // @Override - // public String getValue(FreeformFinSet finset, int row) { - // return "" + (row+1) + "."; - // } - // @Override - // public int getWidth() { - // return 10; - // } - // }, X { @Override public String toString() { @@ -563,11 +515,6 @@ public class FreeformFinSetConfig extends FinSetConfig { } private class FinPointTableModel extends AbstractTableModel { - - /** - * - */ - private static final long serialVersionUID = 4803736958177227852L; @Override public int getColumnCount() { @@ -603,6 +550,7 @@ public class FreeformFinSetConfig extends FinSetConfig { if (!(o instanceof String)) return; + // bounds check that indices are valid if (rowIndex < 0 || rowIndex >= finset.getFinPoints().length || columnIndex < 0 || columnIndex >= Columns.values().length) { throw new IllegalArgumentException("Index out of bounds, row=" + rowIndex + " column=" + columnIndex + " fin point count=" + finset.getFinPoints().length); } @@ -612,15 +560,19 @@ public class FreeformFinSetConfig extends FinSetConfig { double value = UnitGroup.UNITS_LENGTH.fromString(str); Coordinate c = finset.getFinPoints()[rowIndex]; - if (columnIndex == Columns.X.ordinal()) + if (columnIndex == Columns.X.ordinal()){ c = c.setX(value); - else + }else{ c = c.setY(value); - + } + finset.setPoint(rowIndex, c.x, c.y); + updateFields(); } catch (NumberFormatException ignore) { + log.warn("ignoring NumberFormatException while editing a Freeform Fin"); } catch (IllegalFinPointException ignore) { + log.warn("ignoring IllegalFinPointException while editing a Freeform Fin"); } } } From 9aa71c94cf53bd7efa5a5ee8882c347b83c1a453 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Fri, 22 Jun 2018 17:03:33 -0400 Subject: [PATCH 04/47] [Fixes #424] Respaced FinSetConfig Window: Resolved some sources of phantom whitespace; Spacing on component configuration windows is now generally tighter. --- .../configdialog/FreeformFinSetConfig.java | 52 ++++++++++--------- .../configdialog/RocketComponentConfig.java | 10 ++-- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java index 0f989e526..cc25c704c 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java @@ -1,6 +1,5 @@ package net.sf.openrocket.gui.configdialog; - import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -199,9 +198,9 @@ public class FreeformFinSetConfig extends FinSetConfig { } - + // edit fin points directly here private JPanel shapePane() { - JPanel panel = new JPanel(new MigLayout("fill")); + JPanel panel = new JPanel(null); // Create the figure @@ -247,30 +246,33 @@ public class FreeformFinSetConfig extends FinSetConfig { } } }); - - panel.add(tablePane, "growy, width 100lp:100lp:, height 100lp:250lp:"); - panel.add(figurePane, "gap unrel, spanx, spany 3, growx, growy 1000, height 100lp:250lp:, wrap"); - - panel.add(new StyledLabel(trans.get("lbl.doubleClick1"), -2), "alignx 50%, wrap"); - panel.add(new StyledLabel(trans.get("FreeformFinSetConfig.lbl.doubleClick2"), -2), "alignx 50%, wrap"); - - panel.add(scaleButton, "spany 2, alignx 50%, aligny 50%"); - panel.add(exportCsvButton, "spany 2, alignx 50%, aligny 50%"); - ScaleSelector selector = new ScaleSelector(figurePane); - panel.add( selector, "spany 2, aligny 50%"); - - JButton importButton = new JButton(trans.get("CustomFinImport.button.label")); - importButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - importImage(); - } - }); - panel.add(importButton, "spany 2, bottom"); + JButton importButton = new JButton(trans.get("CustomFinImport.button.label")); + importButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + importImage(); + } + }); + ScaleSelector selector = new ScaleSelector(figurePane); + + panel.setLayout(new MigLayout("fill, gap 5!","", "[nogrid, fill, sizegroup display, growprio 200]5![sizegroup text, growprio 5]5![sizegroup buttons, align top, growprio 5]0!")); + + // first row: main display + panel.add(tablePane, "width 100lp:100lp:, growy"); + panel.add(figurePane, "width 200lp:400lp:, gap unrel, grow, height 100lp:250lp:, wrap"); + + // row of text directly below figure + panel.add(new StyledLabel(trans.get("lbl.doubleClick1")+" "+trans.get("FreeformFinSetConfig.lbl.doubleClick2"), -2), "spanx 3"); + panel.add(new StyledLabel(trans.get("FreeformFinSetConfig.lbl.clickDrag"), -2), "spanx 3"); + panel.add(new StyledLabel(trans.get("FreeformFinSetConfig.lbl.ctrlClick"), -2), "spanx 3, wrap"); + + // row of controls at the bottom of the tab: + panel.add(selector, "aligny bottom, gap unrel"); + panel.add(scaleButton, ""); + panel.add(importButton, ""); + panel.add(exportCsvButton, ""); // panel.add(new CustomFinBmpImporter(finset), "spany 2, bottom"); - panel.add(new StyledLabel(trans.get("FreeformFinSetConfig.lbl.clickDrag"), -2), "right, wrap"); - panel.add(new StyledLabel(trans.get("FreeformFinSetConfig.lbl.ctrlClick"), -2), "right"); return panel; } diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java index 9de060940..6321ed099 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java @@ -77,7 +77,7 @@ public class RocketComponentConfig extends JPanel { public RocketComponentConfig(OpenRocketDocument document, RocketComponent component) { - setLayout(new MigLayout("fill", "[min,align right]:10[fill, grow]")); + setLayout(new MigLayout("fill, gap 5!", "[]:5[]", "[growprio 10]10![fill, grow, growprio 500]10![growprio 10]")); this.document = document; this.component = component; @@ -85,7 +85,7 @@ public class RocketComponentConfig extends JPanel { JLabel label = new JLabel(trans.get("RocketCompCfg.lbl.Componentname")); //// The component name. label.setToolTipText(trans.get("RocketCompCfg.ttip.Thecomponentname")); - this.add(label, "spanx, split"); + this.add(label, "spanx, height 50!, split"); componentNameField = new JTextField(15); textFieldListener = new TextFieldListener(); @@ -106,7 +106,7 @@ public class RocketComponentConfig extends JPanel { tabbedPane = new JTabbedPane(); - this.add(tabbedPane, "newline, span, growx, growy 1, wrap"); + this.add(tabbedPane, "newline, span, growx, growy 100, wrap"); //// Override and Mass and CG override options tabbedPane.addTab(trans.get("RocketCompCfg.tab.Override"), null, overrideTab(), @@ -132,7 +132,7 @@ public class RocketComponentConfig extends JPanel { this.remove(buttonPanel); } - buttonPanel = new JPanel(new MigLayout("fill, ins 0")); + buttonPanel = new JPanel(new MigLayout("fillx, ins 0")); //// Mass: infoLabel = new StyledLabel(" ", -1); @@ -154,7 +154,7 @@ public class RocketComponentConfig extends JPanel { updateFields(); - this.add(buttonPanel, "spanx, growx"); + this.add(buttonPanel, "dock south, spanx, growx, height 50!"); } From 95b1e8718b07237a7fede47b9c5424ecc7036432 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Fri, 22 Jun 2018 18:53:08 -0400 Subject: [PATCH 05/47] [cleanup] Refactored naming in ScaleSelector to be more consistent 'Zoom' -> 'Scale' --- .../gui/scalefigure/ScaleSelector.java | 75 +++++++++---------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleSelector.java b/swing/src/net/sf/openrocket/gui/scalefigure/ScaleSelector.java index c7c265ed4..7fd68c0d2 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleSelector.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/ScaleSelector.java @@ -16,23 +16,24 @@ import net.miginfocom.swing.MigLayout; import net.sf.openrocket.gui.util.Icons; import net.sf.openrocket.util.StateChangeListener; +@SuppressWarnings("serial") public class ScaleSelector extends JPanel { // Ready zoom settings private static final DecimalFormat PERCENT_FORMAT = new DecimalFormat("0.#%"); - private static final double[] ZOOM_LEVELS = { 0.15, 0.25, 0.5, 0.75, 1.0, 1.5, 2.0 }; - private static final String ZOOM_FIT = "Fit"; - private static final String[] ZOOM_SETTINGS; + private static final double[] SCALE_LEVELS = { 0.15, 0.25, 0.5, 0.75, 1.0, 1.5, 2.0 }; + private static final String SCALE_FIT = "Fit"; // trans.get("ScaleSelector.something.something"); + private static final String[] SCALE_LABELS; static { - ZOOM_SETTINGS = new String[ZOOM_LEVELS.length + 1]; - for (int i = 0; i < ZOOM_LEVELS.length; i++) - ZOOM_SETTINGS[i] = PERCENT_FORMAT.format(ZOOM_LEVELS[i]); - ZOOM_SETTINGS[ZOOM_SETTINGS.length - 1] = ZOOM_FIT; + SCALE_LABELS = new String[SCALE_LEVELS.length + 1]; + for (int i = 0; i < SCALE_LEVELS.length; i++) + SCALE_LABELS[i] = PERCENT_FORMAT.format(SCALE_LEVELS[i]); + SCALE_LABELS[SCALE_LABELS.length - 1] = SCALE_FIT; } private final ScaleScrollPane scrollPane; - private JComboBox zoomSelector; + private JComboBox scaleSelector; public ScaleSelector(ScaleScrollPane scroll) { super(new MigLayout()); @@ -45,29 +46,29 @@ public class ScaleSelector extends JPanel { @Override public void actionPerformed(ActionEvent e) { double scale = scrollPane.getScaling(); - scale = getPreviousScale(scale); + scale = getNextLargerScale(scale); scrollPane.setScaling(scale); } }); add(button, "gap"); // Zoom level selector - String[] settings = ZOOM_SETTINGS; + String[] settings = SCALE_LABELS; if (!scrollPane.isFittingAllowed()) { settings = Arrays.copyOf(settings, settings.length - 1); } - zoomSelector = new JComboBox(settings); - zoomSelector.setEditable(true); + scaleSelector = new JComboBox<>(settings); + scaleSelector.setEditable(true); setZoomText(); - zoomSelector.addActionListener(new ActionListener() { + scaleSelector.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { try { - String text = (String) zoomSelector.getSelectedItem(); + String text = (String) scaleSelector.getSelectedItem(); text = text.replaceAll("%", "").trim(); - if (text.toLowerCase(Locale.getDefault()).startsWith(ZOOM_FIT.toLowerCase(Locale.getDefault())) && + if (text.toLowerCase(Locale.getDefault()).startsWith(SCALE_FIT.toLowerCase(Locale.getDefault())) && scrollPane.isFittingAllowed()) { scrollPane.setFitting(true); setZoomText(); @@ -93,7 +94,7 @@ public class ScaleSelector extends JPanel { setZoomText(); } }); - add(zoomSelector, "gap rel"); + add(scaleSelector, "gap rel"); // Zoom in button button = new JButton(Icons.ZOOM_IN); @@ -101,7 +102,7 @@ public class ScaleSelector extends JPanel { @Override public void actionPerformed(ActionEvent e) { double scale = scrollPane.getScaling(); - scale = getNextScale(scale); + scale = getNextSmallerScale(scale); scrollPane.setScaling(scale); } }); @@ -110,43 +111,41 @@ public class ScaleSelector extends JPanel { } private void setZoomText() { - String text; - double zoom = scrollPane.getScaling(); - text = PERCENT_FORMAT.format(zoom); + String text = PERCENT_FORMAT.format(scrollPane.getScaling()); if (scrollPane.isFitting()) { text = "Fit (" + text + ")"; } - if (!text.equals(zoomSelector.getSelectedItem())) - zoomSelector.setSelectedItem(text); + if (!text.equals(scaleSelector.getSelectedItem())) + scaleSelector.setSelectedItem(text); } - private double getPreviousScale(double scale) { + private static double getNextLargerScale(final double currentScale) { int i; - for (i = 0; i < ZOOM_LEVELS.length - 1; i++) { - if (scale > ZOOM_LEVELS[i] + 0.05 && scale < ZOOM_LEVELS[i + 1] + 0.05) - return ZOOM_LEVELS[i]; + for (i = 0; i < SCALE_LEVELS.length - 1; i++) { + if (currentScale > SCALE_LEVELS[i] + 0.05 && currentScale < SCALE_LEVELS[i + 1] + 0.05) + return SCALE_LEVELS[i]; } - if (scale > ZOOM_LEVELS[ZOOM_LEVELS.length / 2]) { + if (currentScale > SCALE_LEVELS[SCALE_LEVELS.length / 2]) { // scale is large, drop to next lowest full 100% - scale = Math.ceil(scale - 1.05); - return Math.max(scale, ZOOM_LEVELS[i]); + double nextScale = Math.ceil(currentScale - 1.05); + return Math.max(nextScale, SCALE_LEVELS[i]); } // scale is small - return scale / 1.5; + return currentScale / 1.5; } - private double getNextScale(double scale) { + private static double getNextSmallerScale(final double currentScale) { int i; - for (i = 0; i < ZOOM_LEVELS.length - 1; i++) { - if (scale > ZOOM_LEVELS[i] - 0.05 && scale < ZOOM_LEVELS[i + 1] - 0.05) - return ZOOM_LEVELS[i + 1]; + for (i = 0; i < SCALE_LEVELS.length - 1; i++) { + if (currentScale > SCALE_LEVELS[i] - 0.05 && currentScale < SCALE_LEVELS[i + 1] - 0.05) + return SCALE_LEVELS[i + 1]; } - if (scale > ZOOM_LEVELS[ZOOM_LEVELS.length / 2]) { + if (currentScale > SCALE_LEVELS[SCALE_LEVELS.length / 2]) { // scale is large, give next full 100% - scale = Math.floor(scale + 1.05); - return scale; + double nextScale = Math.floor(currentScale + 1.05); + return nextScale; } - return scale * 1.5; + return currentScale * 1.5; } @Override From 9d76ece12851fa04dce78e5d608726f909092c39 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sun, 24 Jun 2018 16:57:08 -0400 Subject: [PATCH 06/47] [refactor] updated BoundingBox class to be more useful - FlightConfiguration now exposes the BoundingBox method for its rocket --- .../sf/openrocket/rocketcomponent/FinSet.java | 4 +- .../rocketcomponent/FlightConfiguration.java | 20 ++- .../net/sf/openrocket/util/BoundingBox.java | 115 +++++++++++++----- 3 files changed, 100 insertions(+), 39 deletions(-) diff --git a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java index 465e459a0..186f80dcc 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java @@ -587,11 +587,11 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab public BoundingBox getBoundingBox() { - BoundingBox singleFinBounds= new BoundingBox( getFinPoints()); + BoundingBox singleFinBounds= new BoundingBox().update(getFinPoints()); final double finLength = singleFinBounds.max.x; final double finHeight = singleFinBounds.max.y; - BoundingBox compBox = new BoundingBox( getComponentLocations() ); + BoundingBox compBox = new BoundingBox().update(getComponentLocations()); BoundingBox finSetBox = new BoundingBox( compBox.min.sub( 0, finHeight, finHeight ), compBox.max.add( finLength, finHeight, finHeight )); diff --git a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java index b1a394e31..661f6b11d 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java @@ -398,25 +398,37 @@ public class FlightConfiguration implements FlightConfigurableParameterCollection containing coordinates bounding the rocket. + * + * @deprecated Migrate to FlightConfiguration#BoundingBox, when practical. */ + @Deprecated public Collection getBounds() { + return getBoundingBox().toCollection(); + } + + /** + * Return the bounding box of the current configuration. + * + * @return + */ + public BoundingBox getBoundingBox() { if (rocket.getModID() != boundsModID) { boundsModID = rocket.getModID(); BoundingBox bounds = new BoundingBox(); for (RocketComponent component : this.getActiveComponents()) { - BoundingBox componentBounds = new BoundingBox( component.getComponentBounds() ); + BoundingBox componentBounds = new BoundingBox().update(component.getComponentBounds()); - bounds.compare( componentBounds ); + bounds.update( componentBounds ); } cachedLength = bounds.span().x; - cachedBounds.compare( bounds ); + cachedBounds.update( bounds ); } - return cachedBounds.toCollection(); + return cachedBounds; } /** diff --git a/core/src/net/sf/openrocket/util/BoundingBox.java b/core/src/net/sf/openrocket/util/BoundingBox.java index b6b30e12c..6d5f46eb8 100644 --- a/core/src/net/sf/openrocket/util/BoundingBox.java +++ b/core/src/net/sf/openrocket/util/BoundingBox.java @@ -1,5 +1,6 @@ package net.sf.openrocket.util; +import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collection; @@ -8,8 +9,7 @@ public class BoundingBox { public Coordinate max; public BoundingBox() { - min = Coordinate.MAX.setWeight( 0.0); - max = Coordinate.MIN.setWeight( 0.0); + clear(); } public BoundingBox( Coordinate _min, Coordinate _max) { @@ -18,14 +18,9 @@ public class BoundingBox { this.max = _max.clone(); } - public BoundingBox( Coordinate[] list ) { - this(); - this.compare( list); - } - - public BoundingBox( Collection list ) { - this(); - this.compare( list.toArray( new Coordinate[0] )); + public void clear() { + min = Coordinate.MAX.setWeight( 0.0); + max = Coordinate.MIN.setWeight( 0.0); } @Override @@ -33,39 +28,90 @@ public class BoundingBox { return new BoundingBox( this.min, this.max ); } - public void compare( Coordinate c ) { - compare_against_min(c); - compare_against_max(c); + + private void update_x_min( final double xVal) { + if( min.x > xVal) + min = min.setX( xVal ); } - protected void compare_against_min( Coordinate c ) { - if( min.x > c.x ) - min = min.setX( c.x ); - if( min.y > c.y ) - min = min.setY( c.y ); - if( min.z > c.z ) - min = min.setZ( c.z ); + private void update_y_min( final double yVal) { + if( min.y > yVal ) + min = min.setY( yVal ); } - protected void compare_against_max( Coordinate c) { - if( max.x < c.x ) - max = max.setX( c.x ); - if( max.y < c.y ) - max = max.setY( c.y ); - if( max.z < c.z ) - max = max.setZ( c.z ); + private void update_z_min( final double zVal) { + if( min.z > zVal ) + min = min.setZ( zVal ); + } + + private void update_x_max( final double xVal) { + if( max.x < xVal ) + max = max.setX( xVal ); + } + + private void update_y_max( final double yVal) { + if( max.y < yVal ) + max = max.setY( yVal ); + } + + private void update_z_max( final double zVal) { + if( max.z < zVal ) + max = max.setZ( zVal ); + } + + public BoundingBox update( final double val) { + update_x_min(val); + update_y_min(val); + update_z_min(val); + + update_x_max(val); + update_y_max(val); + update_z_max(val); + return this; + } + + + public void update( Coordinate c ) { + update_x_min(c.x); + update_y_min(c.y); + update_z_min(c.z); + + update_x_max(c.x); + update_y_max(c.y); + update_z_max(c.z); } - public BoundingBox compare( Coordinate[] list ) { + public BoundingBox update( Rectangle2D rect ) { + update_x_min(rect.getMinX()); + update_y_min(rect.getMinY()); + update_x_max(rect.getMaxX()); + update_y_max(rect.getMaxY()); + return this; + } + + public BoundingBox update( final Coordinate[] list ) { for( Coordinate c: list ) { - compare( c ); + update( c ); } return this; } - public void compare( BoundingBox other ) { - compare_against_min( other.min); - compare_against_max( other.max); + public BoundingBox update( Collection list ) { + for( Coordinate c: list ) { + update( c ); + } + return this; + } + + public BoundingBox update( BoundingBox other ) { + update_x_min(other.min.x); + update_y_min(other.min.y); + update_z_min(other.min.y); + + update_x_max(other.max.x); + update_y_max(other.max.y); + update_z_max(other.max.z); + return this; } public Coordinate span() { return max.sub( min ); } @@ -81,9 +127,12 @@ public class BoundingBox { return toReturn; } + public Rectangle2D toRectangle() { + return new Rectangle2D.Double(min.x, min.y, (max.x-min.x), (max.y - min.y)); + } + @Override public String toString() { -// return String.format("[( %6.4f, %6.4f, %6.4f) < ( %6.4f, %6.4f, %6.4f)]", return String.format("[( %g, %g, %g) < ( %g, %g, %g)]", min.x, min.y, min.z, max.x, max.y, max.z ); From 885df6ce041cd4dcf5e52cf6494fda2fa30295d2 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sun, 24 Jun 2018 19:01:28 -0400 Subject: [PATCH 07/47] [refactor] Reduce redundant methods in Scalefigures, and harmonize common function names - removed interface that was only inherited by the single AbstractBaseClass - harmonizes the border pixels variables in the scalefigure package --- .../configdialog/FreeformFinSetConfig.java | 2 +- .../GeneralOptimizationDialog.java | 1 - .../sf/openrocket/gui/print/DesignReport.java | 6 +- .../sf/openrocket/gui/print/PrintFigure.java | 8 +- .../gui/scalefigure/AbstractScaleFigure.java | 242 ++++++--- .../gui/scalefigure/FinPointFigure.java | 483 +++++++++--------- .../gui/scalefigure/RocketFigure.java | 327 ++++-------- .../gui/scalefigure/ScaleFigure.java | 82 --- .../gui/scalefigure/ScaleScrollPane.java | 192 +++---- .../gui/scalefigure/ScaleSelector.java | 23 +- 10 files changed, 625 insertions(+), 741 deletions(-) delete mode 100644 swing/src/net/sf/openrocket/gui/scalefigure/ScaleFigure.java diff --git a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java index cc25c704c..2f8cf3f35 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java @@ -367,7 +367,7 @@ public class FreeformFinSetConfig extends FinSetConfig { private int dragIndex = -1; private FinPointScrollPane( final FinPointFigure _figure) { - super( _figure, true); + super( _figure); } @Override diff --git a/swing/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java index 33ec67823..14cda1d8d 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java @@ -506,7 +506,6 @@ public class GeneralOptimizationDialog extends JDialog { // // Rocket figure figure = new RocketFigure( getSelectedSimulation().getRocket() ); - figure.setBorderPixels(1, 1); ScaleScrollPane figureScrollPane = new ScaleScrollPane(figure); figureScrollPane.setFitting(true); panel.add(figureScrollPane, "span, split, height 200lp, grow"); diff --git a/swing/src/net/sf/openrocket/gui/print/DesignReport.java b/swing/src/net/sf/openrocket/gui/print/DesignReport.java index 0a56cce7d..d95f9fb8f 100644 --- a/swing/src/net/sf/openrocket/gui/print/DesignReport.java +++ b/swing/src/net/sf/openrocket/gui/print/DesignReport.java @@ -177,7 +177,7 @@ public class DesignReport { canvas.beginText(); canvas.setFontAndSize(ITextHelper.getBaseFont(), PrintUtilities.NORMAL_FONT_SIZE); - int figHeightPts = (int) (PrintUnit.METERS.toPoints(figure.getFigureHeight()) * 0.4 * (scale / PrintUnit.METERS + int figHeightPts = (int) (PrintUnit.METERS.toPoints(figure.getHeight()) * 0.4 * (scale / PrintUnit.METERS .toPoints(1))); final int diagramHeight = pageImageableHeight * 2 - 70 - (figHeightPts); canvas.moveText(document.leftMargin() + pageSize.getBorderWidthLeft(), diagramHeight); @@ -274,7 +274,7 @@ public class DesignReport { theFigure.updateFigure(); double scale = - (thePageImageableWidth * 2.2) / theFigure.getFigureWidth(); + (thePageImageableWidth * 2.2) / theFigure.getWidth(); theFigure.setScale(scale); /* * page dimensions are in points-per-inch, which, in Java2D, are the same as pixels-per-inch; thus we don't need any conversion @@ -288,7 +288,7 @@ public class DesignReport { int y = PrintUnit.POINTS_PER_INCH; //If the y dimension is negative, then it will potentially be drawn off the top of the page. Move the origin //to allow for this. - if (theFigure.getDimensions().getY() < 0.0d) { + if (theFigure.getHeight() < 0.0d) { y += (int) halfFigureHeight; } g2d.translate(20, y); diff --git a/swing/src/net/sf/openrocket/gui/print/PrintFigure.java b/swing/src/net/sf/openrocket/gui/print/PrintFigure.java index 13661be97..344713505 100644 --- a/swing/src/net/sf/openrocket/gui/print/PrintFigure.java +++ b/swing/src/net/sf/openrocket/gui/print/PrintFigure.java @@ -22,18 +22,12 @@ public class PrintFigure extends RocketFigure { super(rkt); } - @Override - protected double computeTy(int heightPx) { - super.computeTy(heightPx); - return 0; - } - public void setScale(final double theScale) { this.scale = theScale; //dpi/0.0254*scaling; updateFigure(); } public double getFigureHeightPx() { - return this.figureHeightPx; + return this.getSize().height; } } diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java index 2955b34d5..258e1f0dc 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java @@ -2,6 +2,8 @@ package net.sf.openrocket.gui.scalefigure; import java.awt.Color; import java.awt.Dimension; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; import java.util.EventListener; import java.util.EventObject; import java.util.LinkedList; @@ -9,124 +11,194 @@ import java.util.List; import javax.swing.JPanel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.StateChangeListener; - @SuppressWarnings("serial") -public abstract class AbstractScaleFigure extends JPanel implements ScaleFigure { - +public abstract class AbstractScaleFigure extends JPanel { + + private final static Logger log = LoggerFactory.getLogger(AbstractScaleFigure.class); + + /** + * Extra scaling applied to the figure. The f***ing Java JRE doesn't know + * how to draw shapes when using very large scaling factors, so this must + * be manually applied to every single shape used. + *

+ * The scaling factor used is divided by this value, and every coordinate used + * in the figures must be multiplied by this factor. + */ + public static final double EXTRA_SCALE = 1.0; + + public static final double INCHES_PER_METER = 39.3701; + public static final double METERS_PER_INCH = 0.0254; + + public static final double MINIMUM_ZOOM = 0.01; // == 1 % + public static final double MAXIMUM_ZOOM = 1000.00; // == 10,000 % + // Number of pixels to leave at edges when fitting figure private static final int DEFAULT_BORDER_PIXELS_WIDTH = 30; private static final int DEFAULT_BORDER_PIXELS_HEIGHT = 20; + // constant factor that scales screen real-estate to rocket-space + private final double baseScale; + private double userScale = 1.0; + protected double scale = -1; - protected final double dpi; - - protected double scale = 1.0; - protected double scaling = 1.0; - - protected int borderPixelsWidth = DEFAULT_BORDER_PIXELS_WIDTH; - protected int borderPixelsHeight = DEFAULT_BORDER_PIXELS_HEIGHT; + protected static final Dimension borderThickness_px = new Dimension(DEFAULT_BORDER_PIXELS_WIDTH, DEFAULT_BORDER_PIXELS_HEIGHT); + protected Dimension originLocation_px = new Dimension(0,0); + // ======= whatever this figure is drawing, in real-space coordinates: meters + protected Rectangle2D subjectBounds_m = null; + + + // combines the translation and scale in one place: + // which frames does this transform between ? + protected AffineTransform projection = null; + protected final List listeners = new LinkedList(); public AbstractScaleFigure() { - this.dpi = GUIUtil.getDPI(); - this.scaling = 1.0; - this.scale = dpi / 0.0254 * scaling; - + // produces a pixels-per-meter scale factor + // + // dots dots inch + // ---- = ------ * ----- + // meter inch meter + // + this.baseScale = GUIUtil.getDPI() * INCHES_PER_METER; + this.userScale = 1.0; + this.scale = baseScale * userScale; + + this.setPreferredSize(new Dimension(100,100)); + setSize(100,100); + setBackground(Color.WHITE); setOpaque(true); } - - - public abstract void updateFigure(); - - public abstract double getFigureWidth(); - - public abstract double getFigureHeight(); - - - @Override - public double getScaling() { - return scaling; + public double getUserScale(){ + return userScale; } - @Override - public double getAbsoluteScale() { - return scale; + public double getAbsoluteScale() { + return scale; + } + + public Dimension getSubjectOrigin() { + return originLocation_px; + } + + /** + * Set the scale level of the figure. A scale value of 1.0 is equivalent to 100 % scale. + * smaller scale display the subject smaller. + * + * @param newScaleRequest the scale level. + */ + public void scaleTo(final double newScaleRequest) { + if (MathUtil.equals(this.userScale, newScaleRequest, 0.01)){ + return;} + if (Double.isInfinite(newScaleRequest) || Double.isNaN(newScaleRequest)) { + return;} + + log.warn(String.format("scaling Request from %g => %g @%s\n", this.userScale, newScaleRequest, this.getClass().getSimpleName()), new Throwable()); + + this.userScale = MathUtil.clamp( newScaleRequest, MINIMUM_ZOOM, MAXIMUM_ZOOM); + + this.scale = baseScale * userScale; } - @Override - public void setScaling(double scaling) { - if (Double.isInfinite(scaling) || Double.isNaN(scaling)) - scaling = 1.0; - if (scaling < 0.001) - scaling = 0.001; - if (scaling > 1000) - scaling = 1000; - if (Math.abs(this.scaling - scaling) < 0.01) - return; - this.scaling = scaling; - this.scale = dpi / 0.0254 * scaling; - updateFigure(); - } - - @Override - public void setScaling(Dimension bounds) { - double zh = 1, zv = 1; - int w = bounds.width - 2 * borderPixelsWidth - 20; - int h = bounds.height - 2 * borderPixelsHeight - 20; - - if (w < 10) - w = 10; - if (h < 10) - h = 10; - - zh = (w) / getFigureWidth(); - zv = (h) / getFigureHeight(); - - double s = Math.min(zh, zv) / dpi * 0.0254 - 0.001; - - // Restrict to 100% - if (s > 1.0) { - s = 1.0; + /** + * Set the scale level to display newBounds + * + * @param bounds the bounds of the figure. + */ + public void scaleTo(Dimension newBounds) { + if( 0 == newBounds.getWidth() || 0 == newBounds.getHeight()) + return; + + updateSubjectDimensions(); + updateCanvasOrigin(); + updateCanvasSize(); + updateTransform(); + + // dimensions within the viewable area, which are available to draw + final int drawable_width_px = newBounds.width - 2 * borderThickness_px.width; + final int drawable_height_px = newBounds.height - 2 * borderThickness_px.height; + + if(( 0 < drawable_width_px ) && ( 0 < drawable_height_px)) { + final double width_scale = (drawable_width_px) / ( subjectBounds_m.getWidth() * baseScale); + final double height_scale = (drawable_height_px) / ( subjectBounds_m.getHeight() * baseScale); + final double minScale = Math.min(height_scale, width_scale); + + scaleTo(minScale); } + } + + /** + * Return the pixel coordinates of the subject's origin. + * + * @return the pixel coordinates of the figure origin. + */ + protected abstract void updateSubjectDimensions(); - setScaling(s); + protected abstract void updateCanvasOrigin(); + + /** + * update preferred figure Size + + */ + protected void updateCanvasSize() { + Dimension preferredFigureSize_px = new Dimension((int)(subjectBounds_m.getWidth()*scale) + 2*borderThickness_px.width, + (int)(subjectBounds_m.getHeight()*scale) + 2*borderThickness_px.height); + + setPreferredSize(preferredFigureSize_px); + setMinimumSize(preferredFigureSize_px); + revalidate(); + } + + protected void updateTransform(){ + // Calculate and store the transformation used + // (inverse is used in detecting clicks on objects) + projection = new AffineTransform(); + projection.translate(this.originLocation_px.width, originLocation_px.height); + // Mirror position Y-axis upwards + projection.scale(scale, -scale); + } + + /** + * Updates the figure shapes and figure size. + */ + public void updateFigure() { + log.debug(String.format("____ Updating %s to: %g user scale, %g overall scale", this.getClass().getSimpleName(), this.getAbsoluteScale(), this.scale)); + + updateSubjectDimensions(); + updateCanvasOrigin(); + updateCanvasSize(); + updateTransform(); + + revalidate(); + repaint(); + } + + protected Dimension getBorderPixels() { + return borderThickness_px; } - - - @Override - public Dimension getBorderPixels() { - return new Dimension(borderPixelsWidth, borderPixelsHeight); - } - - @Override - public void setBorderPixels(int width, int height) { - this.borderPixelsWidth = width; - this.borderPixelsHeight = height; - } - - - @Override + public void addChangeListener(StateChangeListener listener) { listeners.add(0, listener); } - @Override public void removeChangeListener(StateChangeListener listener) { listeners.remove(listener); } - private EventObject changeEvent = null; - protected void fireChangeEvent() { - if (changeEvent == null) - changeEvent = new EventObject(this); + final EventObject changeEvent = new EventObject(this); + // Copy the list before iterating to prevent concurrent modification exceptions. EventListener[] list = listeners.toArray(new EventListener[0]); for (EventListener l : list) { @@ -135,5 +207,5 @@ public abstract class AbstractScaleFigure extends JPanel implements ScaleFigure } } } - + } diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java index b6fdd2bef..1a275ae63 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java @@ -13,96 +13,73 @@ import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Path2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; +import java.util.LinkedList; +import java.util.List; +import org.slf4j.*; import net.sf.openrocket.rocketcomponent.FreeformFinSet; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.SymmetricComponent; +import net.sf.openrocket.rocketcomponent.Transition; +import net.sf.openrocket.rocketcomponent.position.AxialMethod; import net.sf.openrocket.unit.Tick; import net.sf.openrocket.unit.Unit; import net.sf.openrocket.unit.UnitGroup; +import net.sf.openrocket.util.BoundingBox; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.MathUtil; +import net.sf.openrocket.util.StateChangeListener; -// TODO: MEDIUM: the figure jumps and bugs when using automatic fitting - @SuppressWarnings("serial") public class FinPointFigure extends AbstractScaleFigure { - - private static final int BOX_SIZE = 4; - - private final FreeformFinSet finset; + + private final static Logger log = LoggerFactory.getLogger(FinPointFigure.class); + + + private static final float MINIMUM_CANVAS_SIZE_METERS = 0.01f; // i.e. 1 cm + + private static final Color GRID_LINE_COLOR = new Color( 137, 137, 137, 32); + private static final float GRID_LINE_BASE_WIDTH = 0.001f; + + private static final int LINE_WIDTH_PIXELS = 1; + + // the size of the boxes around each fin point vertex + private static final float BOX_WIDTH_PIXELS = 12; + + private static final double MINOR_TICKS = 0.05; + private static final double MAJOR_TICKS = 0.1; + + private final FreeformFinSet finset; private int modID = -1; - private double minX, maxX, maxY; - private double figureWidth = 0; - private double figureHeight = 0; - private double translateX = 0; - private double translateY = 0; - - private AffineTransform transform; - private Rectangle2D.Double[] handles = null; + protected final List listeners = new LinkedList(); + + private Rectangle2D.Double[] finPointHandles = null; + public FinPointFigure(FreeformFinSet finset) { this.finset = finset; + + // useful for debugging -- shows a contrast against un-drawn space. + setBackground(Color.WHITE); + setOpaque(true); + + updateTransform(); } - - + @Override public void paintComponent(Graphics g) { super.paintComponent(g); - Graphics2D g2 = (Graphics2D) g; - - if (modID != finset.getRocket().getAerodynamicModID()) { - modID = finset.getRocket().getAerodynamicModID(); - calculateDimensions(); - } - - - double tx, ty; - // Calculate translation for figure centering - if (figureWidth * scale + 2 * borderPixelsWidth < getWidth()) { - - // Figure fits in the viewport - tx = (getWidth() - figureWidth * scale) / 2 - minX * scale; - - } else { - - // Figure does not fit in viewport - tx = borderPixelsWidth - minX * scale; - - } - - - if (figureHeight * scale + 2 * borderPixelsHeight < getHeight()) { - ty = getHeight() - borderPixelsHeight; - } else { - ty = borderPixelsHeight + figureHeight * scale; - } - - if (Math.abs(translateX - tx) > 1 || Math.abs(translateY - ty) > 1) { - // Origin has changed, fire event - translateX = tx; - translateY = ty; - fireChangeEvent(); - } - - - if (Math.abs(translateX - tx) > 1 || Math.abs(translateY - ty) > 1) { - // Origin has changed, fire event - translateX = tx; - translateY = ty; - fireChangeEvent(); - } - - - // Calculate and store the transformation used - transform = new AffineTransform(); - transform.translate(translateX, translateY); - transform.scale(scale / EXTRA_SCALE, -scale / EXTRA_SCALE); - - // TODO: HIGH: border Y-scale upwards - - g2.transform(transform); + Graphics2D g2 = (Graphics2D) g.create(); + + if (modID != finset.getRocket().getAerodynamicModID()) { + modID = finset.getRocket().getAerodynamicModID(); + updateTransform(); + } + + g2.transform(projection); // Set rendering hints appropriately g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, @@ -113,128 +90,184 @@ public class FinPointFigure extends AbstractScaleFigure { RenderingHints.VALUE_ANTIALIAS_ON); + // Background grid + paintBackgroundGrid( g2); - Rectangle visible = g2.getClipBounds(); - double x0 = ((double) visible.x - 3) / EXTRA_SCALE; - double x1 = ((double) visible.x + visible.width + 4) / EXTRA_SCALE; - double y0 = ((double) visible.y - 3) / EXTRA_SCALE; - double y1 = ((double) visible.y + visible.height + 4) / EXTRA_SCALE; - - - // Background grid - - g2.setStroke(new BasicStroke((float) (1.0 * EXTRA_SCALE / scale), - BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); - g2.setColor(new Color(0, 0, 255, 30)); - - Unit unit; - if (this.getParent() != null && - this.getParent().getParent() instanceof ScaleScrollPane) { - unit = ((ScaleScrollPane) this.getParent().getParent()).getCurrentUnit(); - } else { - unit = UnitGroup.UNITS_LENGTH.getDefaultUnit(); - } - - // vertical - Tick[] ticks = unit.getTicks(x0, x1, - ScaleScrollPane.MINOR_TICKS / scale, - ScaleScrollPane.MAJOR_TICKS / scale); - Line2D.Double line = new Line2D.Double(); - for (Tick t : ticks) { - if (t.major) { - line.setLine(t.value * EXTRA_SCALE, y0 * EXTRA_SCALE, - t.value * EXTRA_SCALE, y1 * EXTRA_SCALE); - g2.draw(line); - } - } - - // horizontal - ticks = unit.getTicks(y0, y1, - ScaleScrollPane.MINOR_TICKS / scale, - ScaleScrollPane.MAJOR_TICKS / scale); - for (Tick t : ticks) { - if (t.major) { - line.setLine(x0 * EXTRA_SCALE, t.value * EXTRA_SCALE, - x1 * EXTRA_SCALE, t.value * EXTRA_SCALE); - g2.draw(line); - } - } - - - - - - // Base rocket line - g2.setStroke(new BasicStroke((float) (3.0 * EXTRA_SCALE / scale), - BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); - g2.setColor(Color.GRAY); - - g2.drawLine((int) (x0 * EXTRA_SCALE), 0, (int) (x1 * EXTRA_SCALE), 0); - - - // Fin shape - Coordinate[] points = finset.getFinPoints(); - Path2D.Double shape = new Path2D.Double(); - shape.moveTo(0, 0); - for (int i = 1; i < points.length; i++) { - shape.lineTo(points[i].x * EXTRA_SCALE, points[i].y * EXTRA_SCALE); - } - - g2.setStroke(new BasicStroke((float) (1.0 * EXTRA_SCALE / scale), - BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); - g2.setColor(Color.BLACK); - g2.draw(shape); - - - // Fin point boxes - g2.setColor(new Color(150, 0, 0)); - double s = BOX_SIZE * EXTRA_SCALE / scale; - handles = new Rectangle2D.Double[points.length]; - for (int i = 0; i < points.length; i++) { - Coordinate c = points[i]; - handles[i] = new Rectangle2D.Double(c.x * EXTRA_SCALE - s, c.y * EXTRA_SCALE - s, 2 * s, 2 * s); - g2.draw(handles[i]); - } + paintRocketBody(g2); + paintFinShape(g2); + paintFinHandles(g2); } - + public void paintBackgroundGrid( Graphics2D g2){ + Rectangle visible = g2.getClipBounds(); + int x0 = visible.x - 3; + int x1 = visible.x + visible.width + 4; + int y0 = visible.y - 3; + int y1 = visible.y + visible.height + 4; - public int getIndexByPoint(double x, double y) { - if (handles == null) - return -1; - - // Calculate point in shapes' coordinates - Point2D.Double p = new Point2D.Double(x, y); - try { - transform.inverseTransform(p, p); - } catch (NoninvertibleTransformException e) { - return -1; - } - - for (int i = 0; i < handles.length; i++) { - if (handles[i].contains(p)) - return i; - } - return -1; + final float grid_line_width = (float)(FinPointFigure.GRID_LINE_BASE_WIDTH/this.scale); + g2.setStroke(new BasicStroke( grid_line_width, + BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); + g2.setColor(FinPointFigure.GRID_LINE_COLOR); + + Unit unit; + if (this.getParent() != null && this.getParent().getParent() instanceof ScaleScrollPane) { + unit = ((ScaleScrollPane) this.getParent().getParent()).getCurrentUnit(); + } else { + unit = UnitGroup.UNITS_LENGTH.getDefaultUnit(); + } + + // vertical + Tick[] verticalTicks = unit.getTicks(x0, x1, MINOR_TICKS, MAJOR_TICKS); + Line2D.Double line = new Line2D.Double(); + for (Tick t : verticalTicks) { + if (t.major) { + line.setLine( t.value, y0, t.value, y1); + g2.draw(line); + } + } + + // horizontal + Tick[] horizontalTicks = unit.getTicks(y0, y1, MINOR_TICKS, MAJOR_TICKS); + for (Tick t : horizontalTicks) { + if (t.major) { + line.setLine( x0, t.value, x1, t.value); + g2.draw(line); + } + } + } + + private void paintRocketBody( Graphics2D g2){ + RocketComponent comp = finset.getParent(); + if( comp instanceof Transition ){ + paintBodyTransition(g2); + }else{ + paintBodyTube(g2); + } + } + + // NOTE: This function drawns relative to the reference point of the BODY component + // In other words: 0,0 == the front, foreRadius of the body component + private void paintBodyTransition( Graphics2D g2){ + + // setup lines + final float bodyLineWidth = (float) ( LINE_WIDTH_PIXELS / scale ); + g2.setStroke(new BasicStroke( bodyLineWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); + g2.setColor(Color.BLACK); + + Transition body = (Transition) finset.getParent(); + final float xResolution_m = 0.01f; // distance between draw points, in meters + + final double xFinStart = finset.asPositionValue(AxialMethod.TOP); //<< in body frame + + // vv in fin-frame == draw-frame vv + final double xOffset = -xFinStart; + final double yOffset = -body.getRadius(xFinStart); + + Path2D.Double bodyShape = new Path2D.Double(); + // draw front-cap: + bodyShape.moveTo( xOffset, yOffset); + bodyShape.lineTo( xOffset, yOffset + body.getForeRadius()); + + final float length_m = (float)( body.getLength()); + Point2D.Double cur = new Point2D.Double (); + for( double xBody = xResolution_m ; xBody < length_m; xBody += xResolution_m ){ + // xBody is distance from front of parent body + cur.x = xOffset + xBody; // offset from origin (front of fin) + cur.y = yOffset + body.getRadius( xBody); // offset from origin ( fin-front-point ) + + bodyShape.lineTo( cur.x, cur.y); + } + + // draw end-cap + bodyShape.lineTo( xOffset + length_m, yOffset + body.getAftRadius()); + bodyShape.lineTo( xOffset + length_m, yOffset); + + g2.draw(bodyShape); + } + + private void paintBodyTube( Graphics2D g2){ + Rectangle visible = g2.getClipBounds(); + int x0 = visible.x - 3; + int x1 = visible.x + visible.width + 4; + + final float bodyLineWidth = (float) ( LINE_WIDTH_PIXELS / scale ); + g2.setStroke(new BasicStroke( bodyLineWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); + g2.setColor(Color.BLACK); + + g2.drawLine((int) x0, 0, (int)x1, 0); + } + + private void paintFinShape(final Graphics2D g2){ + // excludes fin tab points + final Coordinate[] drawPoints = finset.getFinPoints(); + + Path2D.Double shape = new Path2D.Double(); + Coordinate startPoint= drawPoints[0]; + shape.moveTo( startPoint.x, startPoint.y); + for (int i = 1; i < drawPoints.length; i++) { + shape.lineTo( drawPoints[i].x, drawPoints[i].y); + } + + final float finEdgeWidth_m = (float) (LINE_WIDTH_PIXELS / scale ); + g2.setStroke(new BasicStroke( finEdgeWidth_m, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); + g2.setColor(Color.BLUE); + g2.draw(shape); } - + private void paintFinHandles(final Graphics2D g2) { + // excludes fin tab points + final Coordinate[] drawPoints = finset.getFinPoints(); + + // Fin point boxes + final float boxWidth = (float) (BOX_WIDTH_PIXELS / scale ); + final float boxEdgeWidth_m = (float) ( LINE_WIDTH_PIXELS / scale ); + g2.setStroke(new BasicStroke( boxEdgeWidth_m, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); + g2.setColor(new Color(150, 0, 0)); + final double boxHalfWidth = boxWidth/2; + finPointHandles = new Rectangle2D.Double[ drawPoints.length]; + for (int i = 0; i < drawPoints.length; i++) { + Coordinate c = drawPoints[i]; + finPointHandles[i] = new Rectangle2D.Double(c.x - boxHalfWidth, c.y - boxHalfWidth, boxWidth, boxWidth); + g2.draw(finPointHandles[i]); + } + } + + public int getIndexByPoint(double x, double y) { + if (finPointHandles == null) + return -1; + + // Calculate point in shapes' coordinates + Point2D.Double p = new Point2D.Double(x, y); + try { + projection.inverseTransform(p, p); + } catch (NoninvertibleTransformException e) { + return -1; + } + + for (int i = 0; i < finPointHandles.length; i++) { + if (finPointHandles[i].contains(p)) + return i; + } + return -1; + } + public int getSegmentByPoint(double x, double y) { - if (handles == null) + if (finPointHandles == null) return -1; // Calculate point in shapes' coordinates Point2D.Double p = new Point2D.Double(x, y); try { - transform.inverseTransform(p, p); + projection.inverseTransform(p, p); } catch (NoninvertibleTransformException e) { return -1; } - double x0 = p.x / EXTRA_SCALE; - double y0 = p.y / EXTRA_SCALE; - double delta = BOX_SIZE / scale; + double x0 = p.x; + double y0 = p.y; + double delta = BOX_WIDTH_PIXELS /*/ scale*/; //System.out.println("Point: " + x0 + "," + y0); //System.out.println("delta: " + (BOX_SIZE / scale)); @@ -262,84 +295,60 @@ public class FinPointFigure extends AbstractScaleFigure { public Point2D.Double convertPoint(double x, double y) { Point2D.Double p = new Point2D.Double(x, y); try { - transform.inverseTransform(p, p); + projection.inverseTransform(p, p); } catch (NoninvertibleTransformException e) { assert (false) : "Should not occur"; return new Point2D.Double(0, 0); } - p.setLocation(p.x / EXTRA_SCALE, p.y / EXTRA_SCALE); + p.setLocation(p.x, p.y); return p; } - - - @Override - public Dimension getOrigin() { + public Dimension getSubjectOrigin() { if (modID != finset.getRocket().getAerodynamicModID()) { modID = finset.getRocket().getAerodynamicModID(); - calculateDimensions(); + updateTransform(); } - return new Dimension((int) translateX, (int) translateY); - } - + return new Dimension(originLocation_px.width, originLocation_px.height); + } + @Override - public double getFigureWidth() { - if (modID != finset.getRocket().getAerodynamicModID()) { - modID = finset.getRocket().getAerodynamicModID(); - calculateDimensions(); - } - return figureWidth; - } - - @Override - public double getFigureHeight() { - if (modID != finset.getRocket().getAerodynamicModID()) { - modID = finset.getRocket().getAerodynamicModID(); - calculateDimensions(); - } - return figureHeight; - } - - - private void calculateDimensions() { - minX = 0; - maxX = 0; - maxY = 0; - - for (Coordinate c : finset.getFinPoints()) { - if (c.x < minX) - minX = c.x; - if (c.x > maxX) - maxX = c.x; - if (c.y > maxY) - maxY = c.y; - } - - if (maxX < 0.01) - maxX = 0.01; - - figureWidth = maxX - minX; - figureHeight = maxY; - + protected void updateSubjectDimensions(){ + // update subject bounds + BoundingBox newBounds = new BoundingBox(); + + // subsequent updates can only increase the size of the bounds, so this is the minimum size. + newBounds.update( MINIMUM_CANVAS_SIZE_METERS); + + SymmetricComponent parent = (SymmetricComponent)this.finset.getParent(); - Dimension d = new Dimension((int) (figureWidth * scale + 2 * borderPixelsWidth), - (int) (figureHeight * scale + 2 * borderPixelsHeight)); - - if (!d.equals(getPreferredSize()) || !d.equals(getMinimumSize())) { - setPreferredSize(d); - setMinimumSize(d); - revalidate(); - } + // N.B.: (0,0) is the fin front-- where it meets the parent body. + final double xFinFront = finset.asPositionValue(AxialMethod.TOP); //<< in body frame + + // update to bound the parent body: + final double xParentFront = -xFinFront; + newBounds.update( xParentFront); + final double xParentBack = -xFinFront + parent.getLength(); + newBounds.update( xParentBack ); + final double yParentCenterline = -parent.getRadius(xFinFront); // from parent centerline to fin front. + newBounds.update( yParentCenterline ); + + // in 99% of fins, this bound is redundant, buuuuut just in case. + final double yParentMax = yParentCenterline + Math.max( parent.getForeRadius(), parent.getAftRadius()); + newBounds.update( yParentMax ); + + // update to bounds the fin points: + newBounds.update( finset.getFinPoints()); + + subjectBounds_m = newBounds.toRectangle(); } - - @Override - public void updateFigure() { - repaint(); - } - - + protected void updateCanvasOrigin() { + originLocation_px.width = borderThickness_px.width - (int)(subjectBounds_m.getX()*scale); + originLocation_px.height = borderThickness_px.height + (int)(subjectBounds_m.getY()*scale); + + } } diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java index c3a59d65d..8018d9c14 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java @@ -18,8 +18,12 @@ import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import net.sf.openrocket.gui.figureelements.FigureElement; import net.sf.openrocket.gui.rocketfigure.RocketComponentShape; +import net.sf.openrocket.gui.scalefigure.RocketPanel.VIEW_TYPE; import net.sf.openrocket.gui.util.ColorConversion; import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.motor.Motor; @@ -30,6 +34,7 @@ import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.startup.Application; +import net.sf.openrocket.util.BoundingBox; import net.sf.openrocket.util.BugException; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.LineStyle; @@ -46,6 +51,8 @@ import net.sf.openrocket.util.Transformation; */ @SuppressWarnings("serial") public class RocketFigure extends AbstractScaleFigure { + + private final static Logger log = LoggerFactory.getLogger(FinPointFigure.class); private static final String ROCKET_FIGURE_PACKAGE = "net.sf.openrocket.gui.rocketfigure"; private static final String ROCKET_FIGURE_SUFFIX = "Shapes"; @@ -61,28 +68,17 @@ public class RocketFigure extends AbstractScaleFigure { private Rocket rocket; private RocketComponent[] selection = new RocketComponent[0]; - private double figureWidth = 0, figureHeight = 0; - protected int figureWidthPx = 0, figureHeightPx = 0; private RocketPanel.VIEW_TYPE currentViewType = RocketPanel.VIEW_TYPE.SideView; private double rotation; - private Transformation transformation; - - private double translateX, translateY; - - - + private Transformation axialRotation; + /* * figureComponents contains the corresponding RocketComponents of the figureShapes */ private final ArrayList figureShapes = new ArrayList(); - - private double minX = 0, maxX = 0, maxR = 0; - // Figure width and height in SI-units and pixels - - private AffineTransform g2transformation = null; private final ArrayList relativeExtra = new ArrayList(); private final ArrayList absoluteExtra = new ArrayList(); @@ -96,27 +92,11 @@ public class RocketFigure extends AbstractScaleFigure { this.rocket = _rkt; this.rotation = 0.0; - this.transformation = Transformation.rotate_x(0.0); + this.axialRotation = Transformation.rotate_x(0.0); updateFigure(); } - @Override - public Dimension getOrigin() { - return new Dimension((int) translateX, (int) translateY); - } - - @Override - public double getFigureHeight() { - return figureHeight; - } - - @Override - public double getFigureWidth() { - return figureWidth; - } - - public RocketComponent[] getSelection() { return selection; } @@ -136,14 +116,14 @@ public class RocketFigure extends AbstractScaleFigure { } public Transformation getRotateTransformation() { - return transformation; + return axialRotation; } public void setRotation(double rot) { if (MathUtil.equals(rotation, rot)) return; this.rotation = rot; - this.transformation = Transformation.rotate_x(rotation); + this.axialRotation = Transformation.rotate_x(rotation); updateFigure(); } @@ -163,22 +143,6 @@ public class RocketFigure extends AbstractScaleFigure { } - /** - * Updates the figure shapes and figure size. - */ - @Override - public void updateFigure() { - figureShapes.clear(); - - calculateSize(); - - getShapeTree( this.figureShapes, rocket, this.transformation, Coordinate.ZERO); - - repaint(); - fireChangeEvent(); - } - - public void addRelativeExtra(FigureElement p) { relativeExtra.add(p); } @@ -219,49 +183,15 @@ public class RocketFigure extends AbstractScaleFigure { AffineTransform baseTransform = g2.getTransform(); - // Update figure shapes if necessary - if (figureShapes == null) - updateFigure(); + updateSubjectDimensions(); + updateCanvasOrigin(); + updateCanvasSize(); + updateTransform(); + + figureShapes.clear(); + updateShapeTree( this.figureShapes, rocket, this.axialRotation, Coordinate.ZERO); - - double tx, ty; - // Calculate translation for figure centering - if (figureWidthPx + 2 * borderPixelsWidth < getWidth()) { - - // Figure fits in the viewport - if (currentViewType == RocketPanel.VIEW_TYPE.BackView){ - tx = getWidth() / 2; - }else{ - tx = (getWidth() - figureWidthPx) / 2 - minX * scale; - } - } else { - - // Figure does not fit in viewport - if (currentViewType == RocketPanel.VIEW_TYPE.BackView){ - tx = borderPixelsWidth + figureWidthPx / 2; - }else{ - tx = borderPixelsWidth - minX * scale; - } - } - - ty = computeTy(figureHeightPx); - - if (Math.abs(translateX - tx) > 1 || Math.abs(translateY - ty) > 1) { - // Origin has changed, fire event - translateX = tx; - translateY = ty; - fireChangeEvent(); - } - - - // Calculate and store the transformation used - // (inverse is used in detecting clicks on objects) - g2transformation = new AffineTransform(); - g2transformation.translate(translateX, translateY); - // Mirror position Y-axis upwards - g2transformation.scale(scale / EXTRA_SCALE, -scale / EXTRA_SCALE); - - g2.transform(g2transformation); + g2.transform(projection); // Set rendering hints appropriately g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, @@ -378,22 +308,11 @@ public class RocketFigure extends AbstractScaleFigure { } - protected double computeTy(int heightPx) { - final double ty; - if (heightPx + 2 * borderPixelsHeight < getHeight()) { - ty = getHeight() / 2; - } else { - ty = borderPixelsHeight + heightPx / 2; - } - return ty; - } - - public RocketComponent[] getComponentsByPoint(double x, double y) { // Calculate point in shapes' coordinates Point2D.Double p = new Point2D.Double(x, y); try { - g2transformation.inverseTransform(p, p); + projection.inverseTransform(p, p); } catch (NoninvertibleTransformException e) { return new RocketComponent[0]; } @@ -408,52 +327,54 @@ public class RocketFigure extends AbstractScaleFigure { return l.toArray(new RocketComponent[0]); } - // NOTE: Recursive function - private void getShapeTree( - ArrayList allShapes, // output parameter - final RocketComponent comp, - final Transformation parentTransform, - final Coordinate parentLocation){ + // NOTE: Recursive function + private ArrayList updateShapeTree( + ArrayList allShapes, // output parameter + final RocketComponent comp, + final Transformation parentTransform, + final Coordinate parentLocation){ - - final int instanceCount = comp.getInstanceCount(); - Coordinate[] instanceLocations = comp.getInstanceLocations(); - instanceLocations = parentTransform.transform( instanceLocations ); - double[] instanceAngles = comp.getInstanceAngles(); - if( instanceLocations.length != instanceAngles.length ){ - throw new ArrayIndexOutOfBoundsException(String.format("lengths of location array (%d) and angle arrays (%d) differs! (in: %s) ", instanceLocations.length, instanceAngles.length, comp.getName())); - } - - // iterate over the aggregated instances *for the whole* tree. - for( int index = 0; instanceCount > index ; ++index ){ - final double currentAngle = instanceAngles[index]; - Transformation currentTransform = parentTransform; - if( 0.00001 < Math.abs( currentAngle )) { - Transformation currentAngleTransform = Transformation.rotate_x( currentAngle ); - currentTransform = currentAngleTransform.applyTransformation( parentTransform ); - } - - Coordinate currentLocation = parentLocation.add( instanceLocations[index] ); - -// System.err.println(String.format("@%s: %s -- inst: [%d/%d]", comp.getClass().getSimpleName(), comp.getName(), index+1, instanceCount)); -// System.err.println(String.format(" -- stage: %d, active: %b, config: (%d) %s", comp.getStageNumber(), this.getConfiguration().isComponentActive(comp), this.getConfiguration().instanceNumber, this.getConfiguration().getId())); -// System.err.println(String.format(" -- %s + %s = %s", parentLocation.toString(), instanceLocations[index].toString(), currentLocation.toString())); -// if( 0.00001 < Math.abs( currentAngle )) { -// System.err.println(String.format(" -- at: %6.4f radians", currentAngle)); -// } - - // generate shape for this component, if active - if( this.rocket.getSelectedConfiguration().isComponentActive( comp )){ - allShapes = addThisShape( allShapes, this.currentViewType, comp, currentLocation, currentTransform); - } - - // recurse into component's children - for( RocketComponent child: comp.getChildren() ){ - // draw a tree for each instance subcomponent - getShapeTree( allShapes, child, currentTransform, currentLocation ); - } - } + final int instanceCount = comp.getInstanceCount(); + Coordinate[] instanceLocations = comp.getInstanceLocations(); + instanceLocations = parentTransform.transform( instanceLocations ); + double[] instanceAngles = comp.getInstanceAngles(); + if( instanceLocations.length != instanceAngles.length ){ + throw new ArrayIndexOutOfBoundsException(String.format("lengths of location array (%d) and angle arrays (%d) differs! (in: %s) ", instanceLocations.length, instanceAngles.length, comp.getName())); + } + + // iterate over the aggregated instances *for the whole* tree. + for( int index = 0; instanceCount > index ; ++index ){ + final double currentAngle = instanceAngles[index]; + + Transformation currentTransform = parentTransform; + if( 0.00001 < Math.abs( currentAngle )) { + Transformation currentAngleTransform = Transformation.rotate_x( currentAngle ); + currentTransform = currentAngleTransform.applyTransformation( parentTransform ); + } + + Coordinate currentLocation = parentLocation.add( instanceLocations[index] ); + + // System.err.println(String.format("@%s: %s -- inst: [%d/%d]", comp.getClass().getSimpleName(), comp.getName(), index+1, instanceCount)); + // System.err.println(String.format(" -- stage: %d, active: %b, config: (%d) %s", comp.getStageNumber(), this.getConfiguration().isComponentActive(comp), this.getConfiguration().instanceNumber, this.getConfiguration().getId())); + // System.err.println(String.format(" -- %s + %s = %s", parentLocation.toString(), instanceLocations[index].toString(), currentLocation.toString())); + // if( 0.00001 < Math.abs( currentAngle )) { + // System.err.println(String.format(" -- at: %6.4f radians", currentAngle)); + // } + + // generate shape for this component, if active + if( this.rocket.getSelectedConfiguration().isComponentActive( comp )){ + allShapes = addThisShape( allShapes, this.currentViewType, comp, currentLocation, currentTransform); + } + + // recurse into component's children + for( RocketComponent child: comp.getChildren() ){ + // draw a tree for each instance subcomponent + updateShapeTree( allShapes, child, currentTransform, currentLocation ); + } + } + + return allShapes; } /** @@ -508,82 +429,50 @@ public class RocketFigure extends AbstractScaleFigure { - /** - * Gets the bounds of the figure, i.e. the maximum extents in the selected dimensions. - * The bounds are stored in the variables minX, maxX and maxR. - */ - private void calculateFigureBounds() { - Collection bounds = rocket.getSelectedConfiguration().getBounds(); - - if (bounds.isEmpty()) { - minX = 0; - maxX = 0; - maxR = 0; - return; - } - - minX = Double.MAX_VALUE; - maxX = Double.MIN_VALUE; - maxR = 0; - for (Coordinate c : bounds) { - double x = c.x, r = MathUtil.hypot(c.y, c.z); - if (x < minX) - minX = x; - if (x > maxX) - maxX = x; - if (r > maxR) - maxR = r; - } - } - -// public double getBestZoom(Rectangle2D bounds) { -// double zh = 1, zv = 1; -// if (bounds.getWidth() > 0.0001) -// zh = (getWidth() - 2 * borderPixelsWidth) / bounds.getWidth(); -// if (bounds.getHeight() > 0.0001) -// zv = (getHeight() - 2 * borderPixelsHeight) / bounds.getHeight(); -// return Math.min(zh, zv); -// } -// - - + /** + * Gets the bounds of the drawn subject in Model-Space + * + * i.e. the maximum extents in the selected dimensions. + * The bounds are stored in the variables minX, maxX and maxR. + * + * @return + */ + @Override + protected void updateSubjectDimensions() { + // calculate bounds, and store in class variables + final BoundingBox bounds = rocket.getSelectedConfiguration().getBoundingBox(); + + switch (currentViewType) { + case SideView: + subjectBounds_m = new Rectangle2D.Double(bounds.min.x, bounds.min.y, bounds.span().x, bounds.span().y); + break; + case BackView: + final double maxR = Math.max(Math.hypot(bounds.min.y, bounds.min.z), Math.hypot(bounds.max.y, bounds.max.z)); + subjectBounds_m = new Rectangle2D.Double(-maxR, -maxR, 2 * maxR, 2 * maxR); + break; + default: + throw new BugException("Illegal figure type = " + currentViewType); + } + } + /** * Calculates the necessary size of the figure and set the PreferredSize * property accordingly. */ - private void calculateSize() { - Rectangle2D dimensions = this.getDimensions(); - - figureHeight = dimensions.getHeight(); - figureWidth = dimensions.getWidth(); - - figureWidthPx = (int) (figureWidth * scale); - figureHeightPx = (int) (figureHeight * scale); - - Dimension dpx = new Dimension( - figureWidthPx + 2 * borderPixelsWidth, - figureHeightPx + 2 * borderPixelsHeight); - - if (!dpx.equals(getPreferredSize()) || !dpx.equals(getMinimumSize())) { - setPreferredSize(dpx); - setMinimumSize(dpx); - revalidate(); - } + @Override + protected void updateCanvasOrigin() { + + final Dimension subjectArea = new Dimension((int)(subjectBounds_m.getWidth()*scale), + (int)(subjectBounds_m.getHeight()*scale)); + + final int newOriginY = borderThickness_px.height + (int)(subjectArea.getHeight() / 2); + if (currentViewType == RocketPanel.VIEW_TYPE.BackView){ + int newOriginX = borderThickness_px.width + getWidth() / 2; + originLocation_px = new Dimension(newOriginX, newOriginY); + }else { + int newOriginX = borderThickness_px.width + (getWidth() - subjectArea.width) / 2; + originLocation_px = new Dimension(newOriginX, newOriginY); + } } - - public Rectangle2D getDimensions() { - calculateFigureBounds(); - - switch (currentViewType) { - case SideView: - return new Rectangle2D.Double(minX, -maxR, maxX - minX, 2 * maxR); - - case BackView: - return new Rectangle2D.Double(-maxR, -maxR, 2 * maxR, 2 * maxR); - - default: - throw new BugException("Illegal figure type = " + currentViewType); - } - } - + } diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/ScaleFigure.java deleted file mode 100644 index 48440fe33..000000000 --- a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleFigure.java +++ /dev/null @@ -1,82 +0,0 @@ -package net.sf.openrocket.gui.scalefigure; - -import java.awt.Dimension; - -import net.sf.openrocket.util.ChangeSource; - - -public interface ScaleFigure extends ChangeSource { - - /** - * Extra scaling applied to the figure. The f***ing Java JRE doesn't know - * how to draw shapes when using very large scaling factors, so this must - * be manually applied to every single shape used. - *

- * The scaling factor used is divided by this value, and every coordinate used - * in the figures must be multiplied by this factor. - */ - public static final double EXTRA_SCALE = 1000; - - /** - * Shorthand for {@link #EXTRA_SCALE}. - */ - public static final double S = EXTRA_SCALE; - - - /** - * Set the scale level of the figure. A scale value of 1.0 indicates an original - * size when using the current DPI level. - * - * @param scale the scale level. - */ - public void setScaling(double scale); - - - /** - * Set the scale level so that the figure fits into the given bounds. - * - * @param bounds the bounds of the figure. - */ - public void setScaling(Dimension bounds); - - - /** - * Return the scale level of the figure. A scale value of 1.0 indicates an original - * size when using the current DPI level. - * - * @return the current scale level. - */ - public double getScaling(); - - - /** - * Return the scale of the figure on px/m. - * - * @return the current scale value. - */ - public double getAbsoluteScale(); - - - /** - * Return the pixel coordinates of the figure origin. - * - * @return the pixel coordinates of the figure origin. - */ - public Dimension getOrigin(); - - - /** - * Get the amount of blank space left around the figure. - * - * @return the amount of horizontal and vertical space left on both sides of the figure. - */ - public Dimension getBorderPixels(); - - /** - * Set the amount of blank space left around the figure. - * - * @param width the amount of horizontal space left on both sides of the figure. - * @param height the amount of vertical space left on both sides of the figure. - */ - public void setBorderPixels(int width, int height); -} diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleScrollPane.java b/swing/src/net/sf/openrocket/gui/scalefigure/ScaleScrollPane.java index a45dd51a8..bc973938d 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleScrollPane.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/ScaleScrollPane.java @@ -17,7 +17,6 @@ import java.util.EventObject; import javax.swing.BorderFactory; import javax.swing.JComponent; -import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.ScrollPaneConstants; import javax.swing.event.ChangeEvent; @@ -29,6 +28,7 @@ import net.sf.openrocket.unit.Tick; import net.sf.openrocket.unit.Unit; import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.util.BugException; +import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.StateChangeListener; @@ -44,6 +44,7 @@ import net.sf.openrocket.util.StateChangeListener; * * @author Sampo Niskanen */ +@SuppressWarnings("serial") public class ScaleScrollPane extends JScrollPane implements MouseListener, MouseMotionListener { @@ -51,45 +52,33 @@ public class ScaleScrollPane extends JScrollPane public static final int MINOR_TICKS = 3; public static final int MAJOR_TICKS = 30; + public static final String USER_SCALE_PROPERTY = "UserScale"; - private JComponent component; - private ScaleFigure figure; + private final JComponent component; + private final AbstractScaleFigure figure; private DoubleModel rulerUnit; private Ruler horizontalRuler; private Ruler verticalRuler; - private final boolean allowFit; - + // is the subject *currently* being fitting private boolean fit = false; - - /** - * Create a scale scroll pane that allows fitting. - * - * @param component the component to contain (must implement ScaleFigure) - */ - public ScaleScrollPane(JComponent component) { - this(component, true); - } - /** * Create a scale scroll pane. * * @param component the component to contain (must implement ScaleFigure) * @param allowFit whether automatic fitting of the figure is allowed */ - public ScaleScrollPane(JComponent component, boolean allowFit) { + public ScaleScrollPane(final JComponent component) { super(component); - if (!(component instanceof ScaleFigure)) { + if (!(component instanceof AbstractScaleFigure)) { throw new IllegalArgumentException("component must implement ScaleFigure"); } this.component = component; - this.figure = (ScaleFigure) component; - this.allowFit = allowFit; - + this.figure = (AbstractScaleFigure) component; rulerUnit = new DoubleModel(0.0, UnitGroup.UNITS_LENGTH); rulerUnit.addChangeListener(new ChangeListener() { @@ -106,50 +95,45 @@ public class ScaleScrollPane extends JScrollPane UnitSelector selector = new UnitSelector(rulerUnit); selector.setFont(new Font("SansSerif", Font.PLAIN, 8)); this.setCorner(JScrollPane.UPPER_LEFT_CORNER, selector); - this.setCorner(JScrollPane.UPPER_RIGHT_CORNER, new JPanel()); - this.setCorner(JScrollPane.LOWER_LEFT_CORNER, new JPanel()); - this.setCorner(JScrollPane.LOWER_RIGHT_CORNER, new JPanel()); this.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY)); - + setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); + setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); + viewport.addMouseListener(this); viewport.addMouseMotionListener(this); figure.addChangeListener(new StateChangeListener() { @Override public void stateChanged(EventObject e) { - horizontalRuler.updateSize(); + horizontalRuler.updateSize(); verticalRuler.updateSize(); - if (fit) { - setFitting(true); - } + if(fit) { + figure.scaleTo(viewport.getExtentSize()); + } } }); viewport.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { - if (fit) { - setFitting(true); - } + if(fit) { + figure.scaleTo(viewport.getExtentSize()); + } + figure.updateFigure(); + + horizontalRuler.updateSize(); + verticalRuler.updateSize(); } }); } - public ScaleFigure getFigure() { + public AbstractScaleFigure getFigure() { return figure; } - - /** - * Return whether automatic fitting of the figure is allowed. - */ - public boolean isFittingAllowed() { - return allowFit; - } - /** * Return whether the figure is currently automatically fitted within the component bounds. */ @@ -159,54 +143,69 @@ public class ScaleScrollPane extends JScrollPane /** * Set whether the figure is automatically fitted within the component bounds. - * - * @throws BugException if automatic fitting is disallowed and fit is true */ - public void setFitting(boolean fit) { - if (fit && !allowFit) { - throw new BugException("Attempting to fit figure not allowing fit."); - } - this.fit = fit; - if (fit) { - setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); + public void setFitting(final boolean shouldFit) { + this.fit = shouldFit; + if (shouldFit) { validate(); - Dimension view = viewport.getExtentSize(); - figure.setScaling(view); - } else { - setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); - setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); + + Dimension view = viewport.getExtentSize(); + figure.scaleTo(view); + this.firePropertyChange( USER_SCALE_PROPERTY, 1.0, figure.getUserScale()); + + revalidate(); } } - - - public double getScaling() { - return figure.getScaling(); + public double getUserScale() { + return figure.getUserScale(); } - public double getScale() { - return figure.getAbsoluteScale(); - } - - public void setScaling(double scale) { - if (fit) { - setFitting(false); - } - figure.setScaling(scale); - horizontalRuler.repaint(); - verticalRuler.repaint(); + public void setScaling(final double newScale) { + // match if closer than 1%: + if( MathUtil.equals(newScale, figure.getUserScale(), 0.01)){ + return; + } + + // if explicitly setting a zoom level, turn off fitting + this.fit = false; + figure.scaleTo(newScale); + + revalidate(); } public Unit getCurrentUnit() { return rulerUnit.getCurrentUnit(); } - + + public String toViewportString(){ + Rectangle view = this.getViewport().getViewRect(); + return ("Viewport::("+view.getWidth()+","+view.getHeight()+")" + +"@("+view.getX()+", "+view.getY()+")"); + } + + @Override + public void revalidate() { + if( null != component ) { + component.revalidate(); + figure.updateFigure(); + } + + if( null != horizontalRuler ){ + horizontalRuler.revalidate(); + horizontalRuler.repaint(); + } + if( null != verticalRuler ){ + verticalRuler.revalidate(); + verticalRuler.repaint(); + } + + super.revalidate(); + } + //////////////// Mouse handlers //////////////// - - private int dragStartX = 0; private int dragStartY = 0; private Rectangle dragRectangle = null; @@ -288,27 +287,25 @@ public class ScaleScrollPane extends JScrollPane repaint(); } - private double fromPx(int px) { - Dimension origin = figure.getOrigin(); - if (orientation == HORIZONTAL) { - px -= origin.width; - } else { - // px = -(px - origin.height); - px -= origin.height; - } - return px / figure.getAbsoluteScale(); + private double fromPx(final int px) { + Dimension origin = figure.getSubjectOrigin(); + double realValue = Double.NaN; + if (orientation == HORIZONTAL) { + realValue = px - origin.width; + } else { + realValue = origin.height - px; + } + return realValue / figure.getAbsoluteScale(); } - private int toPx(double l) { - Dimension origin = figure.getOrigin(); - int px = (int) (l * figure.getAbsoluteScale() + 0.5); + private int toPx(final double value) { + final Dimension origin = figure.getSubjectOrigin(); + final int px = (int) (value * figure.getAbsoluteScale() + 0.5); if (orientation == HORIZONTAL) { - px += origin.width; + return (px + origin.width); } else { - px = px + origin.height; - // px += origin.height; + return (origin.height - px); } - return px; } @@ -322,8 +319,7 @@ public class ScaleScrollPane extends JScrollPane // Fill area with background color g2.setColor(getBackground()); g2.fillRect(area.x, area.y, area.width, area.height + 100); - - + int startpx, endpx; if (orientation == HORIZONTAL) { startpx = area.x; @@ -337,11 +333,19 @@ public class ScaleScrollPane extends JScrollPane double start, end, minor, major; start = fromPx(startpx); end = fromPx(endpx); + minor = MINOR_TICKS / figure.getAbsoluteScale(); major = MAJOR_TICKS / figure.getAbsoluteScale(); - - Tick[] ticks = unit.getTicks(start, end, minor, major); - + + Tick[] ticks = null; + if( VERTICAL == orientation ){ + // the parameters are *intended* to be backwards: because 'getTicks(...)' can only + // create increasing arrays (where the start < end) + ticks = unit.getTicks(end, start, minor, major); + }else if(HORIZONTAL == orientation ){ + // normal parameter order + ticks = unit.getTicks(start, end, minor, major); + } // Set color & hints g2.setColor(Color.BLACK); diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleSelector.java b/swing/src/net/sf/openrocket/gui/scalefigure/ScaleSelector.java index 7fd68c0d2..71cc55b03 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleSelector.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/ScaleSelector.java @@ -4,7 +4,6 @@ import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.text.DecimalFormat; -import java.util.Arrays; import java.util.EventObject; import java.util.Locale; @@ -19,6 +18,9 @@ import net.sf.openrocket.util.StateChangeListener; @SuppressWarnings("serial") public class ScaleSelector extends JPanel { + public static final double MINIMUM_ZOOM = 0.01; // == 1 % + public static final double MAXIMUM_ZOOM = 1000.00; // == 10,000 % + // Ready zoom settings private static final DecimalFormat PERCENT_FORMAT = new DecimalFormat("0.#%"); @@ -45,19 +47,16 @@ public class ScaleSelector extends JPanel { button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - double scale = scrollPane.getScaling(); - scale = getNextLargerScale(scale); - scrollPane.setScaling(scale); + final double oldScale = scrollPane.getUserScale(); + final double newScale = getNextLargerScale(oldScale); + scrollPane.setScaling(newScale); } }); add(button, "gap"); // Zoom level selector String[] settings = SCALE_LABELS; - if (!scrollPane.isFittingAllowed()) { - settings = Arrays.copyOf(settings, settings.length - 1); - } - + scaleSelector = new JComboBox<>(settings); scaleSelector.setEditable(true); setZoomText(); @@ -68,8 +67,7 @@ public class ScaleSelector extends JPanel { String text = (String) scaleSelector.getSelectedItem(); text = text.replaceAll("%", "").trim(); - if (text.toLowerCase(Locale.getDefault()).startsWith(SCALE_FIT.toLowerCase(Locale.getDefault())) && - scrollPane.isFittingAllowed()) { + if (text.toLowerCase(Locale.getDefault()).startsWith(SCALE_FIT.toLowerCase(Locale.getDefault()))){ scrollPane.setFitting(true); setZoomText(); return; @@ -101,7 +99,7 @@ public class ScaleSelector extends JPanel { button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - double scale = scrollPane.getScaling(); + double scale = scrollPane.getUserScale(); scale = getNextSmallerScale(scale); scrollPane.setScaling(scale); } @@ -111,7 +109,8 @@ public class ScaleSelector extends JPanel { } private void setZoomText() { - String text = PERCENT_FORMAT.format(scrollPane.getScaling()); + final double userScale = scrollPane.getUserScale(); + String text = PERCENT_FORMAT.format(userScale); if (scrollPane.isFitting()) { text = "Fit (" + text + ")"; } From 87b1f99a9b4a63f5cbcfebf95724e0d0754ed320 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Wed, 4 Jul 2018 15:40:06 -0400 Subject: [PATCH 08/47] [rm] excised EXTRA_SCALE (==S) factor in ScaleFigure Code --- .../gui/rocketfigure/FinSetShapes.java | 16 +++++----- .../gui/rocketfigure/MassComponentShapes.java | 5 ++- .../gui/rocketfigure/MassObjectShapes.java | 6 ++-- .../gui/rocketfigure/ParachuteShapes.java | 5 ++- .../gui/rocketfigure/RailButtonShapes.java | 32 +++++++++---------- .../rocketfigure/RocketComponentShape.java | 2 -- .../gui/rocketfigure/ShockCordShapes.java | 8 ++--- .../gui/rocketfigure/StreamerShapes.java | 12 +++---- .../SymmetricComponentShapes.java | 20 ++++-------- .../gui/rocketfigure/TransitionShapes.java | 26 +++++++-------- .../gui/rocketfigure/TubeFinSetShapes.java | 4 +-- .../gui/rocketfigure/TubeShapes.java | 10 +++--- .../gui/scalefigure/AbstractScaleFigure.java | 10 ------ .../gui/scalefigure/RocketFigure.java | 24 +++++++------- .../gui/scalefigure/RocketPanel.java | 4 +-- 15 files changed, 83 insertions(+), 101 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/FinSetShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/FinSetShapes.java index 7a0f2e335..6602e462b 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/FinSetShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/FinSetShapes.java @@ -34,9 +34,9 @@ public class FinSetShapes extends RocketComponentShape { Coordinate c = finSetFront.add(finPoints[i]); if (i==0) - p.moveTo(c.x*S, c.y*S); + p.moveTo(c.x, c.y); else - p.lineTo(c.x*S, c.y*S); + p.lineTo(c.x, c.y); } p.closePath(); @@ -87,13 +87,13 @@ public class FinSetShapes extends RocketComponentShape { Path2D.Double p = new Path2D.Double(); a = finFront.add( c[0] ); - p.moveTo(a.z*S, a.y*S); + p.moveTo(a.z, a.y); a = finFront.add( c[1] ); - p.lineTo(a.z*S, a.y*S); + p.lineTo(a.z, a.y); a = finFront.add( c[2] ); - p.lineTo(a.z*S, a.y*S); + p.lineTo(a.z, a.y); a = finFront.add( c[3] ); - p.lineTo(a.z*S, a.y*S); + p.lineTo(a.z, a.y); p.closePath(); return new Shape[]{p}; @@ -190,9 +190,9 @@ public class FinSetShapes extends RocketComponentShape { for (int i=0; i < array.length; i++) { Coordinate a = t.transform(compCenter.add( array[i]) ); if (i==0) - p.moveTo(a.z*S, a.y*S); + p.moveTo(a.z, a.y); else - p.lineTo(a.z*S, a.y*S); + p.lineTo(a.z, a.y); } p.closePath(); return p; diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/MassComponentShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/MassComponentShapes.java index 788872801..2742d959d 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/MassComponentShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/MassComponentShapes.java @@ -30,8 +30,7 @@ public class MassComponentShapes extends RocketComponentShape { Coordinate start = transformation.transform( componentAbsoluteLocation); Shape[] s = new Shape[1]; - s[0] = new RoundRectangle2D.Double(start.x*S,(start.y-radius)*S, - length*S,2*radius*S,arc*S,arc*S); + s[0] = new RoundRectangle2D.Double(start.x, (start.y-radius), length, 2*radius, arc, arc); switch (type) { case ALTIMETER: @@ -75,7 +74,7 @@ public class MassComponentShapes extends RocketComponentShape { Shape[] s = new Shape[start.length]; for (int i=0; i < start.length; i++) { - s[i] = new Ellipse2D.Double((start[i].z-or)*S,(start[i].y-or)*S,2*or*S,2*or*S); + s[i] = new Ellipse2D.Double((start[i].z-or),(start[i].y-or),2*or,2*or); } return RocketComponentShape.toArray(s, component); } diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/MassObjectShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/MassObjectShapes.java index 38a87437c..c66f66699 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/MassObjectShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/MassObjectShapes.java @@ -23,8 +23,8 @@ public class MassObjectShapes extends RocketComponentShape { Shape[] s = new Shape[start.length]; for (int i=0; i < start.length; i++) { - s[i] = new RoundRectangle2D.Double(start[i].x*S,(start[i].y-radius)*S, - length*S,2*radius*S,arc*S,arc*S); + s[i] = new RoundRectangle2D.Double(start[i].x,(start[i].y-radius), + length,2*radius,arc,arc); } return RocketComponentShape.toArray(s, component); @@ -44,7 +44,7 @@ public class MassObjectShapes extends RocketComponentShape { Shape[] s = new Shape[start.length]; for (int i=0; i < start.length; i++) { - s[i] = new Ellipse2D.Double((start[i].z-or)*S,(start[i].y-or)*S,2*or*S,2*or*S); + s[i] = new Ellipse2D.Double((start[i].z-or),(start[i].y-or),2*or,2*or); } return RocketComponentShape.toArray(s, component); } diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/ParachuteShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/ParachuteShapes.java index 49f88ee71..bbfbeb503 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/ParachuteShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/ParachuteShapes.java @@ -26,8 +26,7 @@ public class ParachuteShapes extends RocketComponentShape { Shape[] s = new Shape[start.length]; for (int i=0; i < start.length; i++) { - s[i] = new RoundRectangle2D.Double(start[i].x*S,(start[i].y-radius)*S, - length*S,2*radius*S,arc*S,arc*S); + s[i] = new RoundRectangle2D.Double(start[i].x, (start[i].y-radius), length, 2*radius, arc, arc); } return RocketComponentShape.toArray( addSymbol(s), component); } @@ -46,7 +45,7 @@ public class ParachuteShapes extends RocketComponentShape { Shape[] s = new Shape[start.length]; for (int i=0; i < start.length; i++) { - s[i] = new Ellipse2D.Double((start[i].z-or)*S,(start[i].y-or)*S,2*or*S,2*or*S); + s[i] = new Ellipse2D.Double((start[i].z-or),(start[i].y-or),2*or,2*or); } return RocketComponentShape.toArray( s, component); } diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/RailButtonShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/RailButtonShapes.java index 0222656e5..4abcc49bc 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/RailButtonShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/RailButtonShapes.java @@ -43,12 +43,12 @@ public class RailButtonShapes extends RocketComponentShape { final double drawHeight = outerDiameter*sinr; final Point2D.Double center = new Point2D.Double( instanceAbsoluteLocation.x, instanceAbsoluteLocation.y ); Point2D.Double lowerLeft = new Point2D.Double( center.x - outerRadius, center.y-outerRadius*sinr); - path.append( new Ellipse2D.Double( lowerLeft.x*S, lowerLeft.y*S, drawWidth*S, drawHeight*S), false); + path.append( new Ellipse2D.Double( lowerLeft.x, lowerLeft.y, drawWidth, drawHeight), false); - path.append( new Line2D.Double( lowerLeft.x*S, center.y*S, lowerLeft.x*S, (center.y+baseHeightcos)*S ), false); - path.append( new Line2D.Double( (center.x+outerRadius)*S, center.y*S, (center.x+outerRadius)*S, (center.y+baseHeightcos)*S ), false); + path.append( new Line2D.Double( lowerLeft.x, center.y, lowerLeft.x, (center.y+baseHeightcos) ), false); + path.append( new Line2D.Double( (center.x+outerRadius), center.y, (center.x+outerRadius), (center.y+baseHeightcos) ), false); - path.append( new Ellipse2D.Double( lowerLeft.x*S, (lowerLeft.y+baseHeightcos)*S, drawWidth*S, drawHeight*S), false); + path.append( new Ellipse2D.Double( lowerLeft.x, (lowerLeft.y+baseHeightcos), drawWidth, drawHeight), false); } {// inner @@ -56,24 +56,24 @@ public class RailButtonShapes extends RocketComponentShape { final double drawHeight = innerDiameter*sinr; final Point2D.Double center = new Point2D.Double( instanceAbsoluteLocation.x, instanceAbsoluteLocation.y + baseHeightcos); final Point2D.Double lowerLeft = new Point2D.Double( center.x - innerRadius, center.y-innerRadius*sinr); - path.append( new Ellipse2D.Double( lowerLeft.x*S, lowerLeft.y*S, drawWidth*S, drawHeight*S), false); + path.append( new Ellipse2D.Double( lowerLeft.x, lowerLeft.y, drawWidth, drawHeight), false); - path.append( new Line2D.Double( lowerLeft.x*S, center.y*S, lowerLeft.x*S, (center.y+innerHeightcos)*S ), false); - path.append( new Line2D.Double( (center.x+innerRadius)*S, center.y*S, (center.x+innerRadius)*S, (center.y+innerHeightcos)*S ), false); + path.append( new Line2D.Double( lowerLeft.x, center.y, lowerLeft.x, (center.y+innerHeightcos) ), false); + path.append( new Line2D.Double( (center.x+innerRadius), center.y, (center.x+innerRadius), (center.y+innerHeightcos) ), false); - path.append( new Ellipse2D.Double( lowerLeft.x*S, (lowerLeft.y+innerHeightcos)*S, drawWidth*S, drawHeight*S), false); + path.append( new Ellipse2D.Double( lowerLeft.x, (lowerLeft.y+innerHeightcos), drawWidth, drawHeight), false); } {// outer flange final double drawWidth = outerDiameter; final double drawHeight = outerDiameter*sinr; final Point2D.Double center = new Point2D.Double( instanceAbsoluteLocation.x, instanceAbsoluteLocation.y+baseHeightcos+innerHeightcos); final Point2D.Double lowerLeft = new Point2D.Double( center.x - outerRadius, center.y-outerRadius*sinr); - path.append( new Ellipse2D.Double( lowerLeft.x*S, lowerLeft.y*S, drawWidth*S, drawHeight*S), false); + path.append( new Ellipse2D.Double( lowerLeft.x, lowerLeft.y, drawWidth, drawHeight), false); - path.append( new Line2D.Double( lowerLeft.x*S, center.y*S, lowerLeft.x*S, (center.y+flangeHeightcos)*S ), false); - path.append( new Line2D.Double( (center.x+outerRadius)*S, center.y*S, (center.x+outerRadius)*S, (center.y+flangeHeightcos)*S ), false); + path.append( new Line2D.Double( lowerLeft.x, center.y, lowerLeft.x, (center.y+flangeHeightcos) ), false); + path.append( new Line2D.Double( (center.x+outerRadius), center.y, (center.x+outerRadius), (center.y+flangeHeightcos) ), false); - path.append( new Ellipse2D.Double( lowerLeft.x*S, (lowerLeft.y+flangeHeightcos)*S, drawWidth*S, drawHeight*S), false); + path.append( new Ellipse2D.Double( lowerLeft.x, (lowerLeft.y+flangeHeightcos), drawWidth, drawHeight), false); } return RocketComponentShape.toArray( new Shape[]{ path }, component ); @@ -131,10 +131,10 @@ public class RailButtonShapes extends RocketComponentShape { final double sinr = Math.sin(angle_rad); final double cosr = Math.cos(angle_rad); - rect.moveTo( (x-radius*cosr)*S, (y+radius*sinr)*S); - rect.lineTo( (x-radius*cosr+height*sinr)*S, (y+radius*sinr+height*cosr)*S); - rect.lineTo( (x+radius*cosr+height*sinr)*S, (y-radius*sinr+height*cosr)*S); - rect.lineTo( (x+radius*cosr)*S, (y-radius*sinr)*S); + rect.moveTo( (x-radius*cosr), (y+radius*sinr)); + rect.lineTo( (x-radius*cosr+height*sinr), (y+radius*sinr+height*cosr)); + rect.lineTo( (x+radius*cosr+height*sinr), (y-radius*sinr+height*cosr)); + rect.lineTo( (x+radius*cosr), (y-radius*sinr)); rect.closePath(); // add points diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java b/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java index d3c2fbd52..be1ccc916 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java @@ -16,8 +16,6 @@ import net.sf.openrocket.util.Transformation; */ public class RocketComponentShape { - protected static final double S = RocketFigure.EXTRA_SCALE; - final public boolean hasShape; final public Shape shape; final public net.sf.openrocket.util.Color color; diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/ShockCordShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/ShockCordShapes.java index 58396a4b1..7387399b5 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/ShockCordShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/ShockCordShapes.java @@ -25,8 +25,8 @@ public class ShockCordShapes extends RocketComponentShape { Coordinate start = transformation.transform( componentAbsoluteLocation); Shape[] s = new Shape[1]; - s[0] = new RoundRectangle2D.Double(start.x*S,(start.y-radius)*S, - length*S,2*radius*S,arc*S,arc*S); + s[0] = new RoundRectangle2D.Double(start.x,(start.y-radius), + length,2*radius,arc,arc); return RocketComponentShape.toArray( addSymbol(s), component); } @@ -43,13 +43,13 @@ public class ShockCordShapes extends RocketComponentShape { Shape[] s = new Shape[1]; Coordinate start = componentAbsoluteLocation; - s[0] = new Ellipse2D.Double((start.z-or)*S,(start.y-or)*S,2*or*S,2*or*S); + s[0] = new Ellipse2D.Double((start.z-or),(start.y-or),2*or,2*or); // Coordinate[] start = transformation.transform(tube.toAbsolute(instanceOffset)); // // Shape[] s = new Shape[start.length]; // for (int i=0; i < start.length; i++) { -// s[i] = new Ellipse2D.Double((start[i].z-or)*S,(start[i].y-or)*S,2*or*S,2*or*S); +// s[i] = new Ellipse2D.Double((start[i].z-or),(start[i].y-or),2*or,2*or); // } return RocketComponentShape.toArray( s, component); } diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/StreamerShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/StreamerShapes.java index 28bec20cc..480e8d958 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/StreamerShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/StreamerShapes.java @@ -24,14 +24,14 @@ public class StreamerShapes extends RocketComponentShape { Shape[] s = new Shape[1]; Coordinate frontCenter = componentAbsoluteLocation; - s[0] = new RoundRectangle2D.Double((frontCenter.x)*S,(frontCenter.y-radius)*S, - length*S,2*radius*S,arc*S,arc*S); + s[0] = new RoundRectangle2D.Double((frontCenter.x),(frontCenter.y-radius), + length,2*radius,arc,arc); // Coordinate[] start = transformation.transform(tube.toAbsolute(instanceOffset)); // Shape[] s = new Shape[start.length]; // for (int i=0; i < start.length; i++) { -// s[i] = new RoundRectangle2D.Double(start[i].x*S,(start[i].y-radius)*S, -// length*S,2*radius*S,arc*S,arc*S); +// s[i] = new RoundRectangle2D.Double(start[i].x,(start[i].y-radius), +// length,2*radius,arc,arc); // } return RocketComponentShape.toArray(addSymbol(s), component); } @@ -47,13 +47,13 @@ public class StreamerShapes extends RocketComponentShape { double or = tube.getRadius(); Shape[] s = new Shape[1]; Coordinate center = componentAbsoluteLocation; - s[0] = new Ellipse2D.Double((center.z-or)*S,(center.y-or)*S,2*or*S,2*or*S); + s[0] = new Ellipse2D.Double((center.z-or),(center.y-or),2*or,2*or); // Coordinate[] start = transformation.transform(tube.toAbsolute(instanceOffset)); // // Shape[] s = new Shape[start.length]; // for (int i=0; i < start.length; i++) { -// s[i] = new Ellipse2D.Double((start[i].z-or)*S,(start[i].y-or)*S,2*or*S,2*or*S); +// s[i] = new Ellipse2D.Double((start[i].z-or),(start[i].y-or),2*or,2*or); // } return RocketComponentShape.toArray(s, component); } diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/SymmetricComponentShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/SymmetricComponentShapes.java index c08fd49ac..e03a94b4d 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/SymmetricComponentShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/SymmetricComponentShapes.java @@ -1,5 +1,6 @@ package net.sf.openrocket.gui.rocketfigure; +import net.sf.openrocket.rocketcomponent.SymmetricComponent; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.Transformation; @@ -21,15 +22,8 @@ public class SymmetricComponentShapes extends RocketComponentShape { Transformation transformation, Coordinate componentAbsoluteLocation) { - return getShapesSide(component, transformation, componentAbsoluteLocation, S); - } - - public static RocketComponentShape[] getShapesSide( - net.sf.openrocket.rocketcomponent.RocketComponent component, - Transformation transformation, - Coordinate componentAbsoluteLocation, - final double scaleFactor) { - net.sf.openrocket.rocketcomponent.SymmetricComponent c = (net.sf.openrocket.rocketcomponent.SymmetricComponent) component; + SymmetricComponent c = (SymmetricComponent) component; + int i; final double delta = 0.0000001; @@ -89,14 +83,14 @@ public class SymmetricComponentShapes extends RocketComponentShape { // TODO: LOW: curved path instead of linear Path2D.Double path = new Path2D.Double(); - path.moveTo((nose.x + points.get(len - 1).x) * scaleFactor, (nose.y+points.get(len - 1).y) * scaleFactor); + path.moveTo((nose.x + points.get(len - 1).x) , (nose.y+points.get(len - 1).y) ); for (i = len - 2; i >= 0; i--) { - path.lineTo((nose.x+points.get(i).x)* scaleFactor, (nose.y+points.get(i).y) * scaleFactor); + path.lineTo((nose.x+points.get(i).x), (nose.y+points.get(i).y) ); } for (i = 0; i < len; i++) { - path.lineTo((nose.x+points.get(i).x) * scaleFactor, (nose.y-points.get(i).y) * scaleFactor); + path.lineTo((nose.x+points.get(i).x) , (nose.y-points.get(i).y) ); } - path.lineTo((nose.x+points.get(len - 1).x) * scaleFactor, (nose.y+points.get(len - 1).y) * scaleFactor); + path.lineTo((nose.x+points.get(len - 1).x) , (nose.y+points.get(len - 1).y) ); path.closePath(); //s[len] = path; diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/TransitionShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/TransitionShapes.java index 5a937ac79..e47db5fa2 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/TransitionShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/TransitionShapes.java @@ -8,7 +8,6 @@ import net.sf.openrocket.util.Transformation; import java.awt.*; import java.awt.geom.Ellipse2D; import java.awt.geom.Path2D; -import java.awt.geom.Rectangle2D; public class TransitionShapes extends RocketComponentShape { @@ -16,10 +15,10 @@ public class TransitionShapes extends RocketComponentShape { // TODO: LOW: Uses only first component of cluster (not currently clusterable). public static RocketComponentShape[] getShapesSide( - net.sf.openrocket.rocketcomponent.RocketComponent component, - Transformation transformation, - Coordinate instanceLocation) { - return getShapesSide(component, transformation, instanceLocation, S); + RocketComponent component, + Transformation transformation, + Coordinate instanceLocation) { + return getShapesSide(component, transformation, instanceLocation, 1.0); } public static RocketComponentShape[] getShapesSide( @@ -27,7 +26,8 @@ public class TransitionShapes extends RocketComponentShape { Transformation transformation, Coordinate instanceAbsoluteLocation, final double scaleFactor) { - + + Transition transition = (Transition)component; RocketComponentShape[] mainShapes; @@ -41,15 +41,15 @@ public class TransitionShapes extends RocketComponentShape { double r2 = transition.getAftRadius(); Path2D.Float path = new Path2D.Float(); - path.moveTo( (frontCenter.x)* scaleFactor, (frontCenter.y+ r1)* scaleFactor); - path.lineTo( (frontCenter.x+length)* scaleFactor, (frontCenter.y+r2)* scaleFactor); - path.lineTo( (frontCenter.x+length)* scaleFactor, (frontCenter.y-r2)* scaleFactor); - path.lineTo( (frontCenter.x)* scaleFactor, (frontCenter.y-r1)* scaleFactor); + path.moveTo( (frontCenter.x), (frontCenter.y+ r1)); + path.lineTo( (frontCenter.x+length), (frontCenter.y+r2)); + path.lineTo( (frontCenter.x+length), (frontCenter.y-r2)); + path.lineTo( (frontCenter.x), (frontCenter.y-r1)); path.closePath(); mainShapes = new RocketComponentShape[] { new RocketComponentShape( path, component) }; } else { - mainShapes = SymmetricComponentShapes.getShapesSide(component, transformation, instanceAbsoluteLocation, scaleFactor); + mainShapes = SymmetricComponentShapes.getShapesSide(component, transformation, instanceAbsoluteLocation); } Shape foreShoulder=null, aftShoulder=null; @@ -105,8 +105,8 @@ public class TransitionShapes extends RocketComponentShape { Coordinate center = componentAbsoluteLocation; Shape[] s = new Shape[2]; - s[0] = new Ellipse2D.Double((center.z-r1)*S,(center.y-r1)*S,2*r1*S,2*r1*S); - s[1] = new Ellipse2D.Double((center.z-r2)*S,(center.y-r2)*S,2*r2*S,2*r2*S); + s[0] = new Ellipse2D.Double((center.z-r1),(center.y-r1),2*r1,2*r1); + s[1] = new Ellipse2D.Double((center.z-r2),(center.y-r2),2*r2,2*r2); return RocketComponentShape.toArray(s, component); } diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/TubeFinSetShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/TubeFinSetShapes.java index 44a34bc05..5d6b56323 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/TubeFinSetShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/TubeFinSetShapes.java @@ -40,7 +40,7 @@ public class TubeFinSetShapes extends RocketComponentShape { Shape[] s = new Shape[fins]; for (int i=0; i - * The scaling factor used is divided by this value, and every coordinate used - * in the figures must be multiplied by this factor. - */ - public static final double EXTRA_SCALE = 1.0; public static final double INCHES_PER_METER = 39.3701; public static final double METERS_PER_INCH = 0.0254; diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java index 8018d9c14..d725503e9 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java @@ -229,16 +229,16 @@ public class RocketFigure extends AbstractScaleFigure { float[] dashes = style.getDashes(); for (int j = 0; j < dashes.length; j++) { - dashes[j] *= EXTRA_SCALE / scale; + dashes[j] *= 1.0 / scale; } if (selected) { - g2.setStroke(new BasicStroke((float) (SELECTED_WIDTH * EXTRA_SCALE / scale), + g2.setStroke(new BasicStroke((float) (SELECTED_WIDTH / scale), BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, dashes, 0)); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); } else { - g2.setStroke(new BasicStroke((float) (NORMAL_WIDTH * EXTRA_SCALE / scale), + g2.setStroke(new BasicStroke((float) (NORMAL_WIDTH / scale), BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, dashes, 0)); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE); @@ -246,7 +246,7 @@ public class RocketFigure extends AbstractScaleFigure { g2.draw(rcs.shape); } - g2.setStroke(new BasicStroke((float) (NORMAL_WIDTH * EXTRA_SCALE / scale), + g2.setStroke(new BasicStroke((float) (NORMAL_WIDTH / scale), BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE); @@ -276,13 +276,15 @@ public class RocketFigure extends AbstractScaleFigure { { Shape s; if (currentViewType == RocketPanel.VIEW_TYPE.SideView) { - s = new Rectangle2D.Double(EXTRA_SCALE * curMotorLocation.x, - EXTRA_SCALE * (curMotorLocation.y - motorRadius), EXTRA_SCALE * motorLength, - EXTRA_SCALE * 2 * motorRadius); + s = new Rectangle2D.Double( curMotorLocation.x, + (curMotorLocation.y - motorRadius), + motorLength, + 2 * motorRadius); } else { - s = new Ellipse2D.Double(EXTRA_SCALE * (curMotorLocation.z - motorRadius), - EXTRA_SCALE * (curMotorLocation.y - motorRadius), EXTRA_SCALE * 2 * motorRadius, - EXTRA_SCALE * 2 * motorRadius); + s = new Ellipse2D.Double((curMotorLocation.z - motorRadius), + (curMotorLocation.y - motorRadius), + 2 * motorRadius, + 2 * motorRadius); } g2.setColor(fillColor); g2.fill(s); @@ -295,7 +297,7 @@ public class RocketFigure extends AbstractScaleFigure { // Draw relative extras for (FigureElement e : relativeExtra) { - e.paint(g2, scale / EXTRA_SCALE); + e.paint(g2, scale); } // Draw absolute extras diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java index 6e1b0e3a2..8f0bd6208 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java @@ -641,8 +641,8 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change if (figure.getType() == RocketPanel.VIEW_TYPE.SideView && length > 0) { // TODO: LOW: Y-coordinate and rotation - extraCP.setPosition(cpx * RocketFigure.EXTRA_SCALE, 0); - extraCG.setPosition(cgx * RocketFigure.EXTRA_SCALE, 0); + extraCP.setPosition(cpx, 0); + extraCG.setPosition(cgx, 0); } else { From b63ea3b3cc6e5789e03588a4fb8f7000180f4f46 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Wed, 4 Jul 2018 17:15:25 -0400 Subject: [PATCH 09/47] [fix] FinPointFigure now auto-scales correctly - auto-zooms on startup - ScaleSelector Text updates with +/- buttons - adjusts fin-point drawing code --- .../configdialog/FreeformFinSetConfig.java | 4 +- .../gui/scalefigure/AbstractScaleFigure.java | 7 ++- .../gui/scalefigure/FinPointFigure.java | 48 ++++++++----------- .../gui/scalefigure/RocketFigure.java | 3 ++ .../gui/scalefigure/ScaleSelector.java | 2 + 5 files changed, 31 insertions(+), 33 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java index 2f8cf3f35..2cc516af2 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java @@ -254,7 +254,9 @@ public class FreeformFinSetConfig extends FinSetConfig { } }); ScaleSelector selector = new ScaleSelector(figurePane); - + // fit on first start-up + figurePane.setFitting(true); + panel.setLayout(new MigLayout("fill, gap 5!","", "[nogrid, fill, sizegroup display, growprio 200]5![sizegroup text, growprio 5]5![sizegroup buttons, align top, growprio 5]0!")); // first row: main display diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java index 0798b3d73..9e0802a7c 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java @@ -39,6 +39,7 @@ public abstract class AbstractScaleFigure extends JPanel { protected double scale = -1; protected static final Dimension borderThickness_px = new Dimension(DEFAULT_BORDER_PIXELS_WIDTH, DEFAULT_BORDER_PIXELS_HEIGHT); + // pixel offset from the the subject's origin to the canvas's upper-left-corner. protected Dimension originLocation_px = new Dimension(0,0); // ======= whatever this figure is drawing, in real-space coordinates: meters @@ -99,6 +100,8 @@ public abstract class AbstractScaleFigure extends JPanel { this.userScale = MathUtil.clamp( newScaleRequest, MINIMUM_ZOOM, MAXIMUM_ZOOM); this.scale = baseScale * userScale; + + this.fireChangeEvent(); } /** @@ -111,9 +114,6 @@ public abstract class AbstractScaleFigure extends JPanel { return; updateSubjectDimensions(); - updateCanvasOrigin(); - updateCanvasSize(); - updateTransform(); // dimensions within the viewable area, which are available to draw final int drawable_width_px = newBounds.width - 2 * borderThickness_px.width; @@ -147,7 +147,6 @@ public abstract class AbstractScaleFigure extends JPanel { setPreferredSize(preferredFigureSize_px); setMinimumSize(preferredFigureSize_px); - revalidate(); } protected void updateTransform(){ diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java index 1a275ae63..c3f7431cd 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java @@ -36,9 +36,6 @@ public class FinPointFigure extends AbstractScaleFigure { private final static Logger log = LoggerFactory.getLogger(FinPointFigure.class); - - private static final float MINIMUM_CANVAS_SIZE_METERS = 0.01f; // i.e. 1 cm - private static final Color GRID_LINE_COLOR = new Color( 137, 137, 137, 32); private static final float GRID_LINE_BASE_WIDTH = 0.001f; @@ -53,6 +50,8 @@ public class FinPointFigure extends AbstractScaleFigure { private final FreeformFinSet finset; private int modID = -1; + protected Rectangle2D finBounds_m = null; + protected Rectangle2D mountBounds_m = null; protected final List listeners = new LinkedList(); @@ -66,7 +65,7 @@ public class FinPointFigure extends AbstractScaleFigure { setBackground(Color.WHITE); setOpaque(true); - updateTransform(); + updateFigure(); } @Override @@ -315,40 +314,33 @@ public class FinPointFigure extends AbstractScaleFigure { @Override protected void updateSubjectDimensions(){ - // update subject bounds - BoundingBox newBounds = new BoundingBox(); + // update subject (i.e. Fin) bounds + finBounds_m = new BoundingBox().update(finset.getFinPoints()).toRectangle(); + + // NOTE: the fin's forward root is pinned at 0,0 + finBounds_m.setRect(0, 0, finBounds_m.getWidth(), finBounds_m.getHeight()); - // subsequent updates can only increase the size of the bounds, so this is the minimum size. - newBounds.update( MINIMUM_CANVAS_SIZE_METERS); - - SymmetricComponent parent = (SymmetricComponent)this.finset.getParent(); - - // N.B.: (0,0) is the fin front-- where it meets the parent body. + SymmetricComponent parent = (SymmetricComponent)this.finset.getParent(); + mountBounds_m = new BoundingBox().update(parent.getComponentBounds()).toRectangle(); + final double xFinFront = finset.asPositionValue(AxialMethod.TOP); //<< in body frame // update to bound the parent body: - final double xParentFront = -xFinFront; - newBounds.update( xParentFront); - final double xParentBack = -xFinFront + parent.getLength(); - newBounds.update( xParentBack ); - final double yParentCenterline = -parent.getRadius(xFinFront); // from parent centerline to fin front. - newBounds.update( yParentCenterline ); + final double xParent = -xFinFront; + final double yParent = -parent.getRadius(xFinFront); // from parent centerline to fin front. + final double rParent = Math.max(parent.getForeRadius(), parent.getAftRadius()); + mountBounds_m.setRect(xParent, yParent, mountBounds_m.getWidth(), rParent); - // in 99% of fins, this bound is redundant, buuuuut just in case. - final double yParentMax = yParentCenterline + Math.max( parent.getForeRadius(), parent.getAftRadius()); - newBounds.update( yParentMax ); - - // update to bounds the fin points: - newBounds.update( finset.getFinPoints()); - subjectBounds_m = newBounds.toRectangle(); + subjectBounds_m = new BoundingBox().update(finBounds_m).update(mountBounds_m).toRectangle(); } @Override protected void updateCanvasOrigin() { - originLocation_px.width = borderThickness_px.width - (int)(subjectBounds_m.getX()*scale); - originLocation_px.height = borderThickness_px.height + (int)(subjectBounds_m.getY()*scale); - + // the negative sign is to compensate for the mount's negative location. + originLocation_px.width = borderThickness_px.width - (int)(mountBounds_m.getX()*scale); + originLocation_px.height = borderThickness_px.height + (int)(subjectBounds_m.getHeight()*scale); } + } diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java index d725503e9..a21ee5300 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java @@ -108,6 +108,7 @@ public class RocketFigure extends AbstractScaleFigure { this.selection = selection; } updateFigure(); + fireChangeEvent(); } @@ -125,6 +126,7 @@ public class RocketFigure extends AbstractScaleFigure { this.rotation = rot; this.axialRotation = Transformation.rotate_x(rotation); updateFigure(); + fireChangeEvent(); } @@ -140,6 +142,7 @@ public class RocketFigure extends AbstractScaleFigure { return; this.currentViewType = type; updateFigure(); + fireChangeEvent(); } diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleSelector.java b/swing/src/net/sf/openrocket/gui/scalefigure/ScaleSelector.java index 71cc55b03..a156a544c 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleSelector.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/ScaleSelector.java @@ -50,6 +50,7 @@ public class ScaleSelector extends JPanel { final double oldScale = scrollPane.getUserScale(); final double newScale = getNextLargerScale(oldScale); scrollPane.setScaling(newScale); + setZoomText(); } }); add(button, "gap"); @@ -102,6 +103,7 @@ public class ScaleSelector extends JPanel { double scale = scrollPane.getUserScale(); scale = getNextSmallerScale(scale); scrollPane.setScaling(scale); + setZoomText(); } }); add(button, "gapleft rel"); From c3918ad2d4c9606239a7433b41c9a0a71d013814 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sat, 7 Jul 2018 14:44:41 -0400 Subject: [PATCH 10/47] [feat] FinPointFigure draws its parent/mounting half-body (w/front & back terminators) --- .../gui/scalefigure/FinPointFigure.java | 47 +++++++++++-------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java index c3f7431cd..db03dd446 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java @@ -187,15 +187,23 @@ public class FinPointFigure extends AbstractScaleFigure { } private void paintBodyTube( Graphics2D g2){ - Rectangle visible = g2.getClipBounds(); - int x0 = visible.x - 3; - int x1 = visible.x + visible.width + 4; + // in-figure left extent + final double xFore = mountBounds_m.getMinX(); + // in-figure right extent + final double xAft = mountBounds_m.getMaxX(); + // in-figure right extent + final double yCenter = mountBounds_m.getMinY(); + + Path2D.Double shape = new Path2D.Double(); + shape.moveTo( xFore, yCenter ); + shape.lineTo( xFore, 0); // body tube fore edge + shape.lineTo( xAft, 0); // body tube side + shape.lineTo( xAft, yCenter); // body tube aft edge final float bodyLineWidth = (float) ( LINE_WIDTH_PIXELS / scale ); g2.setStroke(new BasicStroke( bodyLineWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); g2.setColor(Color.BLACK); - - g2.drawLine((int) x0, 0, (int)x1, 0); + g2.draw(shape); } private void paintFinShape(final Graphics2D g2){ @@ -208,7 +216,7 @@ public class FinPointFigure extends AbstractScaleFigure { for (int i = 1; i < drawPoints.length; i++) { shape.lineTo( drawPoints[i].x, drawPoints[i].y); } - + final float finEdgeWidth_m = (float) (LINE_WIDTH_PIXELS / scale ); g2.setStroke(new BasicStroke( finEdgeWidth_m, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); g2.setColor(Color.BLUE); @@ -316,30 +324,31 @@ public class FinPointFigure extends AbstractScaleFigure { protected void updateSubjectDimensions(){ // update subject (i.e. Fin) bounds finBounds_m = new BoundingBox().update(finset.getFinPoints()).toRectangle(); - // NOTE: the fin's forward root is pinned at 0,0 finBounds_m.setRect(0, 0, finBounds_m.getWidth(), finBounds_m.getHeight()); - SymmetricComponent parent = (SymmetricComponent)this.finset.getParent(); - mountBounds_m = new BoundingBox().update(parent.getComponentBounds()).toRectangle(); - - final double xFinFront = finset.asPositionValue(AxialMethod.TOP); //<< in body frame - // update to bound the parent body: - final double xParent = -xFinFront; - final double yParent = -parent.getRadius(xFinFront); // from parent centerline to fin front. + SymmetricComponent parent = (SymmetricComponent)this.finset.getParent(); + final double xParent = - finset.asPositionValue(AxialMethod.TOP); //<< in body frame + final double yParent = -parent.getRadius(xParent); // from parent centerline to fin front. final double rParent = Math.max(parent.getForeRadius(), parent.getAftRadius()); - mountBounds_m.setRect(xParent, yParent, mountBounds_m.getWidth(), rParent); - - - subjectBounds_m = new BoundingBox().update(finBounds_m).update(mountBounds_m).toRectangle(); + mountBounds_m = new Rectangle2D.Double( xParent, yParent, parent.getLength(), rParent); + + final double subjectWidth = Math.max( finBounds_m.getWidth(), parent.getLength()); + final double subjectHeight = Math.max( 2*rParent, rParent + finBounds_m.getHeight()); + subjectBounds_m = new Rectangle2D.Double( xParent, yParent, subjectWidth, subjectHeight); } @Override protected void updateCanvasOrigin() { + final SymmetricComponent parent = (SymmetricComponent)this.finset.getParent(); + final double rMaxParent = Math.max(parent.getForeRadius(), parent.getAftRadius()); + // the negative sign is to compensate for the mount's negative location. originLocation_px.width = borderThickness_px.width - (int)(mountBounds_m.getX()*scale); - originLocation_px.height = borderThickness_px.height + (int)(subjectBounds_m.getHeight()*scale); + originLocation_px.height = borderThickness_px.height + (int)(Math.max( rMaxParent, finBounds_m.getHeight())*scale); + + System.err.println(String.format("________ Origin Location (px): w=%d, h=%d: ", originLocation_px.width, originLocation_px.height)); } From 80c0fa85680712e970d60d226d179c69564088bf Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sat, 7 Jul 2018 15:26:49 -0400 Subject: [PATCH 11/47] [fixes #425] FinPointFigure ScrollBars now adjust with zoom in/out --- .../gui/scalefigure/ScaleScrollPane.java | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleScrollPane.java b/swing/src/net/sf/openrocket/gui/scalefigure/ScaleScrollPane.java index bc973938d..7856083e6 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleScrollPane.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/ScaleScrollPane.java @@ -265,23 +265,23 @@ public class ScaleScrollPane extends JScrollPane public Ruler(int orientation) { this.orientation = orientation; - updateSize(); rulerUnit.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { + updateSize(); Ruler.this.repaint(); } }); } - - public void updateSize() { - Dimension d = component.getPreferredSize(); + private void updateSize() { if (orientation == HORIZONTAL) { - setPreferredSize(new Dimension(d.width + 10, RULER_SIZE)); + Ruler.this.setMinimumSize(new Dimension(component.getWidth() + 10, RULER_SIZE)); + Ruler.this.setPreferredSize(new Dimension(component.getWidth() + 10, RULER_SIZE)); } else { - setPreferredSize(new Dimension(RULER_SIZE, d.height + 10)); + Ruler.this.setMinimumSize(new Dimension(RULER_SIZE, component.getHeight() + 10)); + Ruler.this.setPreferredSize(new Dimension(RULER_SIZE, component.getHeight() + 10)); } revalidate(); repaint(); @@ -314,8 +314,13 @@ public class ScaleScrollPane extends JScrollPane super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; - Rectangle area = g2.getClipBounds(); + updateSize(); + // this function doesn't reliably update all the time, so we'll draw everything for the entire canvas, + // and let the JVM drawing algorithms figure out what should be drawn. + // + Rectangle area = ScaleScrollPane.this.getViewport().getViewRect(); + // Fill area with background color g2.setColor(getBackground()); g2.fillRect(area.x, area.y, area.width, area.height + 100); @@ -329,14 +334,13 @@ public class ScaleScrollPane extends JScrollPane endpx = area.y + area.height; } - Unit unit = rulerUnit.getCurrentUnit(); - double start, end, minor, major; - start = fromPx(startpx); - end = fromPx(endpx); + final double start = fromPx(startpx); + final double end = fromPx(endpx); - minor = MINOR_TICKS / figure.getAbsoluteScale(); - major = MAJOR_TICKS / figure.getAbsoluteScale(); + final double minor = MINOR_TICKS / figure.getAbsoluteScale(); + final double major = MAJOR_TICKS / figure.getAbsoluteScale(); + Unit unit = rulerUnit.getCurrentUnit(); Tick[] ticks = null; if( VERTICAL == orientation ){ // the parameters are *intended* to be backwards: because 'getTicks(...)' can only From fc43b19db0bb4b6b18845018d2542fdb6171bfc9 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sun, 8 Jul 2018 18:40:49 -0400 Subject: [PATCH 12/47] [fix] clicking away from points now longer causes an exception --- .../openrocket/rocketcomponent/FreeformFinSet.java | 3 ++- .../gui/configdialog/FreeformFinSetConfig.java | 8 ++++++-- .../openrocket/gui/scalefigure/FinPointFigure.java | 12 +++++++----- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/core/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java b/core/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java index 8924eb3ba..468cca53a 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java @@ -215,7 +215,8 @@ public class FreeformFinSet extends FinSet { y0 = Double.NaN; x1 = points.get(1).x; y1 = points.get(1).y; - +// } else if ( (0 > index) || (points.size() <= index) ){ +// throw new IllegalFinPointException("Point Index not available!"); } else if (index == points.size() - 1) { // Restrict point diff --git a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java index 2cc516af2..d288935e9 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java @@ -381,13 +381,15 @@ public class FreeformFinSetConfig extends FinSetConfig { return; } - int pointIndex = getPoint(event); + final int pointIndex = getPoint(event); + if ( pointIndex >= 0) { dragIndex = pointIndex; return; } - int segmentIndex = getSegment(event); + final int segmentIndex = getSegment(event); + System.err.println(String.format(".... finpoint//segmentIndex: %d", segmentIndex)); if (segmentIndex >= 0) { Point2D.Double point = getCoordinates(event); finset.addPoint(segmentIndex ); @@ -396,6 +398,8 @@ public class FreeformFinSetConfig extends FinSetConfig { finset.setPoint(dragIndex, point.x, point.y); } catch (IllegalFinPointException ignore) { // no-op + } catch (ArrayIndexOutOfBoundsException ex) { + log.error("bad index while editing fin points!!", ex); } dragIndex = segmentIndex; diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java index db03dd446..1eebd5b67 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java @@ -274,10 +274,10 @@ public class FinPointFigure extends AbstractScaleFigure { double x0 = p.x; double y0 = p.y; - double delta = BOX_WIDTH_PIXELS /*/ scale*/; + double delta = BOX_WIDTH_PIXELS / scale; - //System.out.println("Point: " + x0 + "," + y0); - //System.out.println("delta: " + (BOX_SIZE / scale)); + //System.err.println(String.format("__Point: x=%.4f, y=%.4f", x0, y0)); + //System.err.println(String.format("__delta: %.4f", BOX_WIDTH_PIXELS / scale)); Coordinate[] points = finset.getFinPoints(); for (int i = 1; i < points.length; i++) { @@ -286,11 +286,13 @@ public class FinPointFigure extends AbstractScaleFigure { double x2 = points[i].x; double y2 = points[i].y; - // System.out.println("point1:"+x1+","+y1+" point2:"+x2+","+y2); + //System.out.println("point1:"+x1+","+y1+" point2:"+x2+","+y2); double u = Math.abs((x2 - x1) * (y1 - y0) - (x1 - x0) * (y2 - y1)) / MathUtil.hypot(x2 - x1, y2 - y1); - //System.out.println("Distance of segment " + i + " is " + u); + + //System.err.println("Distance of segment " + i + " is " + u); + if (u < delta) return i; } From 165f005b1232e3788fc7a4aeb06abf43ca4b20e4 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Mon, 9 Jul 2018 00:52:10 -0400 Subject: [PATCH 13/47] Version Bump to Alpha 8 --- core/resources/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/resources/build.properties b/core/resources/build.properties index 6b753a3ae..806d5a7ec 100644 --- a/core/resources/build.properties +++ b/core/resources/build.properties @@ -1,7 +1,7 @@ # The OpenRocket build version -build.version=alpha5 +build.version=alpha8 # The source of the package. When building a package for a specific From 040c2d0091c6f9e629d7a1f65cf044b61fb3ae5e Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Tue, 10 Jul 2018 13:37:50 -0400 Subject: [PATCH 14/47] [fixes #424] Addes back in ConfigDialog outside spacing. --- .../sf/openrocket/gui/configdialog/RocketComponentConfig.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java index 6321ed099..69dd02966 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java @@ -77,7 +77,7 @@ public class RocketComponentConfig extends JPanel { public RocketComponentConfig(OpenRocketDocument document, RocketComponent component) { - setLayout(new MigLayout("fill, gap 5!", "[]:5[]", "[growprio 10]10![fill, grow, growprio 500]10![growprio 10]")); + setLayout(new MigLayout("fill, gap 5!, ins panel", "[]:5[]", "[growprio 10]10![fill, grow, growprio 500]10![growprio 10]")); this.document = document; this.component = component; @@ -154,7 +154,7 @@ public class RocketComponentConfig extends JPanel { updateFields(); - this.add(buttonPanel, "dock south, spanx, growx, height 50!"); + this.add(buttonPanel, "newline, spanx, growx, height 50!"); } From eee24233cb2889a9ddb1a88d90945b0c27ffc816 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sun, 15 Jul 2018 13:44:34 +0100 Subject: [PATCH 15/47] [fixes #419] Clicking in fin-point figure now calculates closest segment correctly --- .../gui/scalefigure/FinPointFigure.java | 71 ++++++++++--------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java index 1eebd5b67..9edc176cb 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java @@ -241,18 +241,25 @@ public class FinPointFigure extends AbstractScaleFigure { } } - public int getIndexByPoint(double x, double y) { - if (finPointHandles == null) - return -1; - + public Point2D.Double getPoint( final int x, final int y){ + if (finPointHandles == null) + return null; + // Calculate point in shapes' coordinates Point2D.Double p = new Point2D.Double(x, y); try { projection.inverseTransform(p, p); + return p; } catch (NoninvertibleTransformException e) { - return -1; + return null; } - + } + + public int getIndexByPoint(final int x, final int y) { + final Point2D.Double p = getPoint(x,y); + if (p == null) + return -1; + for (int i = 0; i < finPointHandles.length; i++) { if (finPointHandles[i].contains(p)) return i; @@ -260,24 +267,12 @@ public class FinPointFigure extends AbstractScaleFigure { return -1; } - public int getSegmentByPoint(double x, double y) { - if (finPointHandles == null) - return -1; - - // Calculate point in shapes' coordinates - Point2D.Double p = new Point2D.Double(x, y); - try { - projection.inverseTransform(p, p); - } catch (NoninvertibleTransformException e) { - return -1; - } - - double x0 = p.x; - double y0 = p.y; - double delta = BOX_WIDTH_PIXELS / scale; - - //System.err.println(String.format("__Point: x=%.4f, y=%.4f", x0, y0)); - //System.err.println(String.format("__delta: %.4f", BOX_WIDTH_PIXELS / scale)); + public int getSegmentByPoint(final int x, final int y) { + final Point2D.Double p = getPoint(x,y); + if (p == null) + return -1; + + final double threshold = BOX_WIDTH_PIXELS / scale; Coordinate[] points = finset.getFinPoints(); for (int i = 1; i < points.length; i++) { @@ -286,22 +281,28 @@ public class FinPointFigure extends AbstractScaleFigure { double x2 = points[i].x; double y2 = points[i].y; - //System.out.println("point1:"+x1+","+y1+" point2:"+x2+","+y2); - - double u = Math.abs((x2 - x1) * (y1 - y0) - (x1 - x0) * (y2 - y1)) / - MathUtil.hypot(x2 - x1, y2 - y1); - - //System.err.println("Distance of segment " + i + " is " + u); - - if (u < delta) - return i; + final double segmentLength = MathUtil.hypot(x2 - x1, y2 - y1); + + // Distance to an infinite line, defined by two points: + // (For a more in-depth explanation, see wikipedia: https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line ) + double x0 = p.x; + double y0 = p.y; + final double distanceToLine = Math.abs((y2 - y1)*x0 - (x2-x1)*y0 + x2*y1 - y2*x1)/segmentLength; + + final double distanceToStart = MathUtil.hypot(x1-x0, y1-y0); + final double distanceToEnd = MathUtil.hypot(x2-x0, y2-y0); + final boolean withinSegment = (distanceToStart < segmentLength && distanceToEnd < segmentLength); + + if ( distanceToLine < threshold && withinSegment){ + return i; + } + } return -1; } - - public Point2D.Double convertPoint(double x, double y) { + public Point2D.Double convertPoint(final double x, final double y) { Point2D.Double p = new Point2D.Double(x, y); try { projection.inverseTransform(p, p); From 1418cb902bac334d66d7a2a80331a036a65d29bf Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sun, 15 Jul 2018 19:20:03 +0100 Subject: [PATCH 16/47] [fixes #431] Fins default to instance count / fin count == 1 - Fixed init bug - added unittests for fin count loading/saving/creation --- .../net/sf/openrocket/rocketcomponent/FinSet.java | 2 +- .../openrocket/rocketcomponent/TrapezoidFinSet.java | 2 +- .../sf/openrocket/rocketcomponent/FinSetTest.java | 12 ++++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java index 186f80dcc..aae3edcb9 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java @@ -74,7 +74,7 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab /** * Number of fins. */ - protected int fins = 3; + protected int fins = 1; /** * Rotation about the x-axis by 2*PI/fins. diff --git a/core/src/net/sf/openrocket/rocketcomponent/TrapezoidFinSet.java b/core/src/net/sf/openrocket/rocketcomponent/TrapezoidFinSet.java index d8e7f5d57..5ee55d9b5 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/TrapezoidFinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/TrapezoidFinSet.java @@ -39,7 +39,7 @@ public class TrapezoidFinSet extends FinSet { public TrapezoidFinSet() { - this(3, 0.05, 0.05, 0.025, 0.03); + this(1, 0.05, 0.05, 0.025, 0.03); } // TODO: HIGH: height=0 -> CP = NaN diff --git a/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java b/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java index f72436d05..a4c3c3ba9 100644 --- a/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java +++ b/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java @@ -22,6 +22,18 @@ import net.sf.openrocket.util.LineStyle; import net.sf.openrocket.util.BaseTestCase.BaseTestCase; public class FinSetTest extends BaseTestCase { + + @Test + public void testMultiplicity() { + final TrapezoidFinSet trapFins = new TrapezoidFinSet(); + assertEquals(1, trapFins.getFinCount()); + + final FreeformFinSet fffins = new FreeformFinSet(); + assertEquals(1, fffins.getFinCount()); + + final EllipticalFinSet efins = new EllipticalFinSet(); + assertEquals(1, efins.getFinCount()); + } @Test public void testTrapezoidCGComputation() { From bea26fc511d81d6e3435e44b182568de743f5d68 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sat, 18 Aug 2018 10:36:07 +0200 Subject: [PATCH 17/47] [refactor] renamed FinSet#fins => FinSet#finCount to make it's meaning more explicit --- .../sf/openrocket/rocketcomponent/FinSet.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java index aae3edcb9..96c193855 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java @@ -74,7 +74,7 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab /** * Number of fins. */ - protected int fins = 1; + private int finCount = 1; /** * Rotation about the x-axis by 2*PI/fins. @@ -155,7 +155,7 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab * @return The number of fins. */ public int getFinCount() { - return fins; + return finCount; } /** @@ -163,13 +163,13 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab * @param n The number of fins, greater of equal to one. */ public void setFinCount(int n) { - if (fins == n) + if (finCount == n) return; if (n < 1) n = 1; if (n > 8) n = 8; - fins = n; + finCount = n; fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } @@ -400,7 +400,7 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab @Override public double getComponentVolume() { // this is for the fins alone, fillets are taken care of separately. - return fins * (getFinArea() + tabHeight * tabLength) * thickness * + return finCount * (getFinArea() + tabHeight * tabLength) * thickness * crossSection.getRelativeVolume(); } @@ -414,7 +414,7 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab double mass = getFinMass(); double filletMass = getFilletMass(); - if (fins == 1) { + if (finCount == 1) { Transformation rotation = Transformation.rotate_x( getAngleOffset()); return rotation.transform( new Coordinate(finCGx, finCGy + getBodyRadius(), 0, (filletMass + mass))); @@ -542,12 +542,12 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab double inertia = (h2 + 2 * w2) / 24; - if (fins == 1) + if (finCount == 1) return inertia; double radius = getBodyRadius(); - return fins * (inertia + MathUtil.pow2(MathUtil.safeSqrt(h2) + radius)); + return finCount * (inertia + MathUtil.pow2(MathUtil.safeSqrt(h2) + radius)); } @@ -577,12 +577,12 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab h = MathUtil.safeSqrt(h * area / w); } - if (fins == 1) + if (finCount == 1) return h * h / 12; double radius = getBodyRadius(); - return fins * (h * h / 12 + MathUtil.pow2(h / 2 + radius)); + return finCount * (h * h / 12 + MathUtil.pow2(h / 2 + radius)); } @@ -823,7 +823,7 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab @Override protected List copyFrom(RocketComponent c) { FinSet src = (FinSet) c; - this.fins = src.fins; + this.finCount = src.finCount; this.finRotation = src.finRotation; this.firstFinOffset = src.firstFinOffset; this.cantAngle = src.cantAngle; From 4a91ecd63a99966e12f3e4236fca22072f0b66a3 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Tue, 10 Jul 2018 14:15:43 -0400 Subject: [PATCH 18/47] [feature][Resolves #426] implemented FinPoint SelectedIndex Indicators - figure and table update each other --- .../configdialog/FreeformFinSetConfig.java | 81 ++++++++++++------- .../gui/scalefigure/FinPointFigure.java | 40 ++++++--- 2 files changed, 81 insertions(+), 40 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java index d288935e9..8949883c1 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java @@ -3,6 +3,7 @@ package net.sf.openrocket.gui.configdialog; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.geom.Point2D; import java.io.BufferedWriter; @@ -71,6 +72,8 @@ public class FreeformFinSetConfig extends FinSetConfig { private JTable table = null; private FinPointTableModel tableModel = null; + private int dragIndex = -1; + private FinPointFigure figure = null; @@ -214,6 +217,14 @@ public class FreeformFinSetConfig extends FinSetConfig { for (int i = 0; i < Columns.values().length; i++) { table.getColumnModel().getColumn(i).setPreferredWidth(Columns.values()[i].getWidth()); } + table.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent ev) { + figure.setSelectedIndex(table.getSelectedRow()); + figure.updateFigure(); + } + + }); JScrollPane tablePane = new JScrollPane(table); JButton scaleButton = new JButton(trans.get("FreeformFinSetConfig.lbl.scaleFin")); @@ -340,8 +351,7 @@ public class FreeformFinSetConfig extends FinSetConfig { } finally { document.stopUndo(); } - } - + } } @@ -351,22 +361,33 @@ public class FreeformFinSetConfig extends FinSetConfig { if (tableModel != null) { tableModel.fireTableDataChanged(); + + // make sure to do this *after* the table data is updated. + if( 0 <= this.dragIndex ) { + table.setRowSelectionInterval(dragIndex, dragIndex); + }else { + table.clearSelection(); + } } + if (figure != null) { - figure.updateFigure(); + if( 0 <= this.dragIndex ) { + figure.setSelectedIndex(dragIndex); + }else{ + figure.resetSelectedIndex(); + } + figure.updateFigure(); } revalidate(); repaint(); } - - private class FinPointScrollPane extends ScaleScrollPane { private static final int ANY_MASK = (MouseEvent.ALT_DOWN_MASK | MouseEvent.ALT_GRAPH_DOWN_MASK | MouseEvent.META_DOWN_MASK | MouseEvent.CTRL_DOWN_MASK | MouseEvent.SHIFT_DOWN_MASK); - private int dragIndex = -1; + private FinPointScrollPane( final FinPointFigure _figure) { super( _figure); @@ -381,30 +402,30 @@ public class FreeformFinSetConfig extends FinSetConfig { return; } - final int pointIndex = getPoint(event); - - if ( pointIndex >= 0) { - dragIndex = pointIndex; + final int pressIndex = getPoint(event); + if ( pressIndex >= 0) { + dragIndex = pressIndex; + updateFields(); return; } final int segmentIndex = getSegment(event); - System.err.println(String.format(".... finpoint//segmentIndex: %d", segmentIndex)); if (segmentIndex >= 0) { Point2D.Double point = getCoordinates(event); finset.addPoint(segmentIndex ); try { finset.setPoint(dragIndex, point.x, point.y); + dragIndex = segmentIndex; + updateFields(); + return; } catch (IllegalFinPointException ignore) { // no-op } catch (ArrayIndexOutOfBoundsException ex) { log.error("bad index while editing fin points!!", ex); } - dragIndex = segmentIndex; updateFields(); - return; } @@ -415,7 +436,7 @@ public class FreeformFinSetConfig extends FinSetConfig { @Override public void mouseDragged(MouseEvent event) { int mods = event.getModifiersEx(); - if (dragIndex < 0 || (mods & (ANY_MASK | MouseEvent.BUTTON1_DOWN_MASK)) != MouseEvent.BUTTON1_DOWN_MASK) { + if (dragIndex <= 0 || (mods & (ANY_MASK | MouseEvent.BUTTON1_DOWN_MASK)) != MouseEvent.BUTTON1_DOWN_MASK) { super.mouseDragged(event); return; } @@ -439,22 +460,22 @@ public class FreeformFinSetConfig extends FinSetConfig { @Override public void mouseClicked(MouseEvent event) { - int mods = event.getModifiersEx(); - if (event.getButton() != MouseEvent.BUTTON1 || (mods & ANY_MASK) != MouseEvent.CTRL_DOWN_MASK) { - super.mouseClicked(event); - return; - } - - int index = getPoint(event); - if (index < 0) { - super.mouseClicked(event); - return; - } - - try { - finset.removePoint(index); - } catch (IllegalFinPointException ignore) { - } + int mods = event.getModifiersEx(); + if (event.getButton() != MouseEvent.BUTTON1 || (mods & ANY_MASK) != MouseEvent.CTRL_DOWN_MASK) { + super.mouseClicked(event); + return; + } + + int index = getPoint(event); + if (index < 0) { + super.mouseClicked(event); + return; + } + + try { + finset.removePoint(index); + } catch (IllegalFinPointException ignore) { + } } diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java index 9edc176cb..0bf8a416d 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java @@ -7,7 +7,6 @@ import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; -import java.awt.geom.AffineTransform; import java.awt.geom.Line2D; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Path2D; @@ -43,6 +42,7 @@ public class FinPointFigure extends AbstractScaleFigure { // the size of the boxes around each fin point vertex private static final float BOX_WIDTH_PIXELS = 12; + private static final float SELECTED_BOX_WIDTH_PIXELS = 16; private static final double MINOR_TICKS = 0.05; private static final double MAJOR_TICKS = 0.1; @@ -56,7 +56,7 @@ public class FinPointFigure extends AbstractScaleFigure { protected final List listeners = new LinkedList(); private Rectangle2D.Double[] finPointHandles = null; - + private int selectedIndex = -1; public FinPointFigure(FreeformFinSet finset) { this.finset = finset; @@ -229,19 +229,30 @@ public class FinPointFigure extends AbstractScaleFigure { // Fin point boxes final float boxWidth = (float) (BOX_WIDTH_PIXELS / scale ); + final float boxHalfWidth = boxWidth/2; + final float selBoxWidth = (float) (SELECTED_BOX_WIDTH_PIXELS / scale ); + final float selBoxHalfWidth = boxWidth/2; + final float boxEdgeWidth_m = (float) ( LINE_WIDTH_PIXELS / scale ); g2.setStroke(new BasicStroke( boxEdgeWidth_m, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); g2.setColor(new Color(150, 0, 0)); - final double boxHalfWidth = boxWidth/2; + finPointHandles = new Rectangle2D.Double[ drawPoints.length]; - for (int i = 0; i < drawPoints.length; i++) { - Coordinate c = drawPoints[i]; - finPointHandles[i] = new Rectangle2D.Double(c.x - boxHalfWidth, c.y - boxHalfWidth, boxWidth, boxWidth); - g2.draw(finPointHandles[i]); + for (int currentIndex = 0; currentIndex < drawPoints.length; currentIndex++) { + Coordinate c = drawPoints[currentIndex]; + + if( currentIndex == selectedIndex ) { + finPointHandles[currentIndex] = new Rectangle2D.Double(c.x - selBoxHalfWidth, c.y - selBoxHalfWidth, selBoxWidth, selBoxWidth); + } else { + // normal boxes + finPointHandles[currentIndex] = new Rectangle2D.Double(c.x - boxHalfWidth, c.y - boxHalfWidth, boxWidth, boxWidth); + } + + g2.draw(finPointHandles[currentIndex]); } } - public Point2D.Double getPoint( final int x, final int y){ + private Point2D.Double getPoint( final int x, final int y){ if (finPointHandles == null) return null; @@ -261,9 +272,11 @@ public class FinPointFigure extends AbstractScaleFigure { return -1; for (int i = 0; i < finPointHandles.length; i++) { - if (finPointHandles[i].contains(p)) + if (finPointHandles[i].contains(p)) { return i; + } } + return -1; } @@ -353,6 +366,13 @@ public class FinPointFigure extends AbstractScaleFigure { System.err.println(String.format("________ Origin Location (px): w=%d, h=%d: ", originLocation_px.width, originLocation_px.height)); } - + + public void resetSelectedIndex() { + this.selectedIndex = -1; + } + + public void setSelectedIndex(final int newIndex) { + this.selectedIndex = newIndex; + } } From 7e5ab5de32a954b3653d38473bd1d8326fd35f42 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sat, 28 Jul 2018 10:47:00 -0400 Subject: [PATCH 19/47] [fix] Revert patch 6289aef0... which introduced simulation anomalies --- .../net/sf/openrocket/aerodynamics/barrowman/FinSetCalc.java | 4 ++-- core/test/net/sf/openrocket/aerodynamics/FinSetCalcTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/net/sf/openrocket/aerodynamics/barrowman/FinSetCalc.java b/core/src/net/sf/openrocket/aerodynamics/barrowman/FinSetCalc.java index 156eea790..36b065ac6 100644 --- a/core/src/net/sf/openrocket/aerodynamics/barrowman/FinSetCalc.java +++ b/core/src/net/sf/openrocket/aerodynamics/barrowman/FinSetCalc.java @@ -77,7 +77,7 @@ public class FinSetCalc extends RocketComponentCalc { } /* - * Calculates the non-axial forces produced by *one* *instance* of the fins. + * Calculates the non-axial forces produced by each set of fins. * (normal and side forces, pitch, yaw and roll moments, CP position, CNa). */ @Override @@ -124,7 +124,7 @@ public class FinSetCalc extends RocketComponentCalc { cna = cna1 * mul; } else { // Basic CNa assuming full efficiency - cna = cna1 / 2.0; + cna = cna1 * finCount / 2.0; } // logger.debug("Component cna = {}", cna); diff --git a/core/test/net/sf/openrocket/aerodynamics/FinSetCalcTest.java b/core/test/net/sf/openrocket/aerodynamics/FinSetCalcTest.java index 0894d9c4a..7eb12199f 100644 --- a/core/test/net/sf/openrocket/aerodynamics/FinSetCalcTest.java +++ b/core/test/net/sf/openrocket/aerodynamics/FinSetCalcTest.java @@ -63,7 +63,7 @@ public class FinSetCalcTest { double exp_cna_fins = 24.146933; double exp_cpx_fins = 0.0193484; - assertEquals(" FinSetCalc produces bad CNa: ", exp_cna_fins, forces.getCNa()*fins.getInstanceCount(), EPSILON); + assertEquals(" FinSetCalc produces bad CNa: ", exp_cna_fins, forces.getCNa(), EPSILON); assertEquals(" FinSetCalc produces bad C_p.x: ", exp_cpx_fins, forces.getCP().x, EPSILON); assertEquals(" FinSetCalc produces bad CN: ", 0.0, forces.getCN(), EPSILON); assertEquals(" FinSetCalc produces bad C_m: ", 0.0, forces.getCm(), EPSILON); @@ -97,7 +97,7 @@ public class FinSetCalcTest { double exp_cna_fins = 32.195911; double exp_cpx_fins = 0.0193484; - assertEquals(" FinSetCalc produces bad CNa: ", exp_cna_fins, forces.getCNa()*fins.getFinCount(), EPSILON); + assertEquals(" FinSetCalc produces bad CNa: ", exp_cna_fins, forces.getCNa(), EPSILON); assertEquals(" FinSetCalc produces bad C_p.x: ", exp_cpx_fins, forces.getCP().x, EPSILON); assertEquals(" FinSetCalc produces bad CN: ", 0.0, forces.getCN(), EPSILON); assertEquals(" FinSetCalc produces bad C_m: ", 0.0, forces.getCm(), EPSILON); From 4cb8a034545975f46ebd183a0c909dcd2a0324b2 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sat, 28 Jul 2018 11:45:31 -0400 Subject: [PATCH 20/47] [resolves #423][partial] BarrowmanCalculator no longer multiplies instanced leaf nodes. --- .../aerodynamics/BarrowmanCalculator.java | 29 +++++-------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java index cd5d07759..f70b959cb 100644 --- a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java +++ b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java @@ -193,32 +193,19 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { forces.zero(); RocketComponentCalc calcObj = calcMap.get(component); calcObj.calculateNonaxialForces(conditions, forces, warnings); - -// // to account for non axi-symmetric rockets such as a Delta-IV heavy, or a Falcon-9 Heavy -// if(( ! component.isAxisymmetric()) &&( component instanceof RingInstanceable )){ -// RingInstanceable ring = (RingInstanceable)component; -// forces.setAxisymmetric(false); -// total.setAxisymmetric(false); -// -// // TODO : Implement Best-Case, Worst-Case Cp calculations -// double minAngle = ring.getAngularOffset(); // angle of minimum CP, MOI -// double maxAngle = minAngle+Math.PI/2; // angle of maximum CP, MOI -// -// // worst case: ignore the CP contribution from *twin* externals -// // NYI -// -// // best case: the twins contribute their full CP broadside -// // NYI -// -// } - int instanceCount = component.getLocations().length; + // previous unstable version + // int instanceCount = component.getLocations().length; + final boolean isAssembly = (component.allowsChildren() && (component.getInstanceCount() > 1)); + + // we will need to adjust the calculations *somehow* here... + Coordinate x_cp_comp = forces.getCP(); - Coordinate x_cp_weighted = x_cp_comp.setWeight(x_cp_comp.weight * instanceCount); + Coordinate x_cp_weighted = x_cp_comp.setWeight(x_cp_comp.weight); Coordinate x_cp_absolute = component.toAbsolute(x_cp_weighted)[0]; forces.setCP(x_cp_absolute); - double CN_instanced = forces.getCN() * instanceCount; + double CN_instanced = forces.getCN(); forces.setCm(CN_instanced * forces.getCP().x / conditions.getRefLength()); if (map != null) { From e6b788cb0b0190ae71702959d16b4e6a5cc3f98a Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sat, 4 Aug 2018 15:56:55 -0400 Subject: [PATCH 21/47] [test] Moved fins from core-body to booster-body; (they are now doubly-instanced); adjusted tests to accept this. --- .../net/sf/openrocket/util/TestRockets.java | 30 ++-- .../aerodynamics/BarrowmanCalculatorTest.java | 5 +- .../masscalc/MassCalculatorTest.java | 149 +++++++++--------- .../rocketcomponent/ParallelStageTest.java | 50 +++--- .../rocketcomponent/RocketTest.java | 24 +-- 5 files changed, 133 insertions(+), 125 deletions(-) diff --git a/core/src/net/sf/openrocket/util/TestRockets.java b/core/src/net/sf/openrocket/util/TestRockets.java index e0eca4c93..9595564ad 100644 --- a/core/src/net/sf/openrocket/util/TestRockets.java +++ b/core/src/net/sf/openrocket/util/TestRockets.java @@ -942,21 +942,6 @@ public class TestRockets { FlightConfigurationId motorConfigId = selFCID; coreBody.setMotorConfig( coreMotorConfig, motorConfigId); - TrapezoidFinSet coreFins = new TrapezoidFinSet(); - coreBody.addChild(coreFins); - coreFins.setName("Core Fins"); - coreFins.setFinCount(4); - coreFins.setBaseRotation( Math.PI / 4); - coreFins.setThickness(0.003); - coreFins.setCrossSection(CrossSection.ROUNDED); - coreFins.setRootChord(0.32); - coreFins.setTipChord(0.12); - coreFins.setHeight(0.12); - coreFins.setSweep(0.18); - coreFins.setAxialMethod(AxialMethod.BOTTOM); - coreFins.setAxialOffset(0.0); - - // ====== Booster Stage Set ====== // ====== ====== ====== ====== ParallelStage boosterStage = new ParallelStage(); @@ -966,6 +951,7 @@ public class TestRockets { boosterStage.setAxialOffset(0.0); boosterStage.setInstanceCount(2); boosterStage.setRadius( RadiusMethod.SURFACE, 0.0 ); + boosterStage.setAngleMethod( AngleMethod.RELATIVE ); { NoseCone boosterCone = new NoseCone(Transition.Shape.POWER, 0.08, 0.0385); @@ -1001,6 +987,20 @@ public class TestRockets { boosterMotorConfig.setMotor( boosterMotor ); boosterMotorTubes.setMotorConfig( boosterMotorConfig, motorConfigId); boosterMotorTubes.setMotorOverhang(0.01234); + + TrapezoidFinSet boosterFins = new TrapezoidFinSet(); + boosterBody.addChild(boosterFins); + boosterFins.setName("Booster Fins"); + boosterFins.setFinCount(3); + boosterFins.setBaseRotation( Math.PI / 4); + boosterFins.setThickness(0.003); + boosterFins.setCrossSection(CrossSection.ROUNDED); + boosterFins.setRootChord(0.32); + boosterFins.setTipChord(0.12); + boosterFins.setHeight(0.12); + boosterFins.setSweep(0.18); + boosterFins.setAxialMethod(AxialMethod.BOTTOM); + boosterFins.setAxialOffset(0.0); } } diff --git a/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java b/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java index 928204d97..06e67aa94 100644 --- a/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java +++ b/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java @@ -15,6 +15,7 @@ import net.sf.openrocket.ServicesForTesting; import net.sf.openrocket.plugin.PluginModule; import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.rocketcomponent.BodyTube; +import net.sf.openrocket.rocketcomponent.FinSet; import net.sf.openrocket.rocketcomponent.FlightConfiguration; import net.sf.openrocket.rocketcomponent.NoseCone; import net.sf.openrocket.rocketcomponent.ParallelStage; @@ -180,7 +181,9 @@ public class BarrowmanCalculatorTest { Rocket rocket = TestRockets.makeFalcon9Heavy(); AerodynamicCalculator calc = new BarrowmanCalculator(); - ParallelStage booster = (ParallelStage)rocket.getChild(1).getChild(0).getChild(1); + final AxialStage coreStage = (AxialStage)rocket.getChild(1); + final ParallelStage booster = (ParallelStage)coreStage.getChild(0).getChild(0); + NoseCone nose = (NoseCone)booster.getChild(0); BodyTube body = (BodyTube)booster.getChild(1); diff --git a/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java b/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java index 6e24625c8..9c123c11d 100644 --- a/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java +++ b/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java @@ -5,6 +5,7 @@ import org.junit.Test; import net.sf.openrocket.motor.Motor; import net.sf.openrocket.rocketcomponent.AxialStage; +import net.sf.openrocket.rocketcomponent.BodyComponent; import net.sf.openrocket.rocketcomponent.BodyTube; import net.sf.openrocket.rocketcomponent.FinSet; import net.sf.openrocket.rocketcomponent.FlightConfiguration; @@ -190,22 +191,17 @@ public class MassCalculatorTest extends BaseTestCase { // ====== Core Stage ====== // ====== ====== ====== + final AxialStage coreStage = (AxialStage)rkt.getChild(1); { expMass = 0.1298860066700161; - cc= rkt.getChild(1).getChild(0); - compMass = cc.getComponentMass(); - assertEquals(cc.getName()+" mass calculated incorrectly: ", expMass, compMass, EPSILON); - - expMass = 0.21326976; - cc= rkt.getChild(1).getChild(0).getChild(0); - compMass = cc.getComponentMass(); - assertEquals(cc.getName()+" mass calculated incorrectly: ", expMass, compMass, EPSILON); + final BodyComponent coreBody = (BodyComponent)coreStage.getChild(0); + compMass = coreBody.getComponentMass(); + assertEquals(coreBody.getName()+" mass calculated incorrectly: ", expMass, compMass, EPSILON); } - - + // ====== Booster Set Stage ====== // ====== ====== ====== - ParallelStage boosters = (ParallelStage) rkt.getChild(1).getChild(0).getChild(1); + ParallelStage boosters = (ParallelStage) coreStage.getChild(0).getChild(0); { expMass = 0.0222459863653; // think of the casts as an assert that ( child instanceof NoseCone) == true @@ -222,6 +218,11 @@ public class MassCalculatorTest extends BaseTestCase { InnerTube mmt = (InnerTube)boosters.getChild(1).getChild(0); compMass = mmt.getComponentMass(); assertEquals( mmt.getName()+" mass calculated incorrectly: ", expMass, compMass, EPSILON); + + expMass = 0.15995232; + final FinSet boosterFins = (FinSet)boosters.getChild(1).getChild(1); + compMass = boosterFins.getComponentMass(); + assertEquals(boosterFins.getName()+" mass calculated incorrectly: ", expMass, compMass, EPSILON); } } @@ -274,21 +275,17 @@ public class MassCalculatorTest extends BaseTestCase { // ====== Core Stage ====== // ====== ====== ====== + final AxialStage coreStage = (AxialStage)rkt.getChild(1); { expCMx = 0.4; - BodyTube coreBody = (BodyTube)rkt.getChild(1).getChild(0); + BodyTube coreBody = (BodyTube)coreStage.getChild(0); actCMx = coreBody.getComponentCG().x; assertEquals("Core Body CMx calculated incorrectly: ", expCMx, actCMx, EPSILON); - - expCMx = 0.19393939; - FinSet coreFins = (FinSet)rkt.getChild(1).getChild(0).getChild(0); - actCMx = coreFins .getComponentCG().x; - assertEquals("Core Fins CMx calculated incorrectly: ", expCMx, actCMx, EPSILON); } // ====== Booster Set Stage ====== // ====== ====== ====== - ParallelStage boosters = (ParallelStage) rkt.getChild(1).getChild(0).getChild(1); + ParallelStage boosters = (ParallelStage) coreStage.getChild(0).getChild(0); { expCMx = 0.055710581052; // think of the casts as an assert that ( child instanceof NoseCone) == true @@ -305,6 +302,11 @@ public class MassCalculatorTest extends BaseTestCase { InnerTube mmt = (InnerTube)boosters.getChild(1).getChild(0); actCMx = mmt.getComponentCG().x; assertEquals(" Motor Mount Tube CMx calculated incorrectly: ", expCMx, actCMx, EPSILON); + + expCMx = 0.19393939; + FinSet boosterFins = (FinSet) boosters.getChild(1).getChild(1); + actCMx = boosterFins .getComponentCG().x; + assertEquals("Core Fins CMx calculated incorrectly: ", expCMx, actCMx, EPSILON); } } @@ -385,44 +387,37 @@ public class MassCalculatorTest extends BaseTestCase { // ====== Core Stage ====== // ====== ====== ====== + final AxialStage coreStage = (AxialStage)rocket.getChild(1); { - cc= rocket.getChild(1).getChild(0); + final BodyTube coreBody = (BodyTube)coreStage.getChild(0); expInertia = 0.000187588; - compInertia = cc.getRotationalInertia(); - assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON); + compInertia = coreBody.getRotationalInertia(); + assertEquals(coreBody.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON); expInertia = 0.00702105; - compInertia = cc.getLongitudinalInertia(); - assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON); + compInertia = coreBody.getLongitudinalInertia(); + assertEquals(coreBody.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON); - cc= rocket.getChild(1).getChild(0).getChild(0); - expInertia = 0.00734753; - compInertia = cc.getRotationalInertia(); - assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON); - expInertia = 0.02160236691801411; - compInertia = cc.getLongitudinalInertia(); - assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON); } - - + // ====== Booster Set Stage ====== // ====== ====== ====== - ParallelStage boosters = (ParallelStage) rocket.getChild(1).getChild(0).getChild(1); + ParallelStage boosters = (ParallelStage) coreStage.getChild(0).getChild(0); { - cc= boosters.getChild(0); + final NoseCone boosterNose = (NoseCone)boosters.getChild(0); expInertia = 1.82665797857e-5; - compInertia = cc.getRotationalInertia(); - assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON); + compInertia = boosterNose.getRotationalInertia(); + assertEquals(boosterNose.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON); expInertia = 1.96501191666e-7; - compInertia = cc.getLongitudinalInertia(); - assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON); + compInertia = boosterNose.getLongitudinalInertia(); + assertEquals(boosterNose.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON); - cc= boosters.getChild(1); + final BodyTube boosterBody = (BodyTube)boosters.getChild(1); expInertia = 1.875878651e-4; - compInertia = cc.getRotationalInertia(); - assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON); + compInertia = boosterBody.getRotationalInertia(); + assertEquals(boosterBody.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON); expInertia = 0.00702104762; - compInertia = cc.getLongitudinalInertia(); - assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON); + compInertia = boosterBody.getLongitudinalInertia(); + assertEquals(boosterBody.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON); cc= boosters.getChild(1).getChild(0); expInertia = 4.11444e-6; @@ -431,6 +426,15 @@ public class MassCalculatorTest extends BaseTestCase { expInertia = 3.75062e-5; compInertia = cc.getLongitudinalInertia(); assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON); + + final FinSet boosterFins = (FinSet)boosters.getChild(1).getChild(1); + expInertia = 0.00413298; + compInertia = boosterFins.getRotationalInertia(); + assertEquals(boosterFins.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON); + expInertia = 0.01215133; + compInertia = boosterFins.getLongitudinalInertia(); + assertEquals(boosterFins.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON); + } } @@ -469,8 +473,8 @@ public class MassCalculatorTest extends BaseTestCase { final RigidBody actualData = MassCalculator.calculateStructure( config ); final Coordinate actualCM = actualData.cm; - double expMass = 0.343156; - double expCMx = 1.134252; + double expMass = 0.12988600; + double expCMx = 0.964; assertEquals("Upper Stage Mass is incorrect: ", expMass, actualCM.weight, EPSILON); assertEquals("Upper Stage CM.x is incorrect: ", expCMx, actualCM.x, EPSILON); @@ -535,14 +539,13 @@ public class MassCalculatorTest extends BaseTestCase { FlightConfiguration config = rocket.getEmptyConfiguration(); - ParallelStage boosters = (ParallelStage) rocket.getChild(1).getChild(0).getChild(1); - config.setOnlyStage( boosters.getStageNumber() ); + config.setOnlyStage( TestRockets.FALCON_9H_BOOSTER_STAGE_NUMBER ); final RigidBody actualData = MassCalculator.calculateStructure( config ); final Coordinate actualCM = actualData.getCM(); - double expMass = 0.34207619524942634; - double expCMx = 0.9447396557660297; + double expMass = 0.66198084; + double expCMx = 1.08642949; assertEquals("Heavy Booster Mass is incorrect: ", expMass, actualCM.weight, EPSILON); assertEquals("Heavy Booster CM.x is incorrect: ", expCMx, actualCM.x, EPSILON); @@ -561,11 +564,11 @@ public class MassCalculatorTest extends BaseTestCase { RigidBody actualBoosterLaunchData = MassCalculator.calculateLaunch( config ); double actualMass = actualBoosterLaunchData.getMass(); - double expectedMass = 1.3260761952; + double expectedMass = 1.64598084; assertEquals(" Booster Launch Mass is incorrect: ", expectedMass, actualMass, EPSILON); final Coordinate actualCM = actualBoosterLaunchData.getCM(); - double expectedCMx = 1.21899745; + double expectedCMx = 1.22267891; Coordinate expCM = new Coordinate(expectedCMx,0,0, expectedMass); assertEquals(" Booster Launch CM.x is incorrect: ", expCM.x, actualCM.x, EPSILON); assertEquals(" Booster Launch CM.y is incorrect: ", expCM.y, actualCM.y, EPSILON); @@ -585,8 +588,8 @@ public class MassCalculatorTest extends BaseTestCase { RigidBody spentData = MassCalculator.calculateBurnout( config ); Coordinate spentCM = spentData.getCM(); - double expSpentMass = 0.8540761952494624; - double expSpentCMx = 1.166306978799226; + double expSpentMass = 1.17398084; + double expSpentCMx = 1.18582650; Coordinate expLaunchCM = new Coordinate( expSpentCMx, 0, 0, expSpentMass); assertEquals(" Booster Launch Mass is incorrect: ", expLaunchCM.weight, spentCM.weight, EPSILON); assertEquals(" Booster Launch CM.x is incorrect: ", expLaunchCM.x, spentCM.x, EPSILON); @@ -606,7 +609,8 @@ public class MassCalculatorTest extends BaseTestCase { RigidBody actualPropellant = MassCalculator.calculateMotor( config ); final Coordinate actCM= actualPropellant.getCM(); - ParallelStage boosters = (ParallelStage) rocket.getChild(1).getChild(0).getChild(1); + final AxialStage coreStage = (AxialStage) rocket.getChild(1); + final ParallelStage boosters = (ParallelStage) coreStage.getChild(0).getChild(0); final MotorMount mnt = (MotorMount)boosters.getChild(1).getChild(0); final Motor boosterMotor = mnt.getMotorConfig( config.getFlightConfigurationID()).getMotor(); @@ -650,11 +654,11 @@ public class MassCalculatorTest extends BaseTestCase { RigidBody spent = MassCalculator.calculateBurnout( config); - double expMOIRotational = 0.00576797953; + double expMOIRotational = 0.01593066; double boosterMOIRotational = spent.getRotationalInertia(); assertEquals(" Booster x-axis MOI is incorrect: ", expMOIRotational, boosterMOIRotational, EPSILON); - double expMOI_tr = 0.054690069584; + double expMOI_tr = 0.08018692435877221; double boosterMOI_tr= spent.getLongitudinalInertia(); assertEquals(" Booster transverse MOI is incorrect: ", expMOI_tr, boosterMOI_tr, EPSILON); } @@ -670,9 +674,9 @@ public class MassCalculatorTest extends BaseTestCase { RigidBody launchData = MassCalculator.calculateLaunch( config); - final double expIxx = 0.00882848653; + final double expIxx = 0.01899116; final double actIxx= launchData.getRotationalInertia(); - final double expIyy = 0.061981403261; + final double expIyy = 0.08637653; final double actIyy= launchData.getLongitudinalInertia(); assertEquals(" Booster x-axis MOI is incorrect: ", expIxx, actIxx, EPSILON); @@ -689,7 +693,8 @@ public class MassCalculatorTest extends BaseTestCase { rocket.setSelectedConfiguration( config.getId() ); config.setOnlyStage( TestRockets.FALCON_9H_BOOSTER_STAGE_NUMBER ); - final ParallelStage boosters = (ParallelStage) rocket.getChild(1).getChild(0).getChild(1); + final AxialStage coreStage = (AxialStage) rocket.getChild(1); + final ParallelStage boosters = (ParallelStage) coreStage.getChild(0).getChild(0); final double overrideMass = 0.5; boosters.setOverrideSubcomponents(true); boosters.setMassOverridden(true); @@ -712,11 +717,11 @@ public class MassCalculatorTest extends BaseTestCase { assertEquals(" Booster Launch CM is incorrect: ", expCM, boosterSetCM); // Validate MOI - double expMOI_axial = 0.0024481075335; + double expMOI_axial = 0.01261079; double boosterMOI_xx= burnout.getRotationalInertia(); assertEquals(" Booster x-axis MOI is incorrect: ", expMOI_axial, boosterMOI_xx, EPSILON); - double expMOI_tr = 8.885103994735; + double expMOI_tr = 16.163954943504205; double boosterMOI_tr= burnout.getLongitudinalInertia(); assertEquals(" Booster transverse MOI is incorrect: ", expMOI_tr, boosterMOI_tr, EPSILON); } @@ -729,7 +734,8 @@ public class MassCalculatorTest extends BaseTestCase { FlightConfiguration config = rocket.getEmptyConfiguration(); rocket.setSelectedConfiguration( config.getId() ); - ParallelStage boosters = (ParallelStage) rocket.getChild(1).getChild(0).getChild(1); + final AxialStage coreStage = (AxialStage) rocket.getChild(1); + final ParallelStage boosters = (ParallelStage) coreStage.getChild(0).getChild(0); config.setOnlyStage( boosters.getStageNumber() ); NoseCone nose = (NoseCone)boosters.getChild(0); @@ -747,10 +753,10 @@ public class MassCalculatorTest extends BaseTestCase { RigidBody boosterData = MassCalculator.calculateStructure( config ); Coordinate boosterCM = boosterData.getCM(); - double expTotalMass = 3.09; + double expTotalMass = 3.40990464; assertEquals(" Booster Launch Mass is incorrect: ", expTotalMass, boosterData.getMass(), EPSILON); - double expCMx = 0.81382493; + double expCMx = 0.85361377; Coordinate expCM = new Coordinate( expCMx, 0, 0, expTotalMass); assertEquals(" Booster Launch CM.x is incorrect: ", expCM.x, boosterCM.x, EPSILON); assertEquals(" Booster Launch CM.y is incorrect: ", expCM.y, boosterCM.y, EPSILON); @@ -758,11 +764,11 @@ public class MassCalculatorTest extends BaseTestCase { assertEquals(" Booster Launch CM is incorrect: ", expCM, boosterCM); // Validate MOI - double expMOI_axial = 0.0213759528078421; + double expMOI_axial = 0.031538609; double boosterMOI_xx= boosterData.getRotationalInertia(); assertEquals(" Booster x-axis MOI is incorrect: ", expMOI_axial, boosterMOI_xx, EPSILON); - double expMOI_tr = 0.299042045787; + double expMOI_tr = 0.37548843; double boosterMOI_tr= boosterData.getLongitudinalInertia(); assertEquals(" Booster transverse MOI is incorrect: ", expMOI_tr, boosterMOI_tr, EPSILON); } @@ -776,7 +782,8 @@ public class MassCalculatorTest extends BaseTestCase { rocket.setSelectedConfiguration( config.getId() ); config.setOnlyStage( TestRockets.FALCON_9H_BOOSTER_STAGE_NUMBER ); - ParallelStage boosters = (ParallelStage) rocket.getChild(1).getChild(0).getChild(1); + final AxialStage coreStage = (AxialStage) rocket.getChild(1); + final ParallelStage boosters = (ParallelStage) coreStage.getChild(0).getChild(0); NoseCone nose = (NoseCone)boosters.getChild(0); nose.setCGOverridden(true); @@ -792,11 +799,11 @@ public class MassCalculatorTest extends BaseTestCase { RigidBody structure = MassCalculator.calculateStructure( config); - double expMass = 0.34207619524942634; + final double expMass = 0.66198084; double calcTotalMass = structure.getMass(); assertEquals(" Booster Launch Mass is incorrect: ", expMass, calcTotalMass, EPSILON); - double expCMx = 1.0265399801199806; + final double expCMx = 1.12869951; Coordinate expCM = new Coordinate( expCMx, 0, 0, expMass); assertEquals(" Booster Launch CM.x is incorrect: ", expCM.x, structure.getCM().x, EPSILON); assertEquals(" Booster Launch CM.y is incorrect: ", expCM.y, structure.getCM().y, EPSILON); @@ -804,11 +811,11 @@ public class MassCalculatorTest extends BaseTestCase { assertEquals(" Booster Launch CM is incorrect: ", expCM, structure.getCM()); // Validate MOI - double expMOI_axial = 0.002448107533; + final double expMOI_axial = 0.012610790; double boosterMOI_xx= structure.getRotationalInertia(); assertEquals(" Booster x-axis MOI is incorrect: ", expMOI_axial, boosterMOI_xx, EPSILON); - double expMOI_tr = 0.031800928766; + final double expMOI_tr = 0.063491225; double boosterMOI_tr= structure.getLongitudinalInertia(); assertEquals(" Booster transverse MOI is incorrect: ", expMOI_tr, boosterMOI_tr, EPSILON); } diff --git a/core/test/net/sf/openrocket/rocketcomponent/ParallelStageTest.java b/core/test/net/sf/openrocket/rocketcomponent/ParallelStageTest.java index ccd83a88a..74dd3d49b 100644 --- a/core/test/net/sf/openrocket/rocketcomponent/ParallelStageTest.java +++ b/core/test/net/sf/openrocket/rocketcomponent/ParallelStageTest.java @@ -133,14 +133,6 @@ public class ParallelStageTest extends BaseTestCase { RocketComponent coreBody = coreStage.getChild(0); Assert.assertEquals( coreBody.getPosition().x, 0.0, EPSILON); Assert.assertEquals( coreBody.getComponentLocations()[0].x, expectedCoreStageX, EPSILON); - - FinSet coreFins = (FinSet)coreBody.getChild(0); - - // default is offset=0, method=BOTTOM - assertEquals( AxialMethod.BOTTOM, coreFins.getAxialMethod() ); - assertEquals( 0.0, coreFins.getAxialOffset(), EPSILON); - assertEquals( 0.480, coreFins.getPosition().x, EPSILON); - assertEquals( 1.044, coreFins.getComponentLocations()[0].x, EPSILON); } @@ -151,7 +143,7 @@ public class ParallelStageTest extends BaseTestCase { AxialStage sustainer = (AxialStage) rocket.getChild(0); AxialStage coreStage = (AxialStage) rocket.getChild(1); - AxialStage booster = (AxialStage) coreStage.getChild(0).getChild(1); + AxialStage booster = (AxialStage) coreStage.getChild(0).getChild(0); AxialStage sustainerPrev = sustainer.getUpperStage(); assertThat("sustainer parent is not found correctly: ", sustainerPrev, equalTo(null)); @@ -194,7 +186,7 @@ public class ParallelStageTest extends BaseTestCase { public void testBoosterInitializationFREERadius() { final Rocket rocket = TestRockets.makeFalcon9Heavy(); final AxialStage coreStage = (AxialStage) rocket.getChild(1); - final ParallelStage parallelBoosterSet = (ParallelStage)coreStage.getChild(0).getChild(1); + final ParallelStage parallelBoosterSet = (ParallelStage)coreStage.getChild(0).getChild(0); // vvvv function under test parallelBoosterSet.setRadiusMethod( RadiusMethod.FREE ); @@ -214,7 +206,7 @@ public class ParallelStageTest extends BaseTestCase { public void testBoosterInitializationSURFACERadius() { final Rocket rocket = TestRockets.makeFalcon9Heavy(); final AxialStage coreStage = (AxialStage) rocket.getChild(1); - final ParallelStage parallelBoosterStage = (ParallelStage)coreStage.getChild(0).getChild(1); + final ParallelStage parallelBoosterStage = (ParallelStage)coreStage.getChild(0).getChild(0); final BodyTube coreBody = (BodyTube)coreStage.getChild(0); final BodyTube boosterBody = (BodyTube)parallelBoosterStage.getChild(1); @@ -257,7 +249,7 @@ public class ParallelStageTest extends BaseTestCase { public void testBoosterInitializationRELATIVERadius() { final Rocket rocket = TestRockets.makeFalcon9Heavy(); final AxialStage coreStage = (AxialStage) rocket.getChild(1); - final ParallelStage parallelBoosterStage = (ParallelStage)coreStage.getChild(0).getChild(1); + final ParallelStage parallelBoosterStage = (ParallelStage)coreStage.getChild(0).getChild(0); final BodyTube coreBody = (BodyTube)coreStage.getChild(0); final BodyTube boosterBody = (BodyTube)parallelBoosterStage.getChild(1); @@ -299,7 +291,7 @@ public class ParallelStageTest extends BaseTestCase { final RocketComponent rocket = TestRockets.makeFalcon9Heavy(); final AxialStage coreStage = (AxialStage) rocket.getChild(1); final BodyTube coreBody = (BodyTube)coreStage.getChild(0); - final ParallelStage boosterStage = (ParallelStage)coreStage.getChild(0).getChild(1); + final ParallelStage boosterStage = (ParallelStage)coreStage.getChild(0).getChild(0); final BodyTube boosterBody = (BodyTube)boosterStage.getChild(1); // vv function under test @@ -335,7 +327,7 @@ public class ParallelStageTest extends BaseTestCase { public void testSetStagePosition_outsideABSOLUTE() { final RocketComponent rocket = TestRockets.makeFalcon9Heavy(); final BodyTube coreBody= (BodyTube) rocket.getChild(1).getChild(0); - final ParallelStage boosterStage = (ParallelStage)coreBody.getChild(1); + final ParallelStage boosterStage = (ParallelStage)coreBody.getChild(0); double targetAbsoluteX = 0.8; double expectedRelativeX = 0.236; @@ -386,7 +378,7 @@ public class ParallelStageTest extends BaseTestCase { public void testSetStagePosition_outsideTOP() { final RocketComponent rocket = TestRockets.makeFalcon9Heavy(); final AxialStage coreStage = (AxialStage) rocket.getChild(1); - final ParallelStage boosterStage = (ParallelStage)coreStage.getChild(0).getChild(1); + final ParallelStage boosterStage = (ParallelStage)coreStage.getChild(0).getChild(0); double targetOffset = 0.2; @@ -417,7 +409,7 @@ public class ParallelStageTest extends BaseTestCase { public void testSetMIDDLE() { final RocketComponent rocket = TestRockets.makeFalcon9Heavy(); final AxialStage coreStage = (AxialStage) rocket.getChild(1); - final ParallelStage boosterStage = (ParallelStage)coreStage.getChild(0).getChild(1); + final ParallelStage boosterStage = (ParallelStage)coreStage.getChild(0).getChild(0); // when 'external' the stage should be freely movable // vv function under test @@ -436,7 +428,8 @@ public class ParallelStageTest extends BaseTestCase { @Test public void testSetBOTTOM() { final RocketComponent rocket = TestRockets.makeFalcon9Heavy(); - final ParallelStage boosterStage = (ParallelStage)rocket.getChild(1).getChild(0).getChild(1); + final AxialStage coreStage = (AxialStage) rocket.getChild(1); + final ParallelStage boosterStage = (ParallelStage)coreStage.getChild(0).getChild(0); // vv function under test double targetOffset = 0.2; @@ -454,7 +447,7 @@ public class ParallelStageTest extends BaseTestCase { public void testSetTOP_getABSOLUTE() { final RocketComponent rocket = TestRockets.makeFalcon9Heavy(); final AxialStage coreStage = (AxialStage) rocket.getChild(1); - final ParallelStage boosterStage = (ParallelStage)coreStage.getChild(0).getChild(1); + final ParallelStage boosterStage = (ParallelStage)coreStage.getChild(0).getChild(0); double targetOffset = 0.2; @@ -480,7 +473,7 @@ public class ParallelStageTest extends BaseTestCase { public void testSetTOP_getAFTER() { final RocketComponent rocket = TestRockets.makeFalcon9Heavy(); final AxialStage coreStage = (AxialStage) rocket.getChild(1); - final ParallelStage boosterStage = (ParallelStage)coreStage.getChild(0).getChild(1); + final ParallelStage boosterStage = (ParallelStage)coreStage.getChild(0).getChild(0); double targetOffset = 0.2; @@ -503,7 +496,7 @@ public class ParallelStageTest extends BaseTestCase { public void testSetTOP_getMIDDLE() { final RocketComponent rocket = TestRockets.makeFalcon9Heavy(); final AxialStage coreStage = (AxialStage) rocket.getChild(1); - final ParallelStage boosterStage = (ParallelStage)coreStage.getChild(0).getChild(1); + final ParallelStage boosterStage = (ParallelStage)coreStage.getChild(0).getChild(0); double targetOffset = 0.2; @@ -525,7 +518,7 @@ public class ParallelStageTest extends BaseTestCase { public void testSetTOP_getBOTTOM() { final RocketComponent rocket = TestRockets.makeFalcon9Heavy(); final AxialStage coreStage = (AxialStage) rocket.getChild(1); - final ParallelStage boosterStage = (ParallelStage)coreStage.getChild(0).getChild(1); + final ParallelStage boosterStage = (ParallelStage)coreStage.getChild(0).getChild(0); double targetOffset = 0.2; @@ -547,7 +540,8 @@ public class ParallelStageTest extends BaseTestCase { @Test public void testSetBOTTOM_getTOP() { final RocketComponent rocket = TestRockets.makeFalcon9Heavy(); - final ParallelStage boosterStage = (ParallelStage)rocket.getChild(1).getChild(0).getChild(1); + final AxialStage coreStage = (AxialStage) rocket.getChild(1); + final ParallelStage boosterStage = (ParallelStage)coreStage.getChild(0).getChild(0); // vv function under test double targetOffset = 0.2; @@ -568,7 +562,7 @@ public class ParallelStageTest extends BaseTestCase { public void testOutsideStageRepositionTOPAfterAdd() { final RocketComponent rocket = TestRockets.makeFalcon9Heavy(); final AxialStage coreStage = (AxialStage) rocket.getChild(1); - final ParallelStage boosterStage = (ParallelStage)coreStage.getChild(0).getChild(1); + final ParallelStage boosterStage = (ParallelStage)coreStage.getChild(0).getChild(0); final double targetOffset = +2.50; final AxialMethod targetMethod = AxialMethod.TOP; @@ -597,7 +591,9 @@ public class ParallelStageTest extends BaseTestCase { @Test public void testStageInitializationMethodValueOrder() { final RocketComponent rocket = TestRockets.makeFalcon9Heavy(); - final BodyTube coreBody = (BodyTube) rocket.getChild(1).getChild(0); + final AxialStage coreStage = (AxialStage) rocket.getChild(1); + final BodyTube coreBody = (BodyTube)coreStage.getChild(0); + ParallelStage boosterA = createExtraBooster(); boosterA.setName("Booster A Stage"); @@ -632,7 +628,7 @@ public class ParallelStageTest extends BaseTestCase { final AxialStage coreStage = (AxialStage) rocket.getChild(1); final BodyTube coreBody = (BodyTube) coreStage.getChild(0); - ParallelStage boosterA = (ParallelStage)coreBody.getChild(1); + ParallelStage boosterA = (ParallelStage)coreBody.getChild(0); ParallelStage boosterB = createExtraBooster(); boosterB.setName("Booster A Stage"); @@ -665,8 +661,8 @@ public class ParallelStageTest extends BaseTestCase { actualStageNumber = boosterC.getStageNumber(); assertEquals(" init order error: Booster B: resultant positions: ", expectedStageNumber, actualStageNumber); - // remove Booster A - coreBody.removeChild(2); + // remove Booster B + coreBody.removeChild(1); String treedump = rocket.toDebugTree(); int expectedStageCount = 4; diff --git a/core/test/net/sf/openrocket/rocketcomponent/RocketTest.java b/core/test/net/sf/openrocket/rocketcomponent/RocketTest.java index 17d2d0570..c37536cfe 100644 --- a/core/test/net/sf/openrocket/rocketcomponent/RocketTest.java +++ b/core/test/net/sf/openrocket/rocketcomponent/RocketTest.java @@ -242,7 +242,7 @@ public class RocketTest extends BaseTestCase { // ====== Booster Set Stage ====== // ====== ====== ====== - ParallelStage boosters = (ParallelStage) coreBody.getChild(1); + ParallelStage boosters = (ParallelStage) coreBody.getChild(0); { assertEquals( RadiusMethod.SURFACE, boosters.getRadiusMethod() ); assertEquals( AngleMethod.RELATIVE, boosters.getAngleMethod() ); @@ -277,19 +277,21 @@ public class RocketTest extends BaseTestCase { loc = boosterBody.getComponentLocations()[0]; assertEquals(boosterBody.getName()+" offset is incorrect: ", 0.08, offset.x, EPSILON); assertEquals(boosterBody.getName()+" location is incorrect: ", 0.564, loc.x, EPSILON); + { + InnerTube mmt = (InnerTube)boosterBody.getChild(0); + offset = mmt.getPosition(); + loc = mmt.getComponentLocations()[0]; + assertEquals(mmt.getName()+" offset is incorrect: ", 0.65, offset.x, EPSILON); + assertEquals(mmt.getName()+" location is incorrect: ", 1.214, loc.x, EPSILON); - InnerTube mmt = (InnerTube)boosters.getChild(1).getChild(0); - offset = mmt.getPosition(); - loc = mmt.getComponentLocations()[0]; - assertEquals(mmt.getName()+" offset is incorrect: ", 0.65, offset.x, EPSILON); - assertEquals(mmt.getName()+" location is incorrect: ", 1.214, loc.x, EPSILON); + final FinSet coreFins = (FinSet)boosterBody.getChild(1); + offset = coreFins.getPosition(); + loc = coreFins.getComponentLocations()[0]; + assertEquals(coreFins.getName()+" offset is incorrect: ", 0.480, offset.x, EPSILON); + assertEquals(coreFins.getName()+" location is incorrect: ", 1.044, loc.x, EPSILON); + } } - FinSet coreFins = (FinSet)rocket.getChild(1).getChild(0).getChild(0); - offset = coreFins.getPosition(); - loc = coreFins.getComponentLocations()[0]; - assertEquals(coreFins.getName()+" offset is incorrect: ", 0.480, offset.x, EPSILON); - assertEquals(coreFins.getName()+" location is incorrect: ", 1.044, loc.x, EPSILON); } } From fab167abdce2eee3484ad184e4ae58d1bc11b633 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sun, 29 Jul 2018 10:17:44 -0400 Subject: [PATCH 22/47] [fix] Fixes the way BarrowmanCalculator handles instancing, particularly for ComponentAssemblies --- .../aerodynamics/AerodynamicForces.java | 39 ++++++- .../aerodynamics/BarrowmanCalculator.java | 108 +++++++++--------- .../aerodynamics/BarrowmanCalculatorTest.java | 5 +- 3 files changed, 91 insertions(+), 61 deletions(-) diff --git a/core/src/net/sf/openrocket/aerodynamics/AerodynamicForces.java b/core/src/net/sf/openrocket/aerodynamics/AerodynamicForces.java index cbfc3ddb7..da41651be 100644 --- a/core/src/net/sf/openrocket/aerodynamics/AerodynamicForces.java +++ b/core/src/net/sf/openrocket/aerodynamics/AerodynamicForces.java @@ -286,7 +286,7 @@ public class AerodynamicForces implements Cloneable, Monitorable { /** * Zero all values to 0 / Coordinate.NUL. Component is left as it was. */ - public void zero() { + public AerodynamicForces zero() { // component untouched setAxisymmetric(true); @@ -303,6 +303,8 @@ public class AerodynamicForces implements Cloneable, Monitorable { setCD(0); setPitchDampingMoment(0); setYawDampingMoment(0); + + return this; } @@ -388,4 +390,39 @@ public class AerodynamicForces implements Cloneable, Monitorable { public int getModID() { return modID; } + + public AerodynamicForces merge(AerodynamicForces other) { + + this.cp = cp.average(other.getCP()); + this.CNa = CNa + other.getCNa(); + this.CN = CN + other.getCN(); + this.Cm = Cm + other.getCm(); + this.Cside = Cside + other.getCside(); + this.Cyaw = Cyaw + other.getCyaw(); + this.Croll = Croll + other.getCroll(); + this.CrollDamp = CrollDamp + other.getCrollDamp(); + this.CrollForce = CrollForce + other.getCrollForce(); + + modID++; + + return this; + } + + public AerodynamicForces multiplex(final int instanceCount) { + + this.cp = cp.setWeight(cp.weight*instanceCount); + this.CNa = CNa*instanceCount; + this.CN = CN*instanceCount; + this.Cm = Cm*instanceCount; + this.Cside = Cside*instanceCount; + this.Cyaw = Cyaw*instanceCount; + this.Croll = Croll*instanceCount; + this.CrollDamp = CrollDamp*instanceCount; + this.CrollForce = CrollForce*instanceCount; + + modID++; + + return this; + } + } diff --git a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java index f70b959cb..ad79ec07d 100644 --- a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java +++ b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java @@ -158,22 +158,16 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { * Perform the actual CP calculation. */ private AerodynamicForces calculateNonAxialForces(FlightConfiguration configuration, FlightConditions conditions, - Map map, WarningSet warnings) { + Map calculators, WarningSet warnings) { checkCache(configuration); - AerodynamicForces total = new AerodynamicForces(); - total.zero(); - - AerodynamicForces forces = new AerodynamicForces(); - if (warnings == null) warnings = ignoreWarningSet; if (conditions.getAOA() > 17.5 * Math.PI / 180) warnings.add(new Warning.LargeAOA(conditions.getAOA())); - if (calcMap == null) buildCalcMap(configuration); @@ -182,60 +176,60 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { warnings.add( Warning.DIAMETER_DISCONTINUITY); } - for (RocketComponent component : configuration.getActiveComponents()) { - - // Skip non-aerodynamic components - if (!component.isAerodynamic()) - continue; - - - // Call calculation method - forces.zero(); - RocketComponentCalc calcObj = calcMap.get(component); - calcObj.calculateNonaxialForces(conditions, forces, warnings); - - - // previous unstable version - // int instanceCount = component.getLocations().length; - final boolean isAssembly = (component.allowsChildren() && (component.getInstanceCount() > 1)); - - // we will need to adjust the calculations *somehow* here... - - Coordinate x_cp_comp = forces.getCP(); - Coordinate x_cp_weighted = x_cp_comp.setWeight(x_cp_comp.weight); - Coordinate x_cp_absolute = component.toAbsolute(x_cp_weighted)[0]; - forces.setCP(x_cp_absolute); - double CN_instanced = forces.getCN(); - forces.setCm(CN_instanced * forces.getCP().x / conditions.getRefLength()); - - if (map != null) { - AerodynamicForces f = map.get(component); - - f.setCP(forces.getCP()); - f.setCNa(forces.getCNa()); - f.setCN(forces.getCN()); - f.setCm(forces.getCm()); - f.setCside(forces.getCside()); - f.setCyaw(forces.getCyaw()); - f.setCroll(forces.getCroll()); - f.setCrollDamp(forces.getCrollDamp()); - f.setCrollForce(forces.getCrollForce()); - } - - total.setCP(total.getCP().average(forces.getCP())); - total.setCNa(total.getCNa() + forces.getCNa()); - total.setCN(total.getCN() + forces.getCN()); - total.setCm(total.getCm() + forces.getCm()); - total.setCside(total.getCside() + forces.getCside()); - total.setCyaw(total.getCyaw() + forces.getCyaw()); - total.setCroll(total.getCroll() + forces.getCroll()); - total.setCrollDamp(total.getCrollDamp() + forces.getCrollDamp()); - total.setCrollForce(total.getCrollForce() + forces.getCrollForce()); - } + AerodynamicForces total = calculateAssemblyNonAxialForces(configuration.getRocket(), configuration, conditions, calculators, warnings, ""); return total; } + private AerodynamicForces calculateAssemblyNonAxialForces( final RocketComponent component, + FlightConfiguration configuration, FlightConditions conditions, + Map calculators, WarningSet warnings, + String indent) { + + final AerodynamicForces assemblyForces= new AerodynamicForces().zero(); + +// System.err.println(String.format("%s@@ %s <%s>", indent, component.getName(), component.getClass().getSimpleName())); + + // ==== calculate child forces ==== + for (RocketComponent child: component.getChildren()) { + AerodynamicForces childForces = calculateAssemblyNonAxialForces( child, configuration, conditions, calculators, warnings, indent+" "); + assemblyForces.merge(childForces); + } + + // calculate *this* component's forces + RocketComponentCalc calcObj = calcMap.get(component); + if(null != calcObj) { + AerodynamicForces componentForces = new AerodynamicForces().zero(); + calcObj.calculateNonaxialForces(conditions, componentForces, warnings); + + Coordinate x_cp_comp = componentForces.getCP(); + Coordinate x_cp_weighted = x_cp_comp.setWeight(x_cp_comp.weight); + Coordinate x_cp_absolute = component.toAbsolute(x_cp_weighted)[0]; + componentForces.setCP(x_cp_absolute); + double CN_instanced = componentForces.getCN(); + componentForces.setCm(CN_instanced * componentForces.getCP().x / conditions.getRefLength()); + +// if( 0.0001 < Math.abs(0 - componentForces.getCNa())){ +// System.err.println(String.format("%s....Component.CNa: %g @ CPx: %g", indent, componentForces.getCNa(), componentForces.getCP().x)); +// } + + assemblyForces.merge(componentForces); + } + +// if( 0.0001 < Math.abs(0 - assemblyForces.getCNa())){ +// System.err.println(String.format("%s....Assembly.CNa: %g @ CPx: %g", indent, assemblyForces.getCNa(), assemblyForces.getCP().x)); +// } + + // fetches instanced versions + // int instanceCount = component.getLocations().length; + + if( component.allowsChildren() && (component.getInstanceCount() > 1)) { + return assemblyForces.multiplex(component.getInstanceCount()); + }else { + return assemblyForces; + } + } + @Override public boolean isContinuous( final Rocket rkt){ diff --git a/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java b/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java index 06e67aa94..068cdf54e 100644 --- a/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java +++ b/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java @@ -108,7 +108,6 @@ public class BarrowmanCalculatorTest { assertEquals(" Estes Alpha III CNa value is incorrect:", exp_cna, calcCP.weight, EPSILON); } - @Test public void testCPDoubleStrapOn() { Rocket rocket = TestRockets.makeFalcon9Heavy(); @@ -117,8 +116,8 @@ public class BarrowmanCalculatorTest { FlightConditions conditions = new FlightConditions(config); WarningSet warnings = new WarningSet(); - double expCPx = 0.994642; - double expCNa = 15.437111; + double expCPx = 1.04662388; + double expCNa = 21.5111598; Coordinate calcCP = calc.getCP(config, conditions, warnings); assertEquals(" Falcon 9 Heavy CP x value is incorrect:", expCPx, calcCP.x, EPSILON); From 8dfd4bfd532190048be5655d5dcf30eedcd7317a Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sat, 18 Aug 2018 11:14:25 +0200 Subject: [PATCH 23/47] [minor][debug][oneline] removed excess sys.err debug line --- swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java index 0bf8a416d..810f56a11 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java @@ -363,8 +363,6 @@ public class FinPointFigure extends AbstractScaleFigure { // the negative sign is to compensate for the mount's negative location. originLocation_px.width = borderThickness_px.width - (int)(mountBounds_m.getX()*scale); originLocation_px.height = borderThickness_px.height + (int)(Math.max( rMaxParent, finBounds_m.getHeight())*scale); - - System.err.println(String.format("________ Origin Location (px): w=%d, h=%d: ", originLocation_px.width, originLocation_px.height)); } public void resetSelectedIndex() { From 3593b2197b993880954c5f7a349160fec9e38e2d Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sat, 18 Aug 2018 11:25:51 +0200 Subject: [PATCH 24/47] [fixes #439] May now delete points again, in the FreeformFinSetConfig window --- .../configdialog/FreeformFinSetConfig.java | 45 ++++++++----------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java index 8949883c1..110c9d910 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java @@ -397,11 +397,6 @@ public class FreeformFinSetConfig extends FinSetConfig { public void mousePressed(MouseEvent event) { int mods = event.getModifiersEx(); - if (event.getButton() != MouseEvent.BUTTON1 || (mods & ANY_MASK) != 0) { - super.mousePressed(event); - return; - } - final int pressIndex = getPoint(event); if ( pressIndex >= 0) { dragIndex = pressIndex; @@ -432,16 +427,15 @@ public class FreeformFinSetConfig extends FinSetConfig { super.mousePressed(event); } - @Override public void mouseDragged(MouseEvent event) { - int mods = event.getModifiersEx(); + int mods = event.getModifiersEx(); if (dragIndex <= 0 || (mods & (ANY_MASK | MouseEvent.BUTTON1_DOWN_MASK)) != MouseEvent.BUTTON1_DOWN_MASK) { super.mouseDragged(event); return; } + Point2D.Double point = getCoordinates(event); - try { finset.setPoint(dragIndex, point.x, point.y); } catch (IllegalFinPointException ignore) { @@ -451,7 +445,6 @@ public class FreeformFinSetConfig extends FinSetConfig { updateFields(); } - @Override public void mouseReleased(MouseEvent event) { dragIndex = -1; @@ -460,24 +453,22 @@ public class FreeformFinSetConfig extends FinSetConfig { @Override public void mouseClicked(MouseEvent event) { - int mods = event.getModifiersEx(); - if (event.getButton() != MouseEvent.BUTTON1 || (mods & ANY_MASK) != MouseEvent.CTRL_DOWN_MASK) { - super.mouseClicked(event); - return; - } - - int index = getPoint(event); - if (index < 0) { - super.mouseClicked(event); - return; - } - - try { - finset.removePoint(index); - } catch (IllegalFinPointException ignore) { - } - } - + int mods = event.getModifiersEx(); + if(( event.getButton() == MouseEvent.BUTTON1) && (0 < (MouseEvent.CTRL_DOWN_MASK & mods))) { + int clickIndex = getPoint(event); + if ( 0 < clickIndex) { + // if ctrl+click, delete point + try { + finset.removePoint(clickIndex); + } catch (IllegalFinPointException ignore) { + log.error("Ignoring IllegalFinPointException while dragging, dragIndex=" + dragIndex + ". This is likely an internal error."); + } + return; + } + } + + super.mouseClicked(event); + } private int getPoint(MouseEvent event) { Point p0 = event.getPoint(); From c971978b024b14f01b6775f35eb39ccfa57bdf02 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sun, 19 Aug 2018 10:42:08 +0200 Subject: [PATCH 25/47] [fix] AbstractScaleFigure now stores (& requires!) the visible bounds when setting zoom/scale. - if the visible bounds are larger than the requested scale bounds, then the figure is expanded to match. --- .../gui/scalefigure/AbstractScaleFigure.java | 41 ++++++++++++------- .../gui/scalefigure/ScaleScrollPane.java | 5 ++- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java index 9e0802a7c..6412b035e 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java @@ -42,10 +42,12 @@ public abstract class AbstractScaleFigure extends JPanel { // pixel offset from the the subject's origin to the canvas's upper-left-corner. protected Dimension originLocation_px = new Dimension(0,0); + // size of the visible region + protected Dimension visibleBounds_px = new Dimension(0,0); + // ======= whatever this figure is drawing, in real-space coordinates: meters protected Rectangle2D subjectBounds_m = null; - // combines the translation and scale in one place: // which frames does this transform between ? protected AffineTransform projection = null; @@ -85,11 +87,15 @@ public abstract class AbstractScaleFigure extends JPanel { /** * Set the scale level of the figure. A scale value of 1.0 is equivalent to 100 % scale. - * smaller scale display the subject smaller. + * Smaller scales display the subject smaller. * - * @param newScaleRequest the scale level. + * If the figure would be smaller than the 'visibleBounds', then the figure is grown to match, + * and the figures internal contents are centered according to the figure's origin. + * + * @param newScaleRequest the scale level + * @param visibleBounds the visible bounds upon the Figure */ - public void scaleTo(final double newScaleRequest) { + public void scaleTo(final double newScaleRequest, final Dimension visibleBounds) { if (MathUtil.equals(this.userScale, newScaleRequest, 0.01)){ return;} if (Double.isInfinite(newScaleRequest) || Double.isNaN(newScaleRequest)) { @@ -98,33 +104,34 @@ public abstract class AbstractScaleFigure extends JPanel { log.warn(String.format("scaling Request from %g => %g @%s\n", this.userScale, newScaleRequest, this.getClass().getSimpleName()), new Throwable()); this.userScale = MathUtil.clamp( newScaleRequest, MINIMUM_ZOOM, MAXIMUM_ZOOM); - this.scale = baseScale * userScale; - + + this.visibleBounds_px = visibleBounds; + this.fireChangeEvent(); } /** * Set the scale level to display newBounds * - * @param bounds the bounds of the figure. + * @param visibleBounds the visible bounds to scale this figure to. */ - public void scaleTo(Dimension newBounds) { - if( 0 == newBounds.getWidth() || 0 == newBounds.getHeight()) + public void scaleTo(Dimension visibleBounds) { + if( 0 == visibleBounds.getWidth() || 0 == visibleBounds.getHeight()) return; updateSubjectDimensions(); // dimensions within the viewable area, which are available to draw - final int drawable_width_px = newBounds.width - 2 * borderThickness_px.width; - final int drawable_height_px = newBounds.height - 2 * borderThickness_px.height; + final int drawable_width_px = visibleBounds.width - 2 * borderThickness_px.width; + final int drawable_height_px = visibleBounds.height - 2 * borderThickness_px.height; if(( 0 < drawable_width_px ) && ( 0 < drawable_height_px)) { final double width_scale = (drawable_width_px) / ( subjectBounds_m.getWidth() * baseScale); final double height_scale = (drawable_height_px) / ( subjectBounds_m.getHeight() * baseScale); final double minScale = Math.min(height_scale, width_scale); - scaleTo(minScale); + scaleTo(minScale, visibleBounds); } } @@ -142,8 +149,12 @@ public abstract class AbstractScaleFigure extends JPanel { */ protected void updateCanvasSize() { - Dimension preferredFigureSize_px = new Dimension((int)(subjectBounds_m.getWidth()*scale) + 2*borderThickness_px.width, - (int)(subjectBounds_m.getHeight()*scale) + 2*borderThickness_px.height); + final int desiredWidth = Math.max((int)this.visibleBounds_px.getWidth(), + (int)(subjectBounds_m.getWidth()*scale) + 2*borderThickness_px.width); + final int desiredHeight = Math.max((int)this.visibleBounds_px.getHeight(), + (int)(subjectBounds_m.getHeight()*scale) + 2*borderThickness_px.height); + + Dimension preferredFigureSize_px = new Dimension(desiredWidth, desiredHeight); setPreferredSize(preferredFigureSize_px); setMinimumSize(preferredFigureSize_px); @@ -165,8 +176,8 @@ public abstract class AbstractScaleFigure extends JPanel { log.debug(String.format("____ Updating %s to: %g user scale, %g overall scale", this.getClass().getSimpleName(), this.getAbsoluteScale(), this.scale)); updateSubjectDimensions(); - updateCanvasOrigin(); updateCanvasSize(); + updateCanvasOrigin(); updateTransform(); revalidate(); diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleScrollPane.java b/swing/src/net/sf/openrocket/gui/scalefigure/ScaleScrollPane.java index 7856083e6..3851771ff 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleScrollPane.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/ScaleScrollPane.java @@ -168,8 +168,9 @@ public class ScaleScrollPane extends JScrollPane } // if explicitly setting a zoom level, turn off fitting - this.fit = false; - figure.scaleTo(newScale); + this.fit = false; + Dimension view = viewport.getExtentSize(); + figure.scaleTo(newScale, view); revalidate(); } From 10a0cabd9877ec27263e28b32a73c84466a9cb37 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sun, 19 Aug 2018 13:18:34 +0200 Subject: [PATCH 26/47] [fixes #436] Rocket figures now center as desired. --- .../openrocket/gui/scalefigure/RocketFigure.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java index a21ee5300..d55946e36 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java @@ -466,16 +466,18 @@ public class RocketFigure extends AbstractScaleFigure { */ @Override protected void updateCanvasOrigin() { + final int subjectWidth = (int)(subjectBounds_m.getWidth()*scale); + final int subjectHeight = (int)(subjectBounds_m.getHeight()*scale); - final Dimension subjectArea = new Dimension((int)(subjectBounds_m.getWidth()*scale), - (int)(subjectBounds_m.getHeight()*scale)); - - final int newOriginY = borderThickness_px.height + (int)(subjectArea.getHeight() / 2); if (currentViewType == RocketPanel.VIEW_TYPE.BackView){ - int newOriginX = borderThickness_px.width + getWidth() / 2; + final int newOriginX = borderThickness_px.width + Math.max(getWidth(), subjectWidth + 2*borderThickness_px.width)/ 2; + final int newOriginY = borderThickness_px.height + getHeight() / 2; + originLocation_px = new Dimension(newOriginX, newOriginY); - }else { - int newOriginX = borderThickness_px.width + (getWidth() - subjectArea.width) / 2; + }else if (currentViewType == RocketPanel.VIEW_TYPE.SideView){ + final int newOriginX = borderThickness_px.width; + final int newOriginY = Math.max(getHeight(), subjectHeight + 2*borderThickness_px.height )/ 2; + originLocation_px = new Dimension(newOriginX, newOriginY); } } From 104b0ce74fe18298ab0d58f3b94d9ee9f04c4031 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sun, 19 Aug 2018 13:30:50 +0200 Subject: [PATCH 27/47] [fixes #425][fixes #440] FinPointFigure contents are bottom-aligned, properly sized. --- .../gui/scalefigure/FinPointFigure.java | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java index 810f56a11..9f8c1fb56 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java @@ -338,6 +338,7 @@ public class FinPointFigure extends AbstractScaleFigure { @Override protected void updateSubjectDimensions(){ + // update subject (i.e. Fin) bounds finBounds_m = new BoundingBox().update(finset.getFinPoints()).toRectangle(); // NOTE: the fin's forward root is pinned at 0,0 @@ -345,24 +346,32 @@ public class FinPointFigure extends AbstractScaleFigure { // update to bound the parent body: SymmetricComponent parent = (SymmetricComponent)this.finset.getParent(); - final double xParent = - finset.asPositionValue(AxialMethod.TOP); //<< in body frame + final double xFinFront = finset.asPositionValue(AxialMethod.TOP); + final double xParent = -xFinFront; final double yParent = -parent.getRadius(xParent); // from parent centerline to fin front. final double rParent = Math.max(parent.getForeRadius(), parent.getAftRadius()); mountBounds_m = new Rectangle2D.Double( xParent, yParent, parent.getLength(), rParent); - final double subjectWidth = Math.max( finBounds_m.getWidth(), parent.getLength()); + final double subjectWidth = Math.max( xFinFront + finBounds_m.getWidth(), parent.getLength()); final double subjectHeight = Math.max( 2*rParent, rParent + finBounds_m.getHeight()); subjectBounds_m = new Rectangle2D.Double( xParent, yParent, subjectWidth, subjectHeight); } @Override protected void updateCanvasOrigin() { - final SymmetricComponent parent = (SymmetricComponent)this.finset.getParent(); - final double rMaxParent = Math.max(parent.getForeRadius(), parent.getAftRadius()); + final int finHeight = (int)(finBounds_m.getHeight()*scale); + final int mountHeight = (int)(mountBounds_m.getHeight()*scale); + final int finFrontX = (int)(subjectBounds_m.getX()*scale); + final int subjectHeight = (int)(subjectBounds_m.getHeight()*scale); - // the negative sign is to compensate for the mount's negative location. - originLocation_px.width = borderThickness_px.width - (int)(mountBounds_m.getX()*scale); - originLocation_px.height = borderThickness_px.height + (int)(Math.max( rMaxParent, finBounds_m.getHeight())*scale); + // the negative sign is to compensate for the mount's negative location. + originLocation_px.width = borderThickness_px.width - finFrontX; + + if( visibleBounds_px.height > (subjectHeight+ 2*borderThickness_px.height)) { + originLocation_px.height = getHeight() - mountHeight - borderThickness_px.height; + }else { + originLocation_px.height = borderThickness_px.height + finHeight; + } } public void resetSelectedIndex() { From f3dbceba378f20b708d6f1796a6331f2ae83fdd5 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sun, 26 Aug 2018 17:27:41 -0400 Subject: [PATCH 28/47] [refactor] separated FinSet Tests into files corresponding to FinSet, TrapezoidalFinSet, and FreeformFinSet --- .../rocketcomponent/FinSetTest.java | 357 ------------------ .../rocketcomponent/FreeformFinSetTest.java | 272 +++++++++++++ .../rocketcomponent/TrapezoidFinSetTest.java | 137 +++++++ 3 files changed, 409 insertions(+), 357 deletions(-) create mode 100644 core/test/net/sf/openrocket/rocketcomponent/FreeformFinSetTest.java create mode 100644 core/test/net/sf/openrocket/rocketcomponent/TrapezoidFinSetTest.java diff --git a/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java b/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java index a4c3c3ba9..20c51442e 100644 --- a/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java +++ b/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java @@ -35,361 +35,4 @@ public class FinSetTest extends BaseTestCase { assertEquals(1, efins.getFinCount()); } - @Test - public void testTrapezoidCGComputation() { - - { - // This is a simple square fin with sides of 1.0. - TrapezoidFinSet fins = new TrapezoidFinSet(); - fins.setFinCount(1); - fins.setFinShape(1.0, 1.0, 0.0, 1.0, .005); - - Coordinate coords = fins.getCG(); - assertEquals(1.0, fins.getFinArea(), 0.001); - assertEquals(0.5, coords.x, 0.001); - assertEquals(0.5, coords.y, 0.001); - } - - { - // This is a trapezoid. Height 1, root 1, tip 1/2 no sweep. - // It can be decomposed into a rectangle followed by a triangle - // +---+ - // | \ - // | \ - // +------+ - TrapezoidFinSet fins = new TrapezoidFinSet(); - fins.setFinCount(1); - fins.setFinShape(1.0, 0.5, 0.0, 1.0, .005); - - Coordinate coords = fins.getCG(); - assertEquals(0.75, fins.getFinArea(), 0.001); - assertEquals(0.3889, coords.x, 0.001); - assertEquals(0.4444, coords.y, 0.001); - } - - } - - @Test - public void testInstancePoints_PI_2_BaseRotation() { - // This is a simple square fin with sides of 1.0. - TrapezoidFinSet fins = new TrapezoidFinSet(); - fins.setFinCount(4); - fins.setFinShape(1.0, 1.0, 0.0, 1.0, .005); - fins.setBaseRotation( Math.PI/2 ); - - BodyTube body = new BodyTube(1.0, 0.05 ); - body.addChild( fins ); - - Coordinate[] points = fins.getInstanceOffsets(); - - assertEquals( 0, points[0].x, 0.00001); - assertEquals( 0, points[0].y, 0.00001); - assertEquals( 0.05, points[0].z, 0.00001); - - assertEquals( 0, points[1].x, 0.00001); - assertEquals( -0.05, points[1].y, 0.00001); - assertEquals( 0, points[1].z, 0.00001); - } - - @Test - public void testInstancePoints_PI_4_BaseRotation() { - // This is a simple square fin with sides of 1.0. - TrapezoidFinSet fins = new TrapezoidFinSet(); - fins.setFinCount(4); - fins.setFinShape(1.0, 1.0, 0.0, 1.0, .005); - fins.setBaseRotation( Math.PI/4 ); - - BodyTube body = new BodyTube(1.0, 0.05 ); - body.addChild( fins ); - - Coordinate[] points = fins.getInstanceOffsets(); - - assertEquals( 0, points[0].x, 0.0001); - assertEquals( 0.03535, points[0].y, 0.0001); - assertEquals( 0.03535, points[0].z, 0.0001); - - assertEquals( 0, points[1].x, 0.0001); - assertEquals( -0.03535, points[1].y, 0.0001); - assertEquals( 0.03535, points[1].z, 0.0001); - } - - - @Test - public void testInstanceAngles_zeroBaseRotation() { - // This is a simple square fin with sides of 1.0. - TrapezoidFinSet fins = new TrapezoidFinSet(); - fins.setFinCount(4); - fins.setFinShape(1.0, 1.0, 0.0, 1.0, .005); - fins.setBaseRotation( 0.0 ); - - double[] angles = fins.getInstanceAngles(); - - assertEquals( angles[0], 0, 0.000001 ); - assertEquals( angles[1], Math.PI/2, 0.000001 ); - assertEquals( angles[2], Math.PI, 0.000001 ); - assertEquals( angles[3], 1.5*Math.PI, 0.000001 ); - } - - @Test - public void testInstanceAngles_90_BaseRotation() { - // This is a simple square fin with sides of 1.0. - TrapezoidFinSet fins = new TrapezoidFinSet(); - fins.setFinCount(4); - fins.setFinShape(1.0, 1.0, 0.0, 1.0, .005); - fins.setBaseRotation( Math.PI/2 ); - - double[] angles = fins.getInstanceAngles(); - - assertEquals( angles[0], Math.PI/2, 0.000001 ); - assertEquals( angles[1], Math.PI, 0.000001 ); - assertEquals( angles[2], 1.5*Math.PI, 0.000001 ); - assertEquals( angles[3], 0, 0.000001 ); - } - - @Test - public void testFreeformCGComputation() throws Exception { - - { - // This is a trapezoid. Height 1, root 1, tip 1/2 no sweep. - // It can be decomposed into a rectangle followed by a triangle - // +---+ - // | \ - // | \ - // +------+ - FreeformFinSet fins = new FreeformFinSet(); - fins.setFinCount(1); - Coordinate[] points = new Coordinate[] { - new Coordinate(0, 0), - new Coordinate(0, 1), - new Coordinate(.5, 1), - new Coordinate(1, 0) - }; - fins.setPoints(points); - Coordinate coords = fins.getCG(); - assertEquals(0.75, fins.getFinArea(), 0.001); - assertEquals(0.3889, coords.x, 0.001); - assertEquals(0.4444, coords.y, 0.001); - } - - { - // This is the same trapezoid as previous free form, but it has - // some extra points along the lines. - FreeformFinSet fins = new FreeformFinSet(); - fins.setFinCount(1); - Coordinate[] points = new Coordinate[] { - new Coordinate(0, 0), - new Coordinate(0, .5), - new Coordinate(0, 1), - new Coordinate(.25, 1), - new Coordinate(.5, 1), - new Coordinate(.75, .5), - new Coordinate(1, 0) - }; - fins.setPoints(points); - Coordinate coords = fins.getCG(); - assertEquals(0.75, fins.getFinArea(), 0.001); - assertEquals(0.3889, coords.x, 0.001); - assertEquals(0.4444, coords.y, 0.001); - } - - { - // This is the same trapezoid as previous free form, but it has - // some extra points which are very close to previous points. - // in particular for points 0 & 1, - // y0 + y1 is very small. - FreeformFinSet fins = new FreeformFinSet(); - fins.setFinCount(1); - Coordinate[] points = new Coordinate[] { - new Coordinate(0, 0), - new Coordinate(0, 1E-15), - new Coordinate(0, 1), - new Coordinate(1E-15, 1), - new Coordinate(.5, 1), - new Coordinate(.5, 1 - 1E-15), - new Coordinate(1, 1E-15), - new Coordinate(1, 0) - }; - fins.setPoints(points); - Coordinate coords = fins.getCG(); - assertEquals(0.75, fins.getFinArea(), 0.001); - assertEquals(0.3889, coords.x, 0.001); - assertEquals(0.4444, coords.y, 0.001); - } - - } - - @Test - public void testWildmanVindicatorShape() throws Exception { - // This fin shape is similar to the aft fins on the Wildman Vindicator. - // A user noticed that if the y values are similar but not equal, - // the compuation of CP was incorrect because of numerical instability. - // - // +-----------------+ - // \ \ - // \ \ - // + \ - // / \ - // +---------------------+ - // - FreeformFinSet fins = new FreeformFinSet(); - fins.setFinCount(1); - Coordinate[] points = new Coordinate[] { - new Coordinate(0, 0), - new Coordinate(0.02143125, 0.01143), - new Coordinate(0.009524999999999999, 0.032543749999999996), - new Coordinate(0.041275, 0.032537399999999994), - new Coordinate(0.066675, 0) - }; - fins.setPoints(points); - Coordinate coords = fins.getCG(); - assertEquals(0.00130, fins.getFinArea(), 0.00001); - assertEquals(0.03423, coords.x, 0.00001); - assertEquals(0.01427, coords.y, 0.00001); - - BodyTube bt = new BodyTube(); - bt.addChild(fins); - FinSetCalc calc = new FinSetCalc(fins); - FlightConditions conditions = new FlightConditions(null); - AerodynamicForces forces = new AerodynamicForces(); - WarningSet warnings = new WarningSet(); - calc.calculateNonaxialForces(conditions, forces, warnings); - //System.out.println(forces); - assertEquals(0.023409, forces.getCP().x, 0.0001); - } - - @Test - public void testFreeFormCGWithNegativeY() throws Exception { - // This particular fin shape is currently not allowed in OR since the y values are negative - // however, it is possible to convert RockSim files and end up with fins which - // have negative y values. - - // A user submitted an ork file which could not be simulated because the fin - // was constructed on a tail cone. It so happened that for one pair of points - // y_n = - y_(n+1) which caused a divide by zero and resulted in CGx = NaN. - - // This Fin set is constructed to have the same problem. It is a square and rectagle - // where the two trailing edge corners of the rectangle satisfy y_0 = -y_1 - // - // +---------+ - // | | - // | | - // +----+ | - // | | - // | | - // +----+ - - FreeformFinSet fins = new FreeformFinSet(); - fins.setFinCount(1); - Coordinate[] points = new Coordinate[] { - new Coordinate(0, 0), - new Coordinate(0, 1), - new Coordinate(2, 1), - new Coordinate(2, -1), - new Coordinate(1, -1), - new Coordinate(1, 0) - }; - fins.setPoints(points); - Coordinate coords = fins.getCG(); - assertEquals(3.0, fins.getFinArea(), 0.001); - assertEquals(3.5 / 3.0, coords.x, 0.001); - assertEquals(0.5 / 3.0, coords.y, 0.001); - - } - - - @Test - public void testFreeformConvert() { - testFreeformConvert(new TrapezoidFinSet()); - testFreeformConvert(new EllipticalFinSet()); - testFreeformConvert(new FreeformFinSet()); - } - - - private void testFreeformConvert(FinSet fin) { - FreeformFinSet converted; - Material mat = Material.newMaterial(Type.BULK, "foo", 0.1, true); - - fin.setBaseRotation(1.1); - fin.setCantAngle(0.001); - fin.setCGOverridden(true); - fin.setColor(Color.BLACK); - fin.setComment("cmt"); - fin.setCrossSection(CrossSection.ROUNDED); - fin.setFinCount(5); - fin.setFinish(Finish.ROUGH); - fin.setLineStyle(LineStyle.DASHDOT); - fin.setMassOverridden(true); - fin.setMaterial(mat); - fin.setOverrideCGX(0.012); - fin.setOverrideMass(0.0123); - fin.setOverrideSubcomponents(true); - fin.setAxialOffset(0.1); - fin.setAxialMethod(AxialMethod.ABSOLUTE); - fin.setTabHeight(0.01); - fin.setTabLength(0.02); - fin.setTabRelativePosition(TabRelativePosition.END); - fin.setTabShift(0.015); - fin.setThickness(0.005); - - - converted = FreeformFinSet.convertFinSet((FinSet) fin.copy()); - - /// what do we want to ACTUALLY compare? - // ComponentCompare.assertSimilarity(fin, converted, true); // deprecated; removed - - - assertEquals(converted.getComponentName(), converted.getName()); - - - // Create test rocket - Rocket rocket = new Rocket(); - AxialStage stage = new AxialStage(); - BodyTube body = new BodyTube(); - - rocket.addChild(stage); - stage.addChild(body); - body.addChild(fin); - rocket.enableEvents(); - - Listener l1 = new Listener("l1"); - rocket.addComponentChangeListener(l1); - - fin.setName("Custom name"); - assertEquals("FinSet listener has not been notified: ", l1.changed, true); - assertEquals(ComponentChangeEvent.NONFUNCTIONAL_CHANGE, l1.changetype); - - - // Create copy - RocketComponent rocketcopy = rocket.copy(); - - Listener l2 = new Listener("l2"); - rocketcopy.addComponentChangeListener(l2); - - FinSet fincopy = (FinSet) rocketcopy.getChild(0).getChild(0).getChild(0); - FreeformFinSet.convertFinSet(fincopy); - - assertTrue("FinSet listener is changed", l2.changed); - assertEquals(ComponentChangeEvent.TREE_CHANGE, - l2.changetype & ComponentChangeEvent.TREE_CHANGE); - - } - - - private static class Listener implements ComponentChangeListener { - private boolean changed = false; - private int changetype = 0; - private final String name; - - public Listener(String name) { - this.name = name; - } - - @Override - public void componentChanged(ComponentChangeEvent e) { - assertFalse("Ensuring listener " + name + " has not been called.", changed); - changed = true; - changetype = e.getType(); - } - } - } diff --git a/core/test/net/sf/openrocket/rocketcomponent/FreeformFinSetTest.java b/core/test/net/sf/openrocket/rocketcomponent/FreeformFinSetTest.java new file mode 100644 index 000000000..7df8e3039 --- /dev/null +++ b/core/test/net/sf/openrocket/rocketcomponent/FreeformFinSetTest.java @@ -0,0 +1,272 @@ +package net.sf.openrocket.rocketcomponent; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.awt.geom.Point2D; + +import org.junit.Test; + +import net.sf.openrocket.aerodynamics.AerodynamicForces; +import net.sf.openrocket.aerodynamics.FlightConditions; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.aerodynamics.barrowman.FinSetCalc; +import net.sf.openrocket.material.Material; +import net.sf.openrocket.material.Material.Type; +import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish; +import net.sf.openrocket.rocketcomponent.FinSet.CrossSection; +import net.sf.openrocket.rocketcomponent.FinSet.TabRelativePosition; +import net.sf.openrocket.rocketcomponent.position.*; +import net.sf.openrocket.util.Color; +import net.sf.openrocket.util.Coordinate; +import net.sf.openrocket.util.LineStyle; +import net.sf.openrocket.util.BaseTestCase.BaseTestCase; + +public class FreeformFinSetTest extends BaseTestCase { + + @Test + public void testFreeformCGComputationSimpleTrapezoid() throws Exception { + // This is a trapezoid. Height 1, root 1, tip 1/2 no sweep. + // It can be decomposed into a rectangle followed by a triangle + // +---+ + // | \ + // | \ + // +------+ + FreeformFinSet fins = new FreeformFinSet(); + fins.setFinCount(1); + Coordinate[] points = new Coordinate[] { + new Coordinate(0, 0), + new Coordinate(0, 1), + new Coordinate(.5, 1), + new Coordinate(1, 0) + }; + fins.setPoints(points); + Coordinate coords = fins.getCG(); + assertEquals(0.75, fins.getFinArea(), 0.001); + assertEquals(0.3889, coords.x, 0.001); + assertEquals(0.4444, coords.y, 0.001); + } + + @Test + public void testFreeformCGComputationTrapezoidExtraPoints() throws Exception { + // This is the same trapezoid as previous free form, but it has + // some extra points along the lines. + FreeformFinSet fins = new FreeformFinSet(); + fins.setFinCount(1); + Coordinate[] points = new Coordinate[] { + new Coordinate(0, 0), + new Coordinate(0, .5), + new Coordinate(0, 1), + new Coordinate(.25, 1), + new Coordinate(.5, 1), + new Coordinate(.75, .5), + new Coordinate(1, 0) + }; + fins.setPoints(points); + Coordinate coords = fins.getCG(); + assertEquals(0.75, fins.getFinArea(), 0.001); + assertEquals(0.3889, coords.x, 0.001); + assertEquals(0.4444, coords.y, 0.001); + } + + @Test + public void testFreeformCGComputationAdjacentPoinst() throws Exception { + // This is the same trapezoid as previous free form, but it has + // some extra points which are very close to previous points. + // in particular for points 0 & 1, + // y0 + y1 is very small. + FreeformFinSet fins = new FreeformFinSet(); + fins.setFinCount(1); + Coordinate[] points = new Coordinate[] { + new Coordinate(0, 0), + new Coordinate(0, 1E-15), + new Coordinate(0, 1), + new Coordinate(1E-15, 1), + new Coordinate(.5, 1), + new Coordinate(.5, 1 - 1E-15), + new Coordinate(1, 1E-15), + new Coordinate(1, 0) + }; + fins.setPoints(points); + Coordinate coords = fins.getCG(); + assertEquals(0.75, fins.getFinArea(), 0.001); + assertEquals(0.3889, coords.x, 0.001); + assertEquals(0.4444, coords.y, 0.001); + } + + @Test + public void testWildmanVindicatorShape() throws Exception { + // This fin shape is similar to the aft fins on the Wildman Vindicator. + // A user noticed that if the y values are similar but not equal, + // the compuation of CP was incorrect because of numerical instability. + // + // +-----------------+ + // \ \ + // \ \ + // + \ + // / \ + // +---------------------+ + // + FreeformFinSet fins = new FreeformFinSet(); + fins.setFinCount(1); + Coordinate[] points = new Coordinate[] { + new Coordinate(0, 0), + new Coordinate(0.02143125, 0.01143), + new Coordinate(0.009524999999999999, 0.032543749999999996), + new Coordinate(0.041275, 0.032537399999999994), + new Coordinate(0.066675, 0) + }; + fins.setPoints(points); + Coordinate coords = fins.getCG(); + assertEquals(0.00130, fins.getFinArea(), 0.00001); + assertEquals(0.03423, coords.x, 0.00001); + assertEquals(0.01427, coords.y, 0.00001); + + BodyTube bt = new BodyTube(); + bt.addChild(fins); + FinSetCalc calc = new FinSetCalc(fins); + FlightConditions conditions = new FlightConditions(null); + AerodynamicForces forces = new AerodynamicForces(); + WarningSet warnings = new WarningSet(); + calc.calculateNonaxialForces(conditions, forces, warnings); + //System.out.println(forces); + assertEquals(0.023409, forces.getCP().x, 0.0001); + } + + @Test + public void testFreeFormCGWithNegativeY() throws Exception { + // This particular fin shape is currently not allowed in OR since the y values are negative + // however, it is possible to convert RockSim files and end up with fins which + // have negative y values. + + // A user submitted an ork file which could not be simulated because the fin + // was constructed on a tail cone. It so happened that for one pair of points + // y_n = - y_(n+1) which caused a divide by zero and resulted in CGx = NaN. + + // This Fin set is constructed to have the same problem. It is a square and rectagle + // where the two trailing edge corners of the rectangle satisfy y_0 = -y_1 + // + // +---------+ + // | | + // | | + // +----+ | + // | | + // | | + // +----+ + + FreeformFinSet fins = new FreeformFinSet(); + fins.setFinCount(1); + Coordinate[] points = new Coordinate[] { + new Coordinate(0, 0), + new Coordinate(0, 1), + new Coordinate(2, 1), + new Coordinate(2, -1), + new Coordinate(1, -1), + new Coordinate(1, 0) + }; + fins.setPoints(points); + Coordinate coords = fins.getCG(); + assertEquals(3.0, fins.getFinArea(), 0.001); + assertEquals(3.5 / 3.0, coords.x, 0.001); + assertEquals(0.5 / 3.0, coords.y, 0.001); + + } + + + @Test + public void testFreeformConvert() { + testFreeformConvert(new TrapezoidFinSet()); + testFreeformConvert(new EllipticalFinSet()); + testFreeformConvert(new FreeformFinSet()); + } + + + private void testFreeformConvert(FinSet fin) { + FreeformFinSet converted; + Material mat = Material.newMaterial(Type.BULK, "foo", 0.1, true); + + fin.setBaseRotation(1.1); + fin.setCantAngle(0.001); + fin.setCGOverridden(true); + fin.setColor(Color.BLACK); + fin.setComment("cmt"); + fin.setCrossSection(CrossSection.ROUNDED); + fin.setFinCount(5); + fin.setFinish(Finish.ROUGH); + fin.setLineStyle(LineStyle.DASHDOT); + fin.setMassOverridden(true); + fin.setMaterial(mat); + fin.setOverrideCGX(0.012); + fin.setOverrideMass(0.0123); + fin.setOverrideSubcomponents(true); + fin.setAxialOffset(0.1); + fin.setAxialMethod(AxialMethod.ABSOLUTE); + fin.setTabHeight(0.01); + fin.setTabLength(0.02); + fin.setTabRelativePosition(TabRelativePosition.END); + fin.setTabShift(0.015); + fin.setThickness(0.005); + + + converted = FreeformFinSet.convertFinSet((FinSet) fin.copy()); + + /// what do we want to ACTUALLY compare? + // ComponentCompare.assertSimilarity(fin, converted, true); // deprecated; removed + + + assertEquals(converted.getComponentName(), converted.getName()); + + + // Create test rocket + Rocket rocket = new Rocket(); + AxialStage stage = new AxialStage(); + BodyTube body = new BodyTube(); + + rocket.addChild(stage); + stage.addChild(body); + body.addChild(fin); + rocket.enableEvents(); + + Listener l1 = new Listener("l1"); + rocket.addComponentChangeListener(l1); + + fin.setName("Custom name"); + assertEquals("FinSet listener has not been notified: ", l1.changed, true); + assertEquals(ComponentChangeEvent.NONFUNCTIONAL_CHANGE, l1.changetype); + + + // Create copy + RocketComponent rocketcopy = rocket.copy(); + + Listener l2 = new Listener("l2"); + rocketcopy.addComponentChangeListener(l2); + + FinSet fincopy = (FinSet) rocketcopy.getChild(0).getChild(0).getChild(0); + FreeformFinSet.convertFinSet(fincopy); + + assertTrue("FinSet listener is changed", l2.changed); + assertEquals(ComponentChangeEvent.TREE_CHANGE, + l2.changetype & ComponentChangeEvent.TREE_CHANGE); + + } + + + private static class Listener implements ComponentChangeListener { + private boolean changed = false; + private int changetype = 0; + private final String name; + + public Listener(String name) { + this.name = name; + } + + @Override + public void componentChanged(ComponentChangeEvent e) { + assertFalse("Ensuring listener " + name + " has not been called.", changed); + changed = true; + changetype = e.getType(); + } + } + +} diff --git a/core/test/net/sf/openrocket/rocketcomponent/TrapezoidFinSetTest.java b/core/test/net/sf/openrocket/rocketcomponent/TrapezoidFinSetTest.java new file mode 100644 index 000000000..6eda63d65 --- /dev/null +++ b/core/test/net/sf/openrocket/rocketcomponent/TrapezoidFinSetTest.java @@ -0,0 +1,137 @@ +package net.sf.openrocket.rocketcomponent; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import net.sf.openrocket.aerodynamics.AerodynamicForces; +import net.sf.openrocket.aerodynamics.FlightConditions; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.aerodynamics.barrowman.FinSetCalc; +import net.sf.openrocket.material.Material; +import net.sf.openrocket.material.Material.Type; +import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish; +import net.sf.openrocket.rocketcomponent.FinSet.CrossSection; +import net.sf.openrocket.rocketcomponent.FinSet.TabRelativePosition; +import net.sf.openrocket.rocketcomponent.position.*; +import net.sf.openrocket.util.Color; +import net.sf.openrocket.util.Coordinate; +import net.sf.openrocket.util.LineStyle; +import net.sf.openrocket.util.BaseTestCase.BaseTestCase; + +public class TrapezoidFinSetTest extends BaseTestCase { + + @Test + public void testTrapezoidCGComputation() { + + { + // This is a simple square fin with sides of 1.0. + TrapezoidFinSet fins = new TrapezoidFinSet(); + fins.setFinCount(1); + fins.setFinShape(1.0, 1.0, 0.0, 1.0, .005); + + Coordinate coords = fins.getCG(); + assertEquals(1.0, fins.getFinArea(), 0.001); + assertEquals(0.5, coords.x, 0.001); + assertEquals(0.5, coords.y, 0.001); + } + + { + // This is a trapezoid. Height 1, root 1, tip 1/2 no sweep. + // It can be decomposed into a rectangle followed by a triangle + // +---+ + // | \ + // | \ + // +------+ + TrapezoidFinSet fins = new TrapezoidFinSet(); + fins.setFinCount(1); + fins.setFinShape(1.0, 0.5, 0.0, 1.0, .005); + + Coordinate coords = fins.getCG(); + assertEquals(0.75, fins.getFinArea(), 0.001); + assertEquals(0.3889, coords.x, 0.001); + assertEquals(0.4444, coords.y, 0.001); + } + + } + + @Test + public void testInstancePoints_PI_2_BaseRotation() { + // This is a simple square fin with sides of 1.0. + TrapezoidFinSet fins = new TrapezoidFinSet(); + fins.setFinCount(4); + fins.setFinShape(1.0, 1.0, 0.0, 1.0, .005); + fins.setBaseRotation( Math.PI/2 ); + + BodyTube body = new BodyTube(1.0, 0.05 ); + body.addChild( fins ); + + Coordinate[] points = fins.getInstanceOffsets(); + + assertEquals( 0, points[0].x, 0.00001); + assertEquals( 0, points[0].y, 0.00001); + assertEquals( 0.05, points[0].z, 0.00001); + + assertEquals( 0, points[1].x, 0.00001); + assertEquals( -0.05, points[1].y, 0.00001); + assertEquals( 0, points[1].z, 0.00001); + } + + @Test + public void testInstancePoints_PI_4_BaseRotation() { + // This is a simple square fin with sides of 1.0. + TrapezoidFinSet fins = new TrapezoidFinSet(); + fins.setFinCount(4); + fins.setFinShape(1.0, 1.0, 0.0, 1.0, .005); + fins.setBaseRotation( Math.PI/4 ); + + BodyTube body = new BodyTube(1.0, 0.05 ); + body.addChild( fins ); + + Coordinate[] points = fins.getInstanceOffsets(); + + assertEquals( 0, points[0].x, 0.0001); + assertEquals( 0.03535, points[0].y, 0.0001); + assertEquals( 0.03535, points[0].z, 0.0001); + + assertEquals( 0, points[1].x, 0.0001); + assertEquals( -0.03535, points[1].y, 0.0001); + assertEquals( 0.03535, points[1].z, 0.0001); + } + + + @Test + public void testInstanceAngles_zeroBaseRotation() { + // This is a simple square fin with sides of 1.0. + TrapezoidFinSet fins = new TrapezoidFinSet(); + fins.setFinCount(4); + fins.setFinShape(1.0, 1.0, 0.0, 1.0, .005); + fins.setBaseRotation( 0.0 ); + + double[] angles = fins.getInstanceAngles(); + + assertEquals( angles[0], 0, 0.000001 ); + assertEquals( angles[1], Math.PI/2, 0.000001 ); + assertEquals( angles[2], Math.PI, 0.000001 ); + assertEquals( angles[3], 1.5*Math.PI, 0.000001 ); + } + + @Test + public void testInstanceAngles_90_BaseRotation() { + // This is a simple square fin with sides of 1.0. + TrapezoidFinSet fins = new TrapezoidFinSet(); + fins.setFinCount(4); + fins.setFinShape(1.0, 1.0, 0.0, 1.0, .005); + fins.setBaseRotation( Math.PI/2 ); + + double[] angles = fins.getInstanceAngles(); + + assertEquals( angles[0], Math.PI/2, 0.000001 ); + assertEquals( angles[1], Math.PI, 0.000001 ); + assertEquals( angles[2], 1.5*Math.PI, 0.000001 ); + assertEquals( angles[3], 0, 0.000001 ); + } + +} From e4b6b25a8b3c41a844066cc54a8fa3a9ee2485ca Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sun, 26 Aug 2018 17:31:46 -0400 Subject: [PATCH 29/47] [fixes #419] Adding new points to FreeformFins are now placed at the mouse cursor --- .../rocketcomponent/FreeformFinSet.java | 15 +++++------ .../rocketcomponent/FreeformFinSetTest.java | 27 +++++++++++++++++++ .../configdialog/FreeformFinSetConfig.java | 2 +- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/core/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java b/core/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java index 468cca53a..36dfc59ab 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java @@ -1,5 +1,6 @@ package net.sf.openrocket.rocketcomponent; +import java.awt.geom.Point2D; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -128,16 +129,12 @@ public class FreeformFinSet extends FinSet { * The point is placed at the midpoint of the current segment. * * @param index the fin point before which to add the new point. + * @param point the target location to create the new point at */ - public void addPoint(int index) { - double x0, y0, x1, y1; - - x0 = points.get(index - 1).x; - y0 = points.get(index - 1).y; - x1 = points.get(index).x; - y1 = points.get(index).y; - - points.add(index, new Coordinate((x0 + x1) / 2, (y0 + y1) / 2)); + public void addPoint(int index, Point2D.Double location) { + // new method: add new point at closest point + points.add(index, new Coordinate(location.x, location.y)); + // adding a point within the segment affects neither mass nor aerodynamics fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } diff --git a/core/test/net/sf/openrocket/rocketcomponent/FreeformFinSetTest.java b/core/test/net/sf/openrocket/rocketcomponent/FreeformFinSetTest.java index 7df8e3039..803feb4f5 100644 --- a/core/test/net/sf/openrocket/rocketcomponent/FreeformFinSetTest.java +++ b/core/test/net/sf/openrocket/rocketcomponent/FreeformFinSetTest.java @@ -94,6 +94,33 @@ public class FreeformFinSetTest extends BaseTestCase { assertEquals(0.3889, coords.x, 0.001); assertEquals(0.4444, coords.y, 0.001); } + + @Test + public void testFreeformFinAddPoint() throws Exception { + FreeformFinSet fin = new FreeformFinSet(); + fin.setFinCount(1); + fin.setFinCount(1); + Coordinate[] points = new Coordinate[] { + new Coordinate(0, 0), + new Coordinate(0.5, 1.0), + new Coordinate(1.0, 1.0), + new Coordinate(1, 0) + }; + fin.setPoints(points); + assertEquals(4, fin.getPointCount()); + + // +--+ + // / |x + // / | + // +=====+ + Point2D.Double toAdd = new Point2D.Double(1.01, 0.8); + fin.addPoint(3, toAdd); + + assertEquals(5, fin.getPointCount()); + final Coordinate added = fin.getFinPoints()[3]; + assertEquals(1.1,added.x, 0.1); + assertEquals(0.8, added.y, 0.1); + } @Test public void testWildmanVindicatorShape() throws Exception { diff --git a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java index 110c9d910..cd0fe53f5 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java @@ -407,7 +407,7 @@ public class FreeformFinSetConfig extends FinSetConfig { final int segmentIndex = getSegment(event); if (segmentIndex >= 0) { Point2D.Double point = getCoordinates(event); - finset.addPoint(segmentIndex ); + finset.addPoint(segmentIndex, point); try { finset.setPoint(dragIndex, point.x, point.y); From d2cdea21131c85d2550e9e29dc71f0411d224bdd Mon Sep 17 00:00:00 2001 From: Joe Pfeiffer Date: Sun, 26 Aug 2018 16:32:44 -0600 Subject: [PATCH 30/47] Little bit more massaging for clarity (replace avgImpulse with impulse) --- core/src/net/sf/openrocket/motor/ThrustCurveMotor.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java b/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java index 8bb8afddd..ec5f82605 100644 --- a/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java +++ b/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java @@ -318,26 +318,26 @@ public class ThrustCurveMotor implements Motor, Comparable, Se return (startThrust + endThrust) / 2.0; } - double avgImpulse = 0.0; + double impulse = 0.0; // portion from startTime through time[timeIndex+1] double startThrust = MathUtil.map(startTime, time[timeIndex], time[timeIndex+1], thrust[timeIndex], thrust[timeIndex+1]); - avgImpulse = (time[timeIndex+1] - startTime) * (startThrust + thrust[timeIndex+1]) / 2.0; + impulse = (time[timeIndex+1] - startTime) * (startThrust + thrust[timeIndex+1]) / 2.0; // Now add the whole steps; timeIndex++; while ( timeIndex < time.length -1 && endTime >= time[timeIndex+1] ) { - avgImpulse += (time[timeIndex+1] - time[timeIndex]) * (thrust[timeIndex] + thrust[timeIndex+1]) / 2.0; + impulse += (time[timeIndex+1] - time[timeIndex]) * (thrust[timeIndex] + thrust[timeIndex+1]) / 2.0; timeIndex++; } // Now add the bit after the last time index if ( timeIndex < time.length -1 ) { double endThrust = MathUtil.map( endTime, time[timeIndex], time[timeIndex+1], thrust[timeIndex], thrust[timeIndex+1]); - avgImpulse += ((thrust[timeIndex] + endThrust) / 2.0) * (endTime - time[timeIndex]); + impulse += ((thrust[timeIndex] + endThrust) / 2.0) * (endTime - time[timeIndex]); } - return avgImpulse / (endTime - startTime); + return impulse / (endTime - startTime); } @Override From 3153ccf545a8efa24eab3155ab127f2eab139dc9 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sun, 26 Aug 2018 18:39:59 -0400 Subject: [PATCH 31/47] [fixes #426] reworks FreeformFinSet Selected point display... it is now a second, expanded, different colored box. --- .../gui/scalefigure/FinPointFigure.java | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java index 9f8c1fb56..46afee80a 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java @@ -42,8 +42,9 @@ public class FinPointFigure extends AbstractScaleFigure { // the size of the boxes around each fin point vertex private static final float BOX_WIDTH_PIXELS = 12; - private static final float SELECTED_BOX_WIDTH_PIXELS = 16; - + private static final float SELECTED_BOX_WIDTH_PIXELS = BOX_WIDTH_PIXELS + 4; + private static final Color POINT_COLOR = new Color(100, 100, 100); + private static final Color SELECTED_POINT_COLOR = new Color(200, 0, 0); private static final double MINOR_TICKS = 0.05; private static final double MAJOR_TICKS = 0.1; @@ -230,23 +231,31 @@ public class FinPointFigure extends AbstractScaleFigure { // Fin point boxes final float boxWidth = (float) (BOX_WIDTH_PIXELS / scale ); final float boxHalfWidth = boxWidth/2; - final float selBoxWidth = (float) (SELECTED_BOX_WIDTH_PIXELS / scale ); - final float selBoxHalfWidth = boxWidth/2; - + final float boxEdgeWidth_m = (float) ( LINE_WIDTH_PIXELS / scale ); g2.setStroke(new BasicStroke( boxEdgeWidth_m, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); - g2.setColor(new Color(150, 0, 0)); + g2.setColor(POINT_COLOR); finPointHandles = new Rectangle2D.Double[ drawPoints.length]; for (int currentIndex = 0; currentIndex < drawPoints.length; currentIndex++) { Coordinate c = drawPoints[currentIndex]; if( currentIndex == selectedIndex ) { - finPointHandles[currentIndex] = new Rectangle2D.Double(c.x - selBoxHalfWidth, c.y - selBoxHalfWidth, selBoxWidth, selBoxWidth); - } else { - // normal boxes - finPointHandles[currentIndex] = new Rectangle2D.Double(c.x - boxHalfWidth, c.y - boxHalfWidth, boxWidth, boxWidth); - } + final float selBoxWidth = (float) (SELECTED_BOX_WIDTH_PIXELS / scale ); + final float selBoxHalfWidth = selBoxWidth/2; + + final Rectangle2D.Double selectedPointHighlight = new Rectangle2D.Double(c.x - selBoxHalfWidth, c.y - selBoxHalfWidth, selBoxWidth, selBoxWidth); + + // switch to the highlight color + g2.setColor(SELECTED_POINT_COLOR); + g2.draw(selectedPointHighlight); + + // reset to the normal color + g2.setColor(POINT_COLOR); + } + + // normal boxes + finPointHandles[currentIndex] = new Rectangle2D.Double(c.x - boxHalfWidth, c.y - boxHalfWidth, boxWidth, boxWidth); g2.draw(finPointHandles[currentIndex]); } From 7a04bd567c9a37ad825b52bbe21da47a8faf667e Mon Sep 17 00:00:00 2001 From: Joe Pfeiffer Date: Mon, 27 Aug 2018 09:16:16 -0600 Subject: [PATCH 32/47] missed reversing the operands in the calculation of last bit of impulse --- core/src/net/sf/openrocket/motor/ThrustCurveMotor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java b/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java index ec5f82605..3e851f260 100644 --- a/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java +++ b/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java @@ -334,7 +334,7 @@ public class ThrustCurveMotor implements Motor, Comparable, Se // Now add the bit after the last time index if ( timeIndex < time.length -1 ) { double endThrust = MathUtil.map( endTime, time[timeIndex], time[timeIndex+1], thrust[timeIndex], thrust[timeIndex+1]); - impulse += ((thrust[timeIndex] + endThrust) / 2.0) * (endTime - time[timeIndex]); + impulse += (endTime - time[timeIndex]) * (thrust[timeIndex] + endThrust) / 2.0; } return impulse / (endTime - startTime); From b63616b1be33a059a8cf8d10c7a74a6abef7f99d Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sun, 11 Feb 2018 10:04:50 -0500 Subject: [PATCH 33/47] [build] Updated dependencies for running from intellij --- core/OpenRocket Core.iml | 13 +---- lib-test/OpenRocket Test Libraries.iml | 4 +- swing/OpenRocket Swing.iml | 74 +++++++++++++++++++++++++- 3 files changed, 76 insertions(+), 15 deletions(-) diff --git a/core/OpenRocket Core.iml b/core/OpenRocket Core.iml index e1e2c5ccd..eca58a704 100644 --- a/core/OpenRocket Core.iml +++ b/core/OpenRocket Core.iml @@ -23,13 +23,13 @@ - + - + @@ -177,14 +177,5 @@ - - - - - - - - - \ No newline at end of file diff --git a/lib-test/OpenRocket Test Libraries.iml b/lib-test/OpenRocket Test Libraries.iml index 82b2558c0..b9af6028c 100644 --- a/lib-test/OpenRocket Test Libraries.iml +++ b/lib-test/OpenRocket Test Libraries.iml @@ -10,11 +10,12 @@ - + + @@ -78,6 +79,5 @@ - \ No newline at end of file diff --git a/swing/OpenRocket Swing.iml b/swing/OpenRocket Swing.iml index 27d5f85ad..95b144e5f 100644 --- a/swing/OpenRocket Swing.iml +++ b/swing/OpenRocket Swing.iml @@ -24,7 +24,7 @@ - + @@ -207,6 +207,76 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 9f8e57e36ccd2796d251994ac92e5d9249caa23d Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sun, 18 Feb 2018 17:01:09 -0500 Subject: [PATCH 34/47] [feat] added shared build configurations for Intellij at .idea/runConfigurations/*" --- .idea/runConfigurations/All_tests.xml | 25 +++++++++++++++++++ .../runConfigurations/Openrocket_Startup.xml | 10 ++++++++ 2 files changed, 35 insertions(+) create mode 100644 .idea/runConfigurations/All_tests.xml create mode 100644 .idea/runConfigurations/Openrocket_Startup.xml diff --git a/.idea/runConfigurations/All_tests.xml b/.idea/runConfigurations/All_tests.xml new file mode 100644 index 000000000..26cc40265 --- /dev/null +++ b/.idea/runConfigurations/All_tests.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Openrocket_Startup.xml b/.idea/runConfigurations/Openrocket_Startup.xml new file mode 100644 index 000000000..5010b9ab9 --- /dev/null +++ b/.idea/runConfigurations/Openrocket_Startup.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file From 43b8ec3fa5691adfbb2a215ce76b6b71eae30a4e Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sun, 25 Feb 2018 13:57:12 -0500 Subject: [PATCH 35/47] [fix][config] rename Run Target Configurations --- .../{Openrocket_Startup.xml => Openrocket_UI_Jar.xml} | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) rename .idea/runConfigurations/{Openrocket_Startup.xml => Openrocket_UI_Jar.xml} (54%) diff --git a/.idea/runConfigurations/Openrocket_Startup.xml b/.idea/runConfigurations/Openrocket_UI_Jar.xml similarity index 54% rename from .idea/runConfigurations/Openrocket_Startup.xml rename to .idea/runConfigurations/Openrocket_UI_Jar.xml index 5010b9ab9..1338298bc 100644 --- a/.idea/runConfigurations/Openrocket_Startup.xml +++ b/.idea/runConfigurations/Openrocket_UI_Jar.xml @@ -1,10 +1,14 @@ - + - \ No newline at end of file From 4487e457de928a94264fffe68659ebc2473c383d Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sun, 25 Feb 2018 14:00:49 -0500 Subject: [PATCH 36/47] [build] added jar artifact for IDEA Intellij build --- .idea/artifacts/openrocket_jar.xml | 39 ++++++++++++++++++++++++++++++ core/src/META-INF/MANIFEST.MF | 3 +++ 2 files changed, 42 insertions(+) create mode 100644 .idea/artifacts/openrocket_jar.xml create mode 100644 core/src/META-INF/MANIFEST.MF diff --git a/.idea/artifacts/openrocket_jar.xml b/.idea/artifacts/openrocket_jar.xml new file mode 100644 index 000000000..9e135df29 --- /dev/null +++ b/.idea/artifacts/openrocket_jar.xml @@ -0,0 +1,39 @@ + + + $PROJECT_DIR$/build/jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/src/META-INF/MANIFEST.MF b/core/src/META-INF/MANIFEST.MF new file mode 100644 index 000000000..e9cec3261 --- /dev/null +++ b/core/src/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: net.sf.openrocket.startup.SwingStartup + From ef3792d9cbb2c77b9050c323b20f6642732c7799 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sun, 25 Feb 2018 17:25:02 -0500 Subject: [PATCH 37/47] [fix] run configuration and jar paths are now cross-platform --- .idea/artifacts/openrocket_jar.xml | 2 +- .idea/runConfigurations/Openrocket_UI_Jar.xml | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.idea/artifacts/openrocket_jar.xml b/.idea/artifacts/openrocket_jar.xml index 9e135df29..9bbc47070 100644 --- a/.idea/artifacts/openrocket_jar.xml +++ b/.idea/artifacts/openrocket_jar.xml @@ -1,7 +1,7 @@ $PROJECT_DIR$/build/jar - + diff --git a/.idea/runConfigurations/Openrocket_UI_Jar.xml b/.idea/runConfigurations/Openrocket_UI_Jar.xml index 1338298bc..f5e1da03e 100644 --- a/.idea/runConfigurations/Openrocket_UI_Jar.xml +++ b/.idea/runConfigurations/Openrocket_UI_Jar.xml @@ -1,8 +1,7 @@ -