1 package net.sf.openrocket.gui.configdialog;
5 import java.awt.event.MouseEvent;
6 import java.awt.geom.Point2D;
8 import javax.swing.JComboBox;
9 import javax.swing.JLabel;
10 import javax.swing.JPanel;
11 import javax.swing.JScrollPane;
12 import javax.swing.JSeparator;
13 import javax.swing.JSpinner;
14 import javax.swing.JTable;
15 import javax.swing.ListSelectionModel;
16 import javax.swing.SwingConstants;
17 import javax.swing.table.AbstractTableModel;
19 import net.miginfocom.swing.MigLayout;
20 import net.sf.openrocket.gui.SpinnerEditor;
21 import net.sf.openrocket.gui.adaptors.DoubleModel;
22 import net.sf.openrocket.gui.adaptors.EnumModel;
23 import net.sf.openrocket.gui.adaptors.IntegerModel;
24 import net.sf.openrocket.gui.components.BasicSlider;
25 import net.sf.openrocket.gui.components.StyledLabel;
26 import net.sf.openrocket.gui.components.UnitSelector;
27 import net.sf.openrocket.gui.scalefigure.FinPointFigure;
28 import net.sf.openrocket.gui.scalefigure.ScaleScrollPane;
29 import net.sf.openrocket.gui.scalefigure.ScaleSelector;
30 import net.sf.openrocket.material.Material;
31 import net.sf.openrocket.rocketcomponent.FinSet;
32 import net.sf.openrocket.rocketcomponent.FreeformFinSet;
33 import net.sf.openrocket.rocketcomponent.IllegalFinPointException;
34 import net.sf.openrocket.rocketcomponent.RocketComponent;
35 import net.sf.openrocket.unit.UnitGroup;
36 import net.sf.openrocket.util.Coordinate;
38 public class FreeformFinSetConfig extends FinSetConfig {
40 private final FreeformFinSet finset;
41 private JTable table = null;
42 private FinPointTableModel tableModel = null;
44 private FinPointFigure figure = null;
47 public FreeformFinSetConfig(RocketComponent component) {
49 this.finset = (FreeformFinSet)component;
52 tabbedPane.insertTab("General", null, generalPane(), "General properties", 0);
53 tabbedPane.insertTab("Shape", null, shapePane(), "Fin shape", 1);
54 tabbedPane.setSelectedIndex(0);
61 private JPanel generalPane() {
67 JPanel mainPanel = new JPanel(new MigLayout("fill"));
69 JPanel panel = new JPanel(new MigLayout("fill, gap rel unrel","[][65lp::][30lp::]",""));
74 panel.add(new JLabel("Number of fins:"));
76 IntegerModel im = new IntegerModel(component,"FinCount",1,8);
78 spin = new JSpinner(im.getSpinnerModel());
79 spin.setEditor(new SpinnerEditor(spin));
80 panel.add(spin,"growx, wrap");
84 panel.add(new JLabel("Fin rotation:"));
86 m = new DoubleModel(component, "BaseRotation", UnitGroup.UNITS_ANGLE,-Math.PI,Math.PI);
88 spin = new JSpinner(m.getSpinnerModel());
89 spin.setEditor(new SpinnerEditor(spin));
90 panel.add(spin,"growx");
92 panel.add(new UnitSelector(m),"growx");
93 panel.add(new BasicSlider(m.getSliderModel(-Math.PI,Math.PI)),"w 100lp, wrap");
98 JLabel label = new JLabel("Fin cant:");
99 label.setToolTipText("The angle that the fins are canted with respect to the rocket " +
103 m = new DoubleModel(component, "CantAngle", UnitGroup.UNITS_ANGLE,
104 -FinSet.MAX_CANT,FinSet.MAX_CANT);
106 spin = new JSpinner(m.getSpinnerModel());
107 spin.setEditor(new SpinnerEditor(spin));
108 panel.add(spin,"growx");
110 panel.add(new UnitSelector(m),"growx");
111 panel.add(new BasicSlider(m.getSliderModel(-FinSet.MAX_CANT,FinSet.MAX_CANT)),
112 "w 100lp, wrap 40lp");
117 panel.add(new JLabel("Position relative to:"));
119 combo = new JComboBox(
120 new EnumModel<RocketComponent.Position>(component, "RelativePosition",
121 new RocketComponent.Position[] {
122 RocketComponent.Position.TOP,
123 RocketComponent.Position.MIDDLE,
124 RocketComponent.Position.BOTTOM,
125 RocketComponent.Position.ABSOLUTE
127 panel.add(combo,"spanx 3, growx, wrap");
129 panel.add(new JLabel("plus"),"right");
131 m = new DoubleModel(component,"PositionValue",UnitGroup.UNITS_LENGTH);
132 spin = new JSpinner(m.getSpinnerModel());
133 spin.setEditor(new SpinnerEditor(spin));
134 panel.add(spin,"growx");
136 panel.add(new UnitSelector(m),"growx");
137 panel.add(new BasicSlider(m.getSliderModel(
138 new DoubleModel(component.getParent(), "Length", -1.0, UnitGroup.UNITS_NONE),
139 new DoubleModel(component.getParent(), "Length"))),
146 mainPanel.add(panel, "aligny 20%");
147 mainPanel.add(new JSeparator(SwingConstants.VERTICAL), "growy, height 150lp");
150 panel = new JPanel(new MigLayout("gap rel unrel","[][65lp::][30lp::]",""));
156 panel.add(new JLabel("Fin cross section:"),"span, split");
157 combo = new JComboBox(
158 new EnumModel<FinSet.CrossSection>(component,"CrossSection"));
159 panel.add(combo,"growx, wrap unrel");
163 panel.add(new JLabel("Thickness:"));
165 m = new DoubleModel(component,"Thickness",UnitGroup.UNITS_LENGTH,0);
167 spin = new JSpinner(m.getSpinnerModel());
168 spin.setEditor(new SpinnerEditor(spin));
169 panel.add(spin,"growx");
171 panel.add(new UnitSelector(m),"growx");
172 panel.add(new BasicSlider(m.getSliderModel(0,0.01)),"w 100lp, wrap 30lp");
176 materialPanel(panel, Material.Type.BULK);
180 mainPanel.add(panel, "aligny 20%");
187 private JPanel shapePane() {
188 JPanel panel = new JPanel(new MigLayout("fill"));
192 figure = new FinPointFigure(finset);
193 ScaleScrollPane figurePane = new FinPointScrollPane();
194 figurePane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
195 figurePane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
198 tableModel = new FinPointTableModel();
199 table = new JTable(tableModel);
200 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
201 for (int i=0; i < Columns.values().length; i++) {
202 table.getColumnModel().getColumn(i).
203 setPreferredWidth(Columns.values()[i].getWidth());
205 JScrollPane tablePane = new JScrollPane(table);
208 // panel.add(new JLabel("Coordinates:"), "aligny bottom, alignx 50%");
209 // panel.add(new JLabel(" View:"), "wrap, aligny bottom");
212 panel.add(tablePane,"growy, width 100lp:100lp:, height 100lp:250lp:");
213 panel.add(figurePane,"gap unrel, spanx, growx, growy 1000, height 100lp:250lp:, wrap");
215 panel.add(new StyledLabel("Double-click", -2), "alignx 50%");
217 panel.add(new ScaleSelector(figurePane),"spany 2");
218 panel.add(new StyledLabel("Click+drag: Add and move points " +
219 "Ctrl+click: Remove point", -2), "spany 2, right, wrap");
222 panel.add(new StyledLabel("to edit", -2), "alignx 50%");
232 public void updateFields() {
233 super.updateFields();
235 if (tableModel != null) {
236 tableModel.fireTableDataChanged();
238 if (figure != null) {
239 figure.updateFigure();
246 private class FinPointScrollPane extends ScaleScrollPane {
247 private static final int ANY_MASK =
248 (MouseEvent.ALT_DOWN_MASK | MouseEvent.ALT_GRAPH_DOWN_MASK |
249 MouseEvent.META_DOWN_MASK | MouseEvent.CTRL_DOWN_MASK |
250 MouseEvent.SHIFT_DOWN_MASK);
252 private int dragIndex = -1;
254 public FinPointScrollPane() {
255 super(figure, false); // Disallow fitting as it's buggy
259 public void mousePressed(MouseEvent event) {
260 int mods = event.getModifiersEx();
262 if (event.getButton() != MouseEvent.BUTTON1 ||
263 (mods & ANY_MASK) != 0) {
264 super.mousePressed(event);
268 int index = getPoint(event);
273 index = getSegment(event);
275 Point2D.Double point = getCoordinates(event);
276 finset.addPoint(index);
278 finset.setPoint(index, point.x, point.y);
279 } catch (IllegalFinPointException ignore) { }
285 super.mousePressed(event);
291 public void mouseDragged(MouseEvent event) {
292 int mods = event.getModifiersEx();
294 (mods & (ANY_MASK | MouseEvent.BUTTON1_DOWN_MASK)) !=
295 MouseEvent.BUTTON1_DOWN_MASK) {
296 super.mouseDragged(event);
299 Point2D.Double point = getCoordinates(event);
302 finset.setPoint(dragIndex, point.x, point.y);
303 } catch (IllegalFinPointException ignore) {
304 System.out.println("IAE:"+ignore);
310 public void mouseReleased(MouseEvent event) {
312 super.mouseReleased(event);
316 public void mouseClicked(MouseEvent event) {
317 int mods = event.getModifiersEx();
318 if (event.getButton() != MouseEvent.BUTTON1 ||
319 (mods & ANY_MASK) != MouseEvent.CTRL_DOWN_MASK) {
320 super.mouseClicked(event);
324 int index = getPoint(event);
326 super.mouseClicked(event);
331 finset.removePoint(index);
332 } catch (IllegalFinPointException ignore) {
337 private int getPoint(MouseEvent event) {
338 Point p0 = event.getPoint();
339 Point p1 = this.getViewport().getViewPosition();
343 return figure.getIndexByPoint(x, y);
346 private int getSegment(MouseEvent event) {
347 Point p0 = event.getPoint();
348 Point p1 = this.getViewport().getViewPosition();
352 return figure.getSegmentByPoint(x, y);
355 private Point2D.Double getCoordinates(MouseEvent event) {
356 Point p0 = event.getPoint();
357 Point p1 = this.getViewport().getViewPosition();
361 return figure.convertPoint(x, y);
371 private enum Columns {
374 // public String toString() {
378 // public String getValue(FreeformFinSet finset, int row) {
379 // return "" + (row+1) + ".";
382 // public int getWidth() {
388 public String toString() {
389 return "X / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().toString();
392 public String getValue(FreeformFinSet finset, int row) {
393 return UnitGroup.UNITS_LENGTH.getDefaultUnit()
394 .toString(finset.getFinPoints()[row].x);
399 public String toString() {
400 return "Y / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().toString();
403 public String getValue(FreeformFinSet finset, int row) {
404 return UnitGroup.UNITS_LENGTH.getDefaultUnit()
405 .toString(finset.getFinPoints()[row].y);
409 public abstract String getValue(FreeformFinSet finset, int row);
411 public abstract String toString();
412 public int getWidth() {
417 private class FinPointTableModel extends AbstractTableModel {
420 public int getColumnCount() {
421 return Columns.values().length;
425 public int getRowCount() {
426 return finset.getPointCount();
430 public Object getValueAt(int rowIndex, int columnIndex) {
431 return Columns.values()[columnIndex].getValue(finset, rowIndex);
435 public String getColumnName(int columnIndex) {
436 return Columns.values()[columnIndex].toString();
440 public boolean isCellEditable(int rowIndex, int columnIndex) {
441 if (rowIndex == 0 || rowIndex == getRowCount()-1) {
442 return (columnIndex == Columns.X.ordinal());
445 return (columnIndex == Columns.X.ordinal() || columnIndex == Columns.Y.ordinal());
449 public void setValueAt(Object o, int rowIndex, int columnIndex) {
450 if (!(o instanceof String))
453 String str = (String)o;
456 double value = UnitGroup.UNITS_LENGTH.fromString(str);
457 Coordinate c = finset.getFinPoints()[rowIndex];
458 if (columnIndex == Columns.X.ordinal())
463 finset.setPoint(rowIndex, c.x, c.y);
465 } catch (NumberFormatException ignore) {
466 } catch (IllegalFinPointException ignore) {