import java.util.LinkedHashSet;
import net.sf.openrocket.gui.figureelements.FigureElement;
+import net.sf.openrocket.gui.main.ExceptionHandler;
+import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.rocketcomponent.Configuration;
-import net.sf.openrocket.rocketcomponent.Motor;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.LineStyle;
import net.sf.openrocket.util.MathUtil;
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
-
public class RocketFigure extends AbstractScaleFigure {
private static final long serialVersionUID = 1L;
// Width for drawing normal and selected components
public static final double NORMAL_WIDTH = 1.0;
public static final double SELECTED_WIDTH = 2.0;
-
- private final Configuration configuration;
+
+ private Configuration configuration;
private RocketComponent[] selection = new RocketComponent[0];
private int type = TYPE_SIDE;
-
+
private double rotation;
private Transformation transformation;
private double translateX, translateY;
-
-
+
+
/*
* figureComponents contains the corresponding RocketComponents of the figureShapes
*/
private final ArrayList<Shape> figureShapes = new ArrayList<Shape>();
- private final ArrayList<RocketComponent> figureComponents =
- new ArrayList<RocketComponent>();
+ private final ArrayList<RocketComponent> figureComponents =
+ new ArrayList<RocketComponent>();
- private double minX=0, maxX=0, maxR=0;
+ private double minX = 0, maxX = 0, maxR = 0;
// Figure width and height in SI-units and pixels
- private double figureWidth=0, figureHeight=0;
- private int figureWidthPx=0, figureHeightPx=0;
+ private double figureWidth = 0, figureHeight = 0;
+ private int figureWidthPx = 0, figureHeightPx = 0;
private AffineTransform g2transformation = null;
this.rotation = 0.0;
this.transformation = Transformation.rotate_x(0.0);
- calculateSize();
updateFigure();
}
+ /**
+ * Set the configuration displayed by the figure. It may use the same or different rocket.
+ *
+ * @param configuration the configuration to display.
+ */
+ public void setConfiguration(Configuration configuration) {
+ this.configuration = configuration;
+ updateFigure();
+ }
+
+ @Override
public Dimension getOrigin() {
- return new Dimension((int)translateX, (int)translateY);
+ return new Dimension((int) translateX, (int) translateY);
}
@Override
public double getFigureHeight() {
return figureHeight;
}
-
+
@Override
public double getFigureWidth() {
return figureWidth;
}
-
+
public RocketComponent[] getSelection() {
return selection;
public void setSelection(RocketComponent[] selection) {
if (selection == null) {
- selection = new RocketComponent[0];
+ this.selection = new RocketComponent[0];
} else {
this.selection = selection;
}
public void setType(int type) {
if (type != TYPE_BACK && type != TYPE_SIDE) {
- throw new IllegalArgumentException("Illegal type: "+type);
+ throw new IllegalArgumentException("Illegal type: " + type);
}
if (this.type == type)
return;
this.type = type;
updateFigure();
}
-
-
+
/**
* Updates the figure shapes and figure size.
*/
figureComponents.clear();
calculateSize();
-
+
// Get shapes for all active components
- for (RocketComponent c: configuration) {
+ for (RocketComponent c : configuration) {
Shape[] s = getShapes(c);
- for (int i=0; i < s.length; i++) {
+ for (int i = 0; i < s.length; i++) {
figureShapes.add(s[i]);
figureComponents.add(c);
}
repaint();
fireChangeEvent();
}
-
+
public void addRelativeExtra(FigureElement p) {
relativeExtra.add(p);
absoluteExtra.clear();
}
-
+
/**
* Paints the rocket on to the Graphics element.
* <p>
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
- Graphics2D g2 = (Graphics2D)g;
-
+ Graphics2D g2 = (Graphics2D) g;
+
AffineTransform baseTransform = g2.getTransform();
// Update figure shapes if necessary
if (figureShapes == null)
updateFigure();
-
+
double tx, ty;
// Calculate translation for figure centering
- if (figureWidthPx + 2*BORDER_PIXELS_WIDTH < getWidth()) {
-
+ if (figureWidthPx + 2 * borderPixelsWidth < getWidth()) {
+
// Figure fits in the viewport
if (type == TYPE_BACK)
- tx = getWidth()/2;
- else
- tx = (getWidth()-figureWidthPx)/2 - minX*scale;
-
+ tx = getWidth() / 2;
+ else
+ tx = (getWidth() - figureWidthPx) / 2 - minX * scale;
+
} else {
-
+
// Figure does not fit in viewport
if (type == TYPE_BACK)
- tx = BORDER_PIXELS_WIDTH + figureWidthPx/2;
- else
- tx = BORDER_PIXELS_WIDTH - minX*scale;
+ tx = borderPixelsWidth + figureWidthPx / 2;
+ else
+ tx = borderPixelsWidth - minX * scale;
}
- if (figureHeightPx + 2*BORDER_PIXELS_HEIGHT < getHeight()) {
- ty = getHeight()/2;
- } else {
- ty = BORDER_PIXELS_HEIGHT + figureHeightPx/2;
- }
+ ty = computeTy(figureHeightPx);
- if (Math.abs(translateX - tx)>1 || Math.abs(translateY - ty)>1) {
+ if (Math.abs(translateX - tx) > 1 || Math.abs(translateY - ty) > 1) {
// Origin has changed, fire event
translateX = tx;
translateY = ty;
g2transformation = new AffineTransform();
g2transformation.translate(translateX, translateY);
// Mirror position Y-axis upwards
- g2transformation.scale(scale/EXTRA_SCALE, -scale/EXTRA_SCALE);
-
+ g2transformation.scale(scale / EXTRA_SCALE, -scale / EXTRA_SCALE);
+
g2.transform(g2transformation);
-
+
// Set rendering hints appropriately
- g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
+ g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_NORMALIZE);
- g2.setRenderingHint(RenderingHints.KEY_RENDERING,
+ g2.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
- g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
-
+
// Draw all shapes
- for (int i=0; i < figureShapes.size(); i++) {
+ for (int i = 0; i < figureShapes.size(); i++) {
RocketComponent c = figureComponents.get(i);
Shape s = figureShapes.get(i);
boolean selected = false;
// Check if component is in the selection
- for (int j=0; j < selection.length; j++) {
+ for (int j = 0; j < selection.length; j++) {
if (c == selection[j]) {
selected = true;
break;
color = Prefs.getDefaultColor(c.getClass());
}
g2.setColor(color);
-
+
LineStyle style = c.getLineStyle();
if (style == null)
style = Prefs.getDefaultLineStyle(c.getClass());
float[] dashes = style.getDashes();
- for (int j=0; j<dashes.length; j++) {
+ for (int j = 0; j < dashes.length; j++) {
dashes[j] *= EXTRA_SCALE / scale;
}
if (selected) {
- g2.setStroke(new BasicStroke((float)(SELECTED_WIDTH*EXTRA_SCALE/scale),
- BasicStroke.CAP_BUTT,BasicStroke.JOIN_BEVEL, 0, dashes, 0));
+ g2.setStroke(new BasicStroke((float) (SELECTED_WIDTH * EXTRA_SCALE / 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),
- BasicStroke.CAP_BUTT,BasicStroke.JOIN_BEVEL, 0, dashes, 0));
- g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
+ g2.setStroke(new BasicStroke((float) (NORMAL_WIDTH * EXTRA_SCALE / scale),
+ BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, dashes, 0));
+ g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_NORMALIZE);
}
g2.draw(s);
}
- g2.setStroke(new BasicStroke((float)(NORMAL_WIDTH*EXTRA_SCALE/scale),
- BasicStroke.CAP_BUTT,BasicStroke.JOIN_BEVEL));
- g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
+ g2.setStroke(new BasicStroke((float) (NORMAL_WIDTH * EXTRA_SCALE / scale),
+ BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
+ g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_NORMALIZE);
-
+
// Draw motors
String motorID = configuration.getMotorConfigurationID();
Color fillColor = Prefs.getMotorFillColor();
double length = motor.getLength();
double radius = motor.getDiameter() / 2;
- Coordinate[] position = ((RocketComponent)mount).toAbsolute(
- new Coordinate(((RocketComponent)mount).getLength() +
+ Coordinate[] position = ((RocketComponent) mount).toAbsolute(
+ new Coordinate(((RocketComponent) mount).getLength() +
mount.getMotorOverhang() - length));
- for (int i=0; i < position.length; i++) {
+ for (int i = 0; i < position.length; i++) {
position[i] = transformation.transform(position[i]);
}
- for (Coordinate coord: position) {
+ for (Coordinate coord : position) {
Shape s;
if (type == TYPE_SIDE) {
- s = new Rectangle2D.Double(EXTRA_SCALE*coord.x,
- EXTRA_SCALE*(coord.y - radius), EXTRA_SCALE*length,
- EXTRA_SCALE*2*radius);
+ s = new Rectangle2D.Double(EXTRA_SCALE * coord.x,
+ EXTRA_SCALE * (coord.y - radius), EXTRA_SCALE * length,
+ EXTRA_SCALE * 2 * radius);
} else {
- s = new Ellipse2D.Double(EXTRA_SCALE*(coord.z-radius),
- EXTRA_SCALE*(coord.y-radius), EXTRA_SCALE*2*radius,
- EXTRA_SCALE*2*radius);
+ s = new Ellipse2D.Double(EXTRA_SCALE * (coord.z - radius),
+ EXTRA_SCALE * (coord.y - radius), EXTRA_SCALE * 2 * radius,
+ EXTRA_SCALE * 2 * radius);
}
g2.setColor(fillColor);
g2.fill(s);
}
}
-
-
+
+
// Draw relative extras
- for (FigureElement e: relativeExtra) {
- e.paint(g2, scale/EXTRA_SCALE);
+ for (FigureElement e : relativeExtra) {
+ e.paint(g2, scale / EXTRA_SCALE);
}
-
+
// Draw absolute extras
g2.setTransform(baseTransform);
Rectangle rect = this.getVisibleRect();
- for (FigureElement e: absoluteExtra) {
+ for (FigureElement e : absoluteExtra) {
e.paint(g2, 1.0, rect);
}
-
+
+ }
+
+ 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);
+ Point2D.Double p = new Point2D.Double(x, y);
try {
- g2transformation.inverseTransform(p,p);
+ g2transformation.inverseTransform(p, p);
} catch (NoninvertibleTransformException e) {
return new RocketComponent[0];
}
LinkedHashSet<RocketComponent> l = new LinkedHashSet<RocketComponent>();
-
- for (int i=0; i<figureShapes.size(); i++) {
+
+ for (int i = 0; i < figureShapes.size(); i++) {
if (figureShapes.get(i).contains(p))
l.add(figureComponents.get(i));
}
}
-
+
/**
* Gets the shapes required to draw the component.
*
*/
private Shape[] getShapes(RocketComponent component) {
Reflection.Method m;
-
+
// Find the appropriate method
switch (type) {
case TYPE_SIDE:
- m = Reflection.findMethod(ROCKET_FIGURE_PACKAGE, component, ROCKET_FIGURE_SUFFIX, "getShapesSide",
+ m = Reflection.findMethod(ROCKET_FIGURE_PACKAGE, component, ROCKET_FIGURE_SUFFIX, "getShapesSide",
RocketComponent.class, Transformation.class);
break;
-
+
case TYPE_BACK:
- m = Reflection.findMethod(ROCKET_FIGURE_PACKAGE, component, ROCKET_FIGURE_SUFFIX, "getShapesBack",
+ m = Reflection.findMethod(ROCKET_FIGURE_PACKAGE, component, ROCKET_FIGURE_SUFFIX, "getShapesBack",
RocketComponent.class, Transformation.class);
break;
-
+
default:
- throw new RuntimeException("Unknown figure type = "+type);
+ throw new BugException("Unknown figure type = " + type);
}
if (m == null) {
- System.err.println("ERROR: Rocket figure paint method not found for " + component);
+ ExceptionHandler.handleErrorCondition("ERROR: Rocket figure paint method not found for "
+ + component);
return new Shape[0];
}
-
- return (Shape[])m.invokeStatic(component,transformation);
+
+ return (Shape[]) m.invokeStatic(component, transformation);
}
-
+
/**
* 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.
maxR = 0;
return;
}
-
+
minX = Double.MAX_VALUE;
maxX = Double.MIN_VALUE;
maxR = 0;
- for (Coordinate c: bounds) {
+ for (Coordinate c : bounds) {
double x = c.x, r = MathUtil.hypot(c.y, c.z);
if (x < minX)
minX = x;
public double getBestZoom(Rectangle2D bounds) {
- double zh=1, zv=1;
+ double zh = 1, zv = 1;
if (bounds.getWidth() > 0.0001)
- zh = (getWidth()-2*BORDER_PIXELS_WIDTH)/bounds.getWidth();
+ zh = (getWidth() - 2 * borderPixelsWidth) / bounds.getWidth();
if (bounds.getHeight() > 0.0001)
- zv = (getHeight()-2*BORDER_PIXELS_HEIGHT)/bounds.getHeight();
+ zv = (getHeight() - 2 * borderPixelsHeight) / bounds.getHeight();
return Math.min(zh, zv);
}
-
+
+
/**
* Calculates the necessary size of the figure and set the PreferredSize
* property accordingly.
switch (type) {
case TYPE_SIDE:
- figureWidth = maxX-minX;
- figureHeight = 2*maxR;
+ figureWidth = maxX - minX;
+ figureHeight = 2 * maxR;
break;
-
+
case TYPE_BACK:
- figureWidth = 2*maxR;
- figureHeight = 2*maxR;
+ figureWidth = 2 * maxR;
+ figureHeight = 2 * maxR;
break;
-
+
default:
- assert(false): "Should not occur, type="+type;
+ assert (false) : "Should not occur, type=" + type;
figureWidth = 0;
figureHeight = 0;
}
- figureWidthPx = (int)(figureWidth * scale);
- figureHeightPx = (int)(figureHeight * scale);
-
- Dimension d = new Dimension(figureWidthPx+2*BORDER_PIXELS_WIDTH,
- figureHeightPx+2*BORDER_PIXELS_HEIGHT);
+ figureWidthPx = (int) (figureWidth * scale);
+ figureHeightPx = (int) (figureHeight * scale);
+
+ Dimension d = new Dimension(figureWidthPx + 2 * borderPixelsWidth,
+ figureHeightPx + 2 * borderPixelsHeight);
if (!d.equals(getPreferredSize()) || !d.equals(getMinimumSize())) {
setPreferredSize(d);
public Rectangle2D getDimensions() {
switch (type) {
case TYPE_SIDE:
- return new Rectangle2D.Double(minX,-maxR,maxX-minX,2*maxR);
+ return new Rectangle2D.Double(minX, -maxR, maxX - minX, 2 * maxR);
case TYPE_BACK:
- return new Rectangle2D.Double(-maxR,-maxR,2*maxR,2*maxR);
+ return new Rectangle2D.Double(-maxR, -maxR, 2 * maxR, 2 * maxR);
default:
- throw new RuntimeException("Illegal figure type = "+type);
+ throw new BugException("Illegal figure type = " + type);
}
}
-
+
}