1 package net.sf.openrocket.gui.configdialog;
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.List;
18 import javax.swing.BorderFactory;
19 import javax.swing.JButton;
20 import javax.swing.JComponent;
21 import javax.swing.JLabel;
22 import javax.swing.JPanel;
23 import javax.swing.JSpinner;
24 import javax.swing.border.BevelBorder;
25 import javax.swing.event.ChangeEvent;
26 import javax.swing.event.ChangeListener;
28 import net.miginfocom.swing.MigLayout;
29 import net.sf.openrocket.gui.Resettable;
30 import net.sf.openrocket.gui.SpinnerEditor;
31 import net.sf.openrocket.gui.adaptors.DoubleModel;
32 import net.sf.openrocket.gui.components.BasicSlider;
33 import net.sf.openrocket.gui.components.UnitSelector;
34 import net.sf.openrocket.rocketcomponent.ClusterConfiguration;
35 import net.sf.openrocket.rocketcomponent.Clusterable;
36 import net.sf.openrocket.rocketcomponent.InnerTube;
37 import net.sf.openrocket.rocketcomponent.MotorMount;
38 import net.sf.openrocket.rocketcomponent.RocketComponent;
39 import net.sf.openrocket.unit.UnitGroup;
42 public class InnerTubeConfig extends ThicknessRingComponentConfig {
45 public InnerTubeConfig(RocketComponent c) {
51 tabbedPane.insertTab("Radial position", null, tab, "Radial position", 1);
53 tab = new MotorConfig((MotorMount)c);
54 tabbedPane.insertTab("Motor", null, tab, "Motor mount configuration", 2);
57 tabbedPane.insertTab("Cluster", null, tab, "Cluster configuration", 3);
59 tabbedPane.setSelectedIndex(0);
63 private JPanel clusterTab() {
64 JPanel panel = new JPanel(new MigLayout());
66 JPanel subPanel = new JPanel(new MigLayout());
68 // Cluster type selection
69 subPanel.add(new JLabel("Select cluster configuration:"),"spanx, wrap");
70 subPanel.add(new ClusterSelectionPanel((InnerTube)component),"spanx, wrap");
71 // JPanel clusterSelection = new ClusterSelectionPanel((InnerTube)component);
72 // clusterSelection.setBackground(Color.blue);
73 // subPanel.add(clusterSelection);
78 subPanel = new JPanel(new MigLayout("gap rel unrel","[][65lp::][30lp::]"));
80 // Tube separation scale
81 JLabel l = new JLabel("Tube separation:");
82 l.setToolTipText("The separation of the tubes, 1.0 = touching each other");
84 DoubleModel dm = new DoubleModel(component,"ClusterScale",1,UnitGroup.UNITS_NONE,0);
86 JSpinner spin = new JSpinner(dm.getSpinnerModel());
87 spin.setEditor(new SpinnerEditor(spin));
88 spin.setToolTipText("The separation of the tubes, 1.0 = touching each other");
89 subPanel.add(spin,"growx");
91 BasicSlider bs = new BasicSlider(dm.getSliderModel(0, 1, 4));
92 bs.setToolTipText("The separation of the tubes, 1.0 = touching each other");
93 subPanel.add(bs,"skip,w 100lp, wrap");
96 l = new JLabel("Rotation:");
97 l.setToolTipText("Rotation angle of the cluster configuration");
99 dm = new DoubleModel(component,"ClusterRotation",1,UnitGroup.UNITS_ANGLE,0);
101 spin = new JSpinner(dm.getSpinnerModel());
102 spin.setEditor(new SpinnerEditor(spin));
103 spin.setToolTipText("Rotation angle of the cluster configuration");
104 subPanel.add(spin,"growx");
106 subPanel.add(new UnitSelector(dm),"growx");
107 bs = new BasicSlider(dm.getSliderModel(-Math.PI, 0, Math.PI));
108 bs.setToolTipText("Rotation angle of the cluster configuration");
109 subPanel.add(bs,"w 100lp, wrap");
112 JButton reset = new JButton("Reset");
113 reset.setToolTipText("Reset the separation and rotation to the default values");
114 reset.addActionListener(new ActionListener() {
116 public void actionPerformed(ActionEvent arg0) {
117 ((InnerTube)component).setClusterScale(1.0);
118 ((InnerTube)component).setClusterRotation(0.0);
121 subPanel.add(reset,"spanx,right");
123 panel.add(subPanel,"grow");
131 class ClusterSelectionPanel extends JPanel {
132 private static final int BUTTON_SIZE = 50;
133 private static final int MOTOR_DIAMETER = 10;
135 private static final Color SELECTED_COLOR = Color.RED;
136 private static final Color UNSELECTED_COLOR = Color.WHITE;
137 private static final Color MOTOR_FILL_COLOR = Color.GREEN;
138 private static final Color MOTOR_BORDER_COLOR = Color.BLACK;
140 public ClusterSelectionPanel(Clusterable component) {
141 super(new MigLayout("gap 0 0",
142 "["+BUTTON_SIZE+"!]["+BUTTON_SIZE+"!]["+BUTTON_SIZE+"!]["+BUTTON_SIZE+"!]",
143 "["+BUTTON_SIZE+"!]["+BUTTON_SIZE+"!]["+BUTTON_SIZE+"!]"));
145 for (int i=0; i<ClusterConfiguration.CONFIGURATIONS.length; i++) {
146 ClusterConfiguration config = ClusterConfiguration.CONFIGURATIONS[i];
148 JComponent button = new ClusterButton(component,config);
158 private class ClusterButton extends JPanel implements ChangeListener, MouseListener,
160 private Clusterable component;
161 private ClusterConfiguration config;
163 public ClusterButton(Clusterable c, ClusterConfiguration config) {
165 this.config = config;
166 setMinimumSize(new Dimension(BUTTON_SIZE,BUTTON_SIZE));
167 setPreferredSize(new Dimension(BUTTON_SIZE,BUTTON_SIZE));
168 setMaximumSize(new Dimension(BUTTON_SIZE,BUTTON_SIZE));
169 setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
170 // setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
171 component.addChangeListener(this);
172 addMouseListener(this);
177 public void paintComponent(Graphics g) {
178 super.paintComponent(g);
179 Graphics2D g2 = (Graphics2D)g;
180 Rectangle area = g2.getClipBounds();
182 if (component.getClusterConfiguration() == config)
183 g2.setColor(SELECTED_COLOR);
185 g2.setColor(UNSELECTED_COLOR);
187 g2.fillRect(area.x, area.y, area.width, area.height);
189 g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
190 RenderingHints.VALUE_STROKE_NORMALIZE);
191 g2.setRenderingHint(RenderingHints.KEY_RENDERING,
192 RenderingHints.VALUE_RENDER_QUALITY);
193 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
194 RenderingHints.VALUE_ANTIALIAS_ON);
196 List<Double> points = config.getPoints();
197 Ellipse2D.Float circle = new Ellipse2D.Float();
198 for (int i=0; i < points.size()/2; i++) {
199 double x = points.get(i*2);
200 double y = points.get(i*2+1);
202 double px = BUTTON_SIZE/2 + x*MOTOR_DIAMETER;
203 double py = BUTTON_SIZE/2 - y*MOTOR_DIAMETER;
204 circle.setFrameFromCenter(px,py,px+MOTOR_DIAMETER/2,py+MOTOR_DIAMETER/2);
206 g2.setColor(MOTOR_FILL_COLOR);
208 g2.setColor(MOTOR_BORDER_COLOR);
214 public void stateChanged(ChangeEvent e) {
219 public void mouseClicked(MouseEvent e) {
220 if (e.getButton() == MouseEvent.BUTTON1) {
221 component.setClusterConfiguration(config);
225 public void mouseEntered(MouseEvent e) { }
226 public void mouseExited(MouseEvent e) { }
227 public void mousePressed(MouseEvent e) { }
228 public void mouseReleased(MouseEvent e) { }
231 public void resetModel() {
232 component.removeChangeListener(this);
233 removeMouseListener(this);