[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
This commit is contained in:
parent
9d76ece128
commit
885df6ce04
@ -367,7 +367,7 @@ public class FreeformFinSetConfig extends FinSetConfig {
|
||||
private int dragIndex = -1;
|
||||
|
||||
private FinPointScrollPane( final FinPointFigure _figure) {
|
||||
super( _figure, true);
|
||||
super( _figure);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.
|
||||
* <p>
|
||||
* 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<EventListener> listeners = new LinkedList<EventListener>();
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -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<StateChangeListener> listeners = new LinkedList<StateChangeListener>();
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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<RocketComponentShape> figureShapes = new ArrayList<RocketComponentShape>();
|
||||
|
||||
|
||||
private double minX = 0, maxX = 0, maxR = 0;
|
||||
// Figure width and height in SI-units and pixels
|
||||
|
||||
private AffineTransform g2transformation = null;
|
||||
|
||||
private final ArrayList<FigureElement> relativeExtra = new ArrayList<FigureElement>();
|
||||
private final ArrayList<FigureElement> absoluteExtra = new ArrayList<FigureElement>();
|
||||
@ -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<RocketComponentShape> allShapes, // output parameter
|
||||
final RocketComponent comp,
|
||||
final Transformation parentTransform,
|
||||
final Coordinate parentLocation){
|
||||
// NOTE: Recursive function
|
||||
private ArrayList<RocketComponentShape> updateShapeTree(
|
||||
ArrayList<RocketComponentShape> 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<Coordinate> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -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.
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
@ -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 <sampo.niskanen@iki.fi>
|
||||
*/
|
||||
@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 <code>fit</code> is <code>true</code>
|
||||
*/
|
||||
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);
|
||||
|
||||
@ -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 + ")";
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user