427868359d760f30708f8838d0631f4896480329
[debian/openrocket] / src / net / sf / openrocket / gui / configdialog / InnerTubeConfig.java
1 package net.sf.openrocket.gui.configdialog;
2
3
4
5 import java.awt.Color;
6 import java.awt.Dimension;
7 import java.awt.Graphics;
8 import java.awt.Graphics2D;
9 import java.awt.Rectangle;
10 import java.awt.RenderingHints;
11 import java.awt.event.ActionEvent;
12 import java.awt.event.ActionListener;
13 import java.awt.event.MouseEvent;
14 import java.awt.event.MouseListener;
15 import java.awt.geom.Ellipse2D;
16 import java.util.Arrays;
17 import java.util.List;
18
19 import javax.swing.BorderFactory;
20 import javax.swing.JButton;
21 import javax.swing.JComponent;
22 import javax.swing.JLabel;
23 import javax.swing.JPanel;
24 import javax.swing.JSpinner;
25 import javax.swing.SwingUtilities;
26 import javax.swing.border.BevelBorder;
27 import javax.swing.event.ChangeEvent;
28 import javax.swing.event.ChangeListener;
29
30 import net.miginfocom.swing.MigLayout;
31 import net.sf.openrocket.gui.Resettable;
32 import net.sf.openrocket.gui.SpinnerEditor;
33 import net.sf.openrocket.gui.adaptors.DoubleModel;
34 import net.sf.openrocket.gui.components.BasicSlider;
35 import net.sf.openrocket.gui.components.UnitSelector;
36 import net.sf.openrocket.rocketcomponent.ClusterConfiguration;
37 import net.sf.openrocket.rocketcomponent.Clusterable;
38 import net.sf.openrocket.rocketcomponent.InnerTube;
39 import net.sf.openrocket.rocketcomponent.MotorMount;
40 import net.sf.openrocket.rocketcomponent.RocketComponent;
41 import net.sf.openrocket.unit.UnitGroup;
42 import net.sf.openrocket.util.Coordinate;
43
44
45 public class InnerTubeConfig extends ThicknessRingComponentConfig {
46
47
48         public InnerTubeConfig(RocketComponent c) {
49                 super(c);
50                 
51                 JPanel tab;
52                 
53                 tab = positionTab();
54                 tabbedPane.insertTab("Radial position", null, tab, "Radial position", 1);
55                 
56                 tab = new MotorConfig((MotorMount)c);
57                 tabbedPane.insertTab("Motor", null, tab, "Motor mount configuration", 2);
58
59                 tab = clusterTab();
60                 tabbedPane.insertTab("Cluster", null, tab, "Cluster configuration", 3);
61                 
62                 tabbedPane.setSelectedIndex(0);
63         }
64         
65         
66         private JPanel clusterTab() {
67                 JPanel panel = new JPanel(new MigLayout());
68                 
69                 JPanel subPanel = new JPanel(new MigLayout());
70                 
71                 // Cluster type selection
72                 subPanel.add(new JLabel("Select cluster configuration:"),"spanx, wrap");
73                 subPanel.add(new ClusterSelectionPanel((InnerTube)component),"spanx, wrap");
74 //              JPanel clusterSelection = new ClusterSelectionPanel((InnerTube)component);
75 //              clusterSelection.setBackground(Color.blue);
76 //              subPanel.add(clusterSelection);
77                 
78                 panel.add(subPanel);
79                 
80                 
81                 subPanel = new JPanel(new MigLayout("gap rel unrel","[][65lp::][30lp::]"));
82
83                 // Tube separation scale
84                 JLabel l = new JLabel("Tube separation:");
85                 l.setToolTipText("The separation of the tubes, 1.0 = touching each other");
86                 subPanel.add(l);
87                 DoubleModel dm  = new DoubleModel(component,"ClusterScale",1,UnitGroup.UNITS_NONE,0);
88
89                 JSpinner spin = new JSpinner(dm.getSpinnerModel());
90                 spin.setEditor(new SpinnerEditor(spin));
91                 spin.setToolTipText("The separation of the tubes, 1.0 = touching each other");
92                 subPanel.add(spin,"growx");
93                 
94                 BasicSlider bs = new BasicSlider(dm.getSliderModel(0, 1, 4));
95                 bs.setToolTipText("The separation of the tubes, 1.0 = touching each other");
96                 subPanel.add(bs,"skip,w 100lp, wrap");
97
98                 // Rotation
99                 l = new JLabel("Rotation:");
100                 l.setToolTipText("Rotation angle of the cluster configuration");
101                 subPanel.add(l);
102                 dm  = new DoubleModel(component,"ClusterRotation",1,UnitGroup.UNITS_ANGLE,
103                                 -Math.PI,Math.PI);
104
105                 spin = new JSpinner(dm.getSpinnerModel());
106                 spin.setEditor(new SpinnerEditor(spin));
107                 spin.setToolTipText("Rotation angle of the cluster configuration");
108                 subPanel.add(spin,"growx");
109                 
110                 subPanel.add(new UnitSelector(dm),"growx");
111                 bs = new BasicSlider(dm.getSliderModel(-Math.PI, 0, Math.PI));
112                 bs.setToolTipText("Rotation angle of the cluster configuration");
113                 subPanel.add(bs,"w 100lp, wrap para");
114
115                 
116                 
117                 // Split button
118                 JButton split = new JButton("Split cluster");
119                 split.setToolTipText("<html>Split the cluster into separate components.<br>" +
120                                 "This also duplicates all components attached to this inner tube.");
121                 split.addActionListener(new ActionListener() {
122                         @Override
123                         public void actionPerformed(ActionEvent e) {
124                                 // Do change in future for overall safety
125                                 SwingUtilities.invokeLater(new Runnable() {
126                                         @Override
127                                         public void run() {
128                                                 RocketComponent parent = component.getParent();
129                                                 int index = parent.getChildPosition(component);
130                                                 if (index < 0) {
131                                                         throw new IllegalStateException("component="+component+
132                                                                         " parent="+parent+" parent.children=" + 
133                                                                         Arrays.toString(parent.getChildren()));
134                                                 }
135
136                                                 InnerTube tube = (InnerTube)component;
137                                                 if (tube.getClusterCount() <= 1)
138                                                         return;
139
140                                                 ComponentConfigDialog.addUndoPosition("Split cluster");
141
142                                                 Coordinate[] coords = { Coordinate.NUL };
143                                                 coords = component.shiftCoordinates(coords);
144                                                 parent.removeChild(index);
145                                                 for (int i=0; i<coords.length; i++) {
146                                                         InnerTube copy = (InnerTube)component.copy();
147                                                         copy.setClusterConfiguration(ClusterConfiguration.SINGLE);
148                                                         copy.setClusterRotation(0.0);
149                                                         copy.setClusterScale(1.0);
150                                                         copy.setRadialShift(coords[i].y, coords[i].z);
151                                                         copy.setName(copy.getName() + " #" + (i+1));
152                                                         
153                                                         parent.addChild(copy, index+i);
154                                                 }
155                                         }
156                                 });
157                         }
158                 });
159                 subPanel.add(split, "spanx, split 2, gapright para, sizegroup buttons, right");
160                 
161                 
162                 // Reset button
163                 JButton reset = new JButton("Reset settings");
164                 reset.setToolTipText("Reset the separation and rotation to the default values");
165                 reset.addActionListener(new ActionListener() {
166                         @Override
167                         public void actionPerformed(ActionEvent arg0) {
168                                 ((InnerTube)component).setClusterScale(1.0);
169                                 ((InnerTube)component).setClusterRotation(0.0);
170                         }
171                 });
172                 subPanel.add(reset,"sizegroup buttons, right");
173                 
174                 panel.add(subPanel,"grow");
175                 
176                 
177                 return panel;
178         }
179 }
180
181
182 class ClusterSelectionPanel extends JPanel {
183         private static final int BUTTON_SIZE = 50;
184         private static final int MOTOR_DIAMETER = 10;
185         
186         private static final Color SELECTED_COLOR = Color.RED;
187         private static final Color UNSELECTED_COLOR = Color.WHITE;
188         private static final Color MOTOR_FILL_COLOR = Color.GREEN;
189         private static final Color MOTOR_BORDER_COLOR = Color.BLACK;
190         
191         public ClusterSelectionPanel(Clusterable component) {
192                 super(new MigLayout("gap 0 0",
193                                 "["+BUTTON_SIZE+"!]["+BUTTON_SIZE+"!]["+BUTTON_SIZE+"!]["+BUTTON_SIZE+"!]",
194                                 "["+BUTTON_SIZE+"!]["+BUTTON_SIZE+"!]["+BUTTON_SIZE+"!]"));
195                 
196                 for (int i=0; i<ClusterConfiguration.CONFIGURATIONS.length; i++) {
197                         ClusterConfiguration config = ClusterConfiguration.CONFIGURATIONS[i];
198                         
199                         JComponent button = new ClusterButton(component,config);
200                         if (i%4 == 3) 
201                                 add(button,"wrap");
202                         else
203                                 add(button);
204                 }
205
206         }
207         
208         
209         private class ClusterButton extends JPanel implements ChangeListener, MouseListener,
210                                                                                                                   Resettable {
211                 private Clusterable component;
212                 private ClusterConfiguration config;
213                 
214                 public ClusterButton(Clusterable c, ClusterConfiguration config) {
215                         component = c;
216                         this.config = config;
217                         setMinimumSize(new Dimension(BUTTON_SIZE,BUTTON_SIZE));
218                         setPreferredSize(new Dimension(BUTTON_SIZE,BUTTON_SIZE));
219                         setMaximumSize(new Dimension(BUTTON_SIZE,BUTTON_SIZE));
220                         setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
221 //                      setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
222                         component.addChangeListener(this);
223                         addMouseListener(this);
224                 }
225                 
226
227                 @Override
228                 public void paintComponent(Graphics g) {
229                         super.paintComponent(g);
230                         Graphics2D g2 = (Graphics2D)g;
231                         Rectangle area = g2.getClipBounds();
232                         
233                         if (component.getClusterConfiguration() == config)
234                                 g2.setColor(SELECTED_COLOR);
235                         else
236                                 g2.setColor(UNSELECTED_COLOR);
237                         
238                         g2.fillRect(area.x, area.y, area.width, area.height);
239                         
240                         g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, 
241                                         RenderingHints.VALUE_STROKE_NORMALIZE);
242                         g2.setRenderingHint(RenderingHints.KEY_RENDERING, 
243                                         RenderingHints.VALUE_RENDER_QUALITY);
244                         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
245                                         RenderingHints.VALUE_ANTIALIAS_ON);
246                         
247                         List<Double> points = config.getPoints();
248                         Ellipse2D.Float circle = new Ellipse2D.Float();
249                         for (int i=0; i < points.size()/2; i++) {
250                                 double x = points.get(i*2);
251                                 double y = points.get(i*2+1);
252                                 
253                                 double px = BUTTON_SIZE/2 + x*MOTOR_DIAMETER;
254                                 double py = BUTTON_SIZE/2 - y*MOTOR_DIAMETER;
255                                 circle.setFrameFromCenter(px,py,px+MOTOR_DIAMETER/2,py+MOTOR_DIAMETER/2);
256                                 
257                                 g2.setColor(MOTOR_FILL_COLOR);
258                                 g2.fill(circle);
259                                 g2.setColor(MOTOR_BORDER_COLOR);
260                                 g2.draw(circle);
261                         }
262                 }
263
264
265                 public void stateChanged(ChangeEvent e) {
266                         repaint();
267                 }
268
269
270                 public void mouseClicked(MouseEvent e) {
271                         if (e.getButton() == MouseEvent.BUTTON1) {
272                                 component.setClusterConfiguration(config);
273                         }
274                 }
275                 
276                 public void mouseEntered(MouseEvent e) { }
277                 public void mouseExited(MouseEvent e) { }
278                 public void mousePressed(MouseEvent e) { }
279                 public void mouseReleased(MouseEvent e) { }
280
281
282                 public void resetModel() {
283                         component.removeChangeListener(this);
284                         removeMouseListener(this);
285                 }
286         }
287         
288 }