Merge branch 'master' into kruland-integration-ui
Conflicts: core/.classpath
This commit is contained in:
commit
dd5a16802b
@ -29,5 +29,7 @@
|
||||
<classpathentry kind="lib" path="lib/aopalliance.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jcommon-1.0.17.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jfreechart-1.0.14.jar" sourcepath="reference/jfreechart-1.0.14-sources.jar"/>
|
||||
<classpathentry kind="lib" path="lib-test/test-plugin.jar"/>
|
||||
<classpathentry kind="lib" path="lib/annotation-detector-3.0.2-SNAPSHOT.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
@ -101,6 +101,7 @@
|
||||
<zipfileset src="${lib.dir}/miglayout15-swing.jar"/>
|
||||
<zipfileset src="${lib.dir}/opencsv-2.3.jar"/>
|
||||
<zipfileset src="${lib.dir}/OrangeExtensions-1.2.jar"/>
|
||||
<zipfileset src="${lib.dir}/annotation-detector-3.0.2-SNAPSHOT.jar"/>
|
||||
|
||||
|
||||
<!-- JOGL libraries need to be jar-in-jar -->
|
||||
|
BIN
core/lib-test/test-plugin.jar
Normal file
BIN
core/lib-test/test-plugin.jar
Normal file
Binary file not shown.
BIN
core/lib/annotation-detector-3.0.2-SNAPSHOT.jar
Normal file
BIN
core/lib/annotation-detector-3.0.2-SNAPSHOT.jar
Normal file
Binary file not shown.
@ -33,7 +33,7 @@ public class MotorDescriptionSubstitutor implements RocketSubstitutor {
|
||||
@Override
|
||||
public String substitute(String str, Rocket rocket, String configId) {
|
||||
String description = getFlightConfigurationDescription(rocket, configId);
|
||||
return str.replaceAll(SUBSTITUTION, description);
|
||||
return str.replace(SUBSTITUTION, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,15 +1,5 @@
|
||||
package net.sf.openrocket.gui.print;
|
||||
|
||||
import net.sf.openrocket.l10n.Translator;
|
||||
import net.sf.openrocket.rocketcomponent.BodyTube;
|
||||
import net.sf.openrocket.rocketcomponent.ExternalComponent;
|
||||
import net.sf.openrocket.rocketcomponent.FinSet;
|
||||
import net.sf.openrocket.rocketcomponent.LaunchLug;
|
||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics;
|
||||
@ -21,12 +11,22 @@ import java.awt.geom.Path2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import net.sf.openrocket.l10n.Translator;
|
||||
import net.sf.openrocket.rocketcomponent.BodyTube;
|
||||
import net.sf.openrocket.rocketcomponent.ExternalComponent;
|
||||
import net.sf.openrocket.rocketcomponent.FinSet;
|
||||
import net.sf.openrocket.rocketcomponent.LaunchLug;
|
||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
|
||||
/**
|
||||
* This is the core Swing representation of a fin marking guide. It can handle multiple fin sets on the same or
|
||||
* different body tubes. One marking guide will be created for any body tube that has a finset. If a tube has multiple
|
||||
@ -36,448 +36,446 @@ import java.util.Map;
|
||||
* <p/>
|
||||
*/
|
||||
public class FinMarkingGuide extends JPanel {
|
||||
|
||||
/**
|
||||
* The stroke of normal lines.
|
||||
*/
|
||||
private final static BasicStroke thinStroke = new BasicStroke(1.0f);
|
||||
|
||||
/**
|
||||
* The size of the arrow in points.
|
||||
*/
|
||||
private static final int ARROW_SIZE = 10;
|
||||
|
||||
/**
|
||||
* Typical thickness of a piece of printer paper (~20-24 lb paper). Wrapping paper around a tube results in the
|
||||
* radius being increased by the thickness of the paper. The smaller the tube, the more pronounced this becomes as a
|
||||
* percentage of circumference. Using 1/10mm as an approximation here.
|
||||
*/
|
||||
private static final double PAPER_THICKNESS_IN_METERS = PrintUnit.MILLIMETERS.toMeters(0.1d);
|
||||
|
||||
/**
|
||||
* The default guide width in inches.
|
||||
*/
|
||||
public final static double DEFAULT_GUIDE_WIDTH = 3d;
|
||||
|
||||
/**
|
||||
* 2 PI radians (represents a circle).
|
||||
*/
|
||||
public final static double TWO_PI = 2 * Math.PI;
|
||||
|
||||
/**
|
||||
* The I18N translator.
|
||||
*/
|
||||
private static final Translator trans = Application.getTranslator();
|
||||
|
||||
/**
|
||||
* The margin.
|
||||
*/
|
||||
private static final int MARGIN = (int) PrintUnit.INCHES.toPoints(0.25f);
|
||||
|
||||
/**
|
||||
* The height (circumference) of the biggest body tube with a finset.
|
||||
*/
|
||||
private int maxHeight = 0;
|
||||
|
||||
/**
|
||||
* A map of body tubes, to a list of components that contains finsets and launch lugs.
|
||||
*/
|
||||
private Map<BodyTube, java.util.List<ExternalComponent>> markingGuideItems;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param rocket the rocket instance
|
||||
*/
|
||||
public FinMarkingGuide(Rocket rocket) {
|
||||
super(false);
|
||||
setBackground(Color.white);
|
||||
markingGuideItems = init(rocket);
|
||||
//Max of 2 drawing guides horizontally per page.
|
||||
setSize((int) PrintUnit.INCHES.toPoints(DEFAULT_GUIDE_WIDTH) * 2 + 3 * MARGIN, maxHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the marking guide class by iterating over a rocket and finding all finsets.
|
||||
*
|
||||
* @param component the root rocket component - this is iterated to find all finset and launch lugs
|
||||
*
|
||||
* @return a map of body tubes to lists of finsets and launch lugs.
|
||||
*/
|
||||
private Map<BodyTube, java.util.List<ExternalComponent>> init(Rocket component) {
|
||||
Iterator<RocketComponent> iter = component.iterator(false);
|
||||
Map<BodyTube, java.util.List<ExternalComponent>> results = new LinkedHashMap<BodyTube, List<ExternalComponent>>();
|
||||
BodyTube current = null;
|
||||
int totalHeight = 0;
|
||||
int iterationHeight = 0;
|
||||
int count = 0;
|
||||
|
||||
while (iter.hasNext()) {
|
||||
RocketComponent next = iter.next();
|
||||
if (next instanceof BodyTube) {
|
||||
current = (BodyTube) next;
|
||||
}
|
||||
else if (next instanceof FinSet || next instanceof LaunchLug) {
|
||||
java.util.List<ExternalComponent> list = results.get(current);
|
||||
if (list == null && current != null) {
|
||||
list = new ArrayList<ExternalComponent>();
|
||||
results.put(current, list);
|
||||
|
||||
double radius = current.getOuterRadius();
|
||||
int circumferenceInPoints = (int) PrintUnit.METERS.toPoints(radius * TWO_PI);
|
||||
|
||||
// Find the biggest body tube circumference.
|
||||
if (iterationHeight < (circumferenceInPoints + MARGIN)) {
|
||||
iterationHeight = circumferenceInPoints + MARGIN;
|
||||
}
|
||||
//At most, two marking guides horizontally. After that, move down and back to the left margin.
|
||||
count++;
|
||||
if (count % 2 == 0) {
|
||||
totalHeight += iterationHeight;
|
||||
iterationHeight = 0;
|
||||
}
|
||||
}
|
||||
if (list != null) {
|
||||
list.add((ExternalComponent) next);
|
||||
}
|
||||
}
|
||||
}
|
||||
maxHeight = totalHeight + iterationHeight;
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a generated image of the fin marking guide. May then be used wherever AWT images can be used, or
|
||||
* converted to another image/picture format and used accordingly.
|
||||
*
|
||||
* @return an awt image of the fin marking guide
|
||||
*/
|
||||
public Image createImage() {
|
||||
int width = getWidth() + 25;
|
||||
int height = getHeight() + 25;
|
||||
// Create a buffered image in which to draw
|
||||
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||
// Create a graphics context on the buffered image
|
||||
Graphics2D g2d = bufferedImage.createGraphics();
|
||||
// Draw graphics
|
||||
g2d.setBackground(Color.white);
|
||||
g2d.clearRect(0, 0, width, height);
|
||||
paintComponent(g2d);
|
||||
// Graphics context no longer needed so dispose it
|
||||
g2d.dispose();
|
||||
return bufferedImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* ---------------------- Page Edge --------------------------------------------------------
|
||||
* | ^
|
||||
* | |
|
||||
* |
|
||||
* | y
|
||||
* |
|
||||
* | |
|
||||
* P v
|
||||
* a --- +----------------------------+ ------------
|
||||
* g<------^-- x ------------>+ + ^
|
||||
* e | + + |
|
||||
* | + + baseYOffset
|
||||
* E | + + v
|
||||
* d | +<----------Fin------------->+ -------------
|
||||
* g | + +
|
||||
* e | + +
|
||||
* | | + +
|
||||
* | | + +
|
||||
* | | + + baseSpacing
|
||||
* | | + +
|
||||
* | | + +
|
||||
* | | + +
|
||||
* | | + +
|
||||
* | | +<----------Fin------------->+ --------------
|
||||
* | | + +
|
||||
* | circumferenceInPoints + +
|
||||
* | | + +
|
||||
* | | + +
|
||||
* | | + + baseSpacing
|
||||
* | | +<------Launch Lug --------->+ -----
|
||||
* | | + + \
|
||||
* | | + + + yLLOffset
|
||||
* | | + + /
|
||||
* | | +<----------Fin------------->+ --------------
|
||||
* | | + + ^
|
||||
* | | + + |
|
||||
* | | + + baseYOffset
|
||||
* | v + + v
|
||||
* | --- +----------------------------+ --------------
|
||||
*
|
||||
* |<-------- width ----------->|
|
||||
*
|
||||
* yLLOffset is computed from the difference between the base rotation of the fin and the radial direction of the
|
||||
* lug.
|
||||
*
|
||||
* Note: There is a current limitation that a tube with multiple launch lugs may not render the lug lines
|
||||
* correctly.
|
||||
* </pre>
|
||||
*
|
||||
* @param g the Graphics context
|
||||
*/
|
||||
@Override
|
||||
public void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
paintFinMarkingGuide(g2);
|
||||
}
|
||||
|
||||
private void paintFinMarkingGuide(Graphics2D g2) {
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
||||
RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
g2.setColor(Color.BLACK);
|
||||
g2.setStroke(thinStroke);
|
||||
int x = MARGIN;
|
||||
int y = MARGIN;
|
||||
|
||||
int width = (int) PrintUnit.INCHES.toPoints(DEFAULT_GUIDE_WIDTH);
|
||||
|
||||
int column = 0;
|
||||
|
||||
for (BodyTube next : markingGuideItems.keySet()) {
|
||||
double circumferenceInPoints = PrintUnit.METERS.toPoints((next.getOuterRadius() + PAPER_THICKNESS_IN_METERS) *
|
||||
TWO_PI);
|
||||
List<ExternalComponent> componentList = markingGuideItems.get(next);
|
||||
//Don't draw the lug if there are no fins.
|
||||
if (hasFins(componentList)) {
|
||||
|
||||
drawMarkingGuide(g2, x, y, (int) Math.ceil(circumferenceInPoints), width);
|
||||
|
||||
//Sort so that fins always precede lugs
|
||||
sort(componentList);
|
||||
|
||||
boolean hasMultipleComponents = componentList.size() > 1;
|
||||
|
||||
double baseSpacing = 0d;
|
||||
double baseYOrigin = 0;
|
||||
double finRadial = 0d;
|
||||
int yFirstFin = y;
|
||||
int yLastFin = y;
|
||||
boolean firstFinSet = true;
|
||||
|
||||
//fin1: 42 fin2: 25
|
||||
for (ExternalComponent externalComponent : componentList) {
|
||||
if (externalComponent instanceof FinSet) {
|
||||
FinSet fins = (FinSet) externalComponent;
|
||||
int finCount = fins.getFinCount();
|
||||
int offset = 0;
|
||||
baseSpacing = (circumferenceInPoints / finCount);
|
||||
double baseRotation = fins.getBaseRotation();
|
||||
if (!firstFinSet) {
|
||||
//Adjust the rotation to a positive number.
|
||||
while (baseRotation < 0) {
|
||||
baseRotation += TWO_PI / finCount;
|
||||
}
|
||||
offset = computeYOffset(y, circumferenceInPoints, baseSpacing, baseYOrigin, finRadial, baseRotation);
|
||||
}
|
||||
else {
|
||||
//baseYOrigin is the distance from the top of the marking guide to the first fin of the first fin set.
|
||||
//This measurement is used to base all subsequent finsets and lugs off of.
|
||||
baseYOrigin = baseSpacing / 2;
|
||||
offset = (int) (baseYOrigin) + y;
|
||||
firstFinSet = false;
|
||||
}
|
||||
yFirstFin = y;
|
||||
yLastFin = y;
|
||||
finRadial = baseRotation;
|
||||
|
||||
//Draw the fin marking lines.
|
||||
for (int fin = 0; fin < finCount; fin++) {
|
||||
if (fin > 0) {
|
||||
offset += baseSpacing;
|
||||
yLastFin = offset;
|
||||
}
|
||||
else {
|
||||
yFirstFin = offset;
|
||||
}
|
||||
drawDoubleArrowLine(g2, x, offset, x + width, offset);
|
||||
// if (hasMultipleComponents) {
|
||||
g2.drawString(externalComponent.getName(), x + (width / 3), offset - 2);
|
||||
// }
|
||||
}
|
||||
}
|
||||
else if (externalComponent instanceof LaunchLug) {
|
||||
LaunchLug lug = (LaunchLug) externalComponent;
|
||||
double yLLOffset = (lug.getRadialDirection() - finRadial) / TWO_PI;
|
||||
//The placement of the lug line must respect the boundary of the outer marking guide. In order
|
||||
//to do that, place it above or below either the top or bottom fin line, based on the difference
|
||||
//between their rotational directions.
|
||||
if (yLLOffset < 0) {
|
||||
yLLOffset = yLLOffset * circumferenceInPoints + yLastFin;
|
||||
}
|
||||
else {
|
||||
yLLOffset = yLLOffset * circumferenceInPoints + yFirstFin;
|
||||
}
|
||||
drawDoubleArrowLine(g2, x, (int) yLLOffset, x + width, (int) yLLOffset);
|
||||
g2.drawString(lug.getName(), x + (width / 3), (int) yLLOffset - 2);
|
||||
|
||||
}
|
||||
}
|
||||
//Only if the tube has a lug or multiple finsets does the orientation of the marking guide matter. So print 'Front'.
|
||||
if (hasMultipleComponents) {
|
||||
drawFrontIndication(g2, x, y, (int) baseSpacing, (int) circumferenceInPoints, width);
|
||||
}
|
||||
|
||||
//At most, two marking guides horizontally. After that, move down and back to the left margin.
|
||||
column++;
|
||||
if (column % 2 == 0) {
|
||||
x = MARGIN;
|
||||
y += circumferenceInPoints + MARGIN;
|
||||
}
|
||||
else {
|
||||
x += MARGIN + width;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the y offset for the next fin line.
|
||||
*
|
||||
* @param y the top margin
|
||||
* @param circumferenceInPoints the circumference (height) of the guide
|
||||
* @param baseSpacing the circumference / fin count
|
||||
* @param baseYOrigin the offset from the top of the guide to the first fin of the first fin set drawn
|
||||
* @param prevBaseRotation the rotation of the previous finset
|
||||
* @param baseRotation the rotation of the current finset
|
||||
*
|
||||
* @return number of points from the top of the marking guide to the line to be drawn
|
||||
*/
|
||||
private int computeYOffset(int y, double circumferenceInPoints, double baseSpacing, double baseYOrigin, double prevBaseRotation, double baseRotation) {
|
||||
int offset;
|
||||
double finRadialDifference = (baseRotation - prevBaseRotation) / TWO_PI;
|
||||
//If the fin line would be off the top of the marking guide, then readjust.
|
||||
if (baseYOrigin + finRadialDifference * circumferenceInPoints < 0) {
|
||||
offset = (int) (baseYOrigin + baseSpacing + finRadialDifference * circumferenceInPoints) + y;
|
||||
}
|
||||
else if (baseYOrigin - finRadialDifference * circumferenceInPoints > 0) {
|
||||
offset = (int) (finRadialDifference * circumferenceInPoints + baseYOrigin) + y;
|
||||
}
|
||||
else {
|
||||
offset = (int) (finRadialDifference * circumferenceInPoints - baseYOrigin) + y;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the list contains a FinSet.
|
||||
*
|
||||
* @param list a list of ExternalComponent
|
||||
*
|
||||
* @return true if the list contains at least one FinSet
|
||||
*/
|
||||
private boolean hasFins(List<ExternalComponent> list) {
|
||||
for (ExternalComponent externalComponent : list) {
|
||||
if (externalComponent instanceof FinSet) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort a list of ExternalComponent in-place. Forces FinSets to precede Launch Lugs.
|
||||
*
|
||||
* @param componentList a list of ExternalComponent
|
||||
*/
|
||||
private void sort(List<ExternalComponent> componentList) {
|
||||
Collections.sort(componentList, new Comparator<ExternalComponent>() {
|
||||
@Override
|
||||
public int compare(ExternalComponent o1, ExternalComponent o2) {
|
||||
if (o1 instanceof FinSet) {
|
||||
return -1;
|
||||
}
|
||||
if (o2 instanceof FinSet) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the marking guide outline.
|
||||
*
|
||||
* @param g2 the graphics context
|
||||
* @param x the starting x coordinate
|
||||
* @param y the starting y coordinate
|
||||
* @param length the length, or height, in print units of the marking guide; should be equivalent to the outer tube
|
||||
* circumference
|
||||
* @param width the width of the marking guide in print units; somewhat arbitrary
|
||||
*/
|
||||
private void drawMarkingGuide(Graphics2D g2, int x, int y, int length, int width) {
|
||||
Path2D outline = new Path2D.Float(GeneralPath.WIND_EVEN_ODD, 4);
|
||||
outline.moveTo(x, y);
|
||||
outline.lineTo(width + x, y);
|
||||
outline.lineTo(width + x, length + y);
|
||||
outline.lineTo(x, length + y);
|
||||
outline.closePath();
|
||||
g2.draw(outline);
|
||||
|
||||
//Draw tick marks for alignment, 1/4 of the width in from either edge
|
||||
int fromEdge = (width) / 4;
|
||||
final int tickLength = 8;
|
||||
//Upper left
|
||||
g2.drawLine(x + fromEdge, y, x + fromEdge, y + tickLength);
|
||||
//Upper right
|
||||
g2.drawLine(x + width - fromEdge, y, x + width - fromEdge, y + tickLength);
|
||||
//Lower left
|
||||
g2.drawLine(x + fromEdge, y + length - tickLength, x + fromEdge, y + length);
|
||||
//Lower right
|
||||
g2.drawLine(x + width - fromEdge, y + length - tickLength, x + width - fromEdge, y + length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a vertical string indicating the front of the rocket. This is necessary when a launch lug exists to give
|
||||
* proper orientation of the guide (assuming that the lug is asymmetrically positioned with respect to a fin).
|
||||
*
|
||||
* @param g2 the graphics context
|
||||
* @param x the starting x coordinate
|
||||
* @param y the starting y coordinate
|
||||
* @param spacing the space between fin lines
|
||||
* @param length the length, or height, in print units of the marking guide; should be equivalent to the outer tube
|
||||
* circumference
|
||||
* @param width the width of the marking guide in print units; somewhat arbitrary
|
||||
*/
|
||||
private void drawFrontIndication(Graphics2D g2, int x, int y, int spacing, int length, int width) {
|
||||
//The magic numbers here are fairly arbitrary. They're chosen in a manner that best positions 'Front' to be
|
||||
//readable, without going to complex string layout prediction logic.
|
||||
int rotateX = x + width - 16;
|
||||
int rotateY = y + (int) (spacing * 1.5) + 20;
|
||||
if (rotateY > y + length + 14) {
|
||||
rotateY = y + length / 2 - 10;
|
||||
}
|
||||
g2.translate(rotateX, rotateY);
|
||||
g2.rotate(Math.PI / 2);
|
||||
g2.drawString(trans.get("FinMarkingGuide.lbl.Front"), 0, 0);
|
||||
g2.rotate(-Math.PI / 2);
|
||||
g2.translate(-rotateX, -rotateY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a horizontal line with arrows on both endpoints. Depicts a fin alignment.
|
||||
*
|
||||
* @param g2 the graphics context
|
||||
* @param x1 the starting x coordinate
|
||||
* @param y1 the starting y coordinate
|
||||
* @param x2 the ending x coordinate
|
||||
* @param y2 the ending y coordinate
|
||||
*/
|
||||
void drawDoubleArrowLine(Graphics2D g2, int x1, int y1, int x2, int y2) {
|
||||
int len = x2 - x1;
|
||||
|
||||
g2.drawLine(x1, y1, x1 + len, y2);
|
||||
g2.fillPolygon(new int[]{x1 + len, x1 + len - ARROW_SIZE, x1 + len - ARROW_SIZE, x1 + len},
|
||||
new int[]{y2, y2 - ARROW_SIZE / 2, y2 + ARROW_SIZE / 2, y2}, 4);
|
||||
|
||||
g2.fillPolygon(new int[]{x1, x1 + ARROW_SIZE, x1 + ARROW_SIZE, x1},
|
||||
new int[]{y1, y1 - ARROW_SIZE / 2, y1 + ARROW_SIZE / 2, y1}, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* The stroke of normal lines.
|
||||
*/
|
||||
private final static BasicStroke thinStroke = new BasicStroke(1.0f);
|
||||
|
||||
/**
|
||||
* The size of the arrow in points.
|
||||
*/
|
||||
private static final int ARROW_SIZE = 10;
|
||||
|
||||
/**
|
||||
* Typical thickness of a piece of printer paper (~20-24 lb paper). Wrapping paper around a tube results in the
|
||||
* radius being increased by the thickness of the paper. The smaller the tube, the more pronounced this becomes as a
|
||||
* percentage of circumference. Using 1/10mm as an approximation here.
|
||||
*/
|
||||
private static final double PAPER_THICKNESS_IN_METERS = PrintUnit.MILLIMETERS.toMeters(0.1d);
|
||||
|
||||
/**
|
||||
* The default guide width in inches.
|
||||
*/
|
||||
public final static double DEFAULT_GUIDE_WIDTH = 3d;
|
||||
|
||||
/**
|
||||
* 2 PI radians (represents a circle).
|
||||
*/
|
||||
public final static double TWO_PI = 2 * Math.PI;
|
||||
public final static double PI = Math.PI;
|
||||
|
||||
/**
|
||||
* The I18N translator.
|
||||
*/
|
||||
private static final Translator trans = Application.getTranslator();
|
||||
|
||||
/**
|
||||
* The margin.
|
||||
*/
|
||||
private static final int MARGIN = (int) PrintUnit.INCHES.toPoints(0.25f);
|
||||
|
||||
/**
|
||||
* The height (circumference) of the biggest body tube with a finset.
|
||||
*/
|
||||
private int maxHeight = 0;
|
||||
|
||||
/**
|
||||
* A map of body tubes, to a list of components that contains finsets and launch lugs.
|
||||
*/
|
||||
private Map<BodyTube, java.util.List<ExternalComponent>> markingGuideItems;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param rocket the rocket instance
|
||||
*/
|
||||
public FinMarkingGuide(Rocket rocket) {
|
||||
super(false);
|
||||
setBackground(Color.white);
|
||||
markingGuideItems = init(rocket);
|
||||
//Max of 2 drawing guides horizontally per page.
|
||||
setSize((int) PrintUnit.INCHES.toPoints(DEFAULT_GUIDE_WIDTH) * 2 + 3 * MARGIN, maxHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the marking guide class by iterating over a rocket and finding all finsets.
|
||||
*
|
||||
* @param component the root rocket component - this is iterated to find all finset and launch lugs
|
||||
*
|
||||
* @return a map of body tubes to lists of finsets and launch lugs.
|
||||
*/
|
||||
private Map<BodyTube, java.util.List<ExternalComponent>> init(Rocket component) {
|
||||
Iterator<RocketComponent> iter = component.iterator(false);
|
||||
Map<BodyTube, java.util.List<ExternalComponent>> results = new LinkedHashMap<BodyTube, List<ExternalComponent>>();
|
||||
BodyTube current = null;
|
||||
int totalHeight = 0;
|
||||
int iterationHeight = 0;
|
||||
int count = 0;
|
||||
|
||||
while (iter.hasNext()) {
|
||||
RocketComponent next = iter.next();
|
||||
if (next instanceof BodyTube) {
|
||||
current = (BodyTube) next;
|
||||
}
|
||||
else if (next instanceof FinSet || next instanceof LaunchLug) {
|
||||
java.util.List<ExternalComponent> list = results.get(current);
|
||||
if (list == null && current != null) {
|
||||
list = new ArrayList<ExternalComponent>();
|
||||
results.put(current, list);
|
||||
|
||||
double radius = current.getOuterRadius();
|
||||
int circumferenceInPoints = (int) PrintUnit.METERS.toPoints(radius * TWO_PI);
|
||||
|
||||
// Find the biggest body tube circumference.
|
||||
if (iterationHeight < (circumferenceInPoints + MARGIN)) {
|
||||
iterationHeight = circumferenceInPoints + MARGIN;
|
||||
}
|
||||
//At most, two marking guides horizontally. After that, move down and back to the left margin.
|
||||
count++;
|
||||
if (count % 2 == 0) {
|
||||
totalHeight += iterationHeight;
|
||||
iterationHeight = 0;
|
||||
}
|
||||
}
|
||||
if (list != null) {
|
||||
list.add((ExternalComponent) next);
|
||||
}
|
||||
}
|
||||
}
|
||||
maxHeight = totalHeight + iterationHeight;
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a generated image of the fin marking guide. May then be used wherever AWT images can be used, or
|
||||
* converted to another image/picture format and used accordingly.
|
||||
*
|
||||
* @return an awt image of the fin marking guide
|
||||
*/
|
||||
public Image createImage() {
|
||||
int width = getWidth() + 25;
|
||||
int height = getHeight() + 25;
|
||||
// Create a buffered image in which to draw
|
||||
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||
// Create a graphics context on the buffered image
|
||||
Graphics2D g2d = bufferedImage.createGraphics();
|
||||
// Draw graphics
|
||||
g2d.setBackground(Color.white);
|
||||
g2d.clearRect(0, 0, width, height);
|
||||
paintComponent(g2d);
|
||||
// Graphics context no longer needed so dispose it
|
||||
g2d.dispose();
|
||||
return bufferedImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* ---------------------- Page Edge --------------------------------------------------------
|
||||
* | ^
|
||||
* | |
|
||||
* |
|
||||
* | y
|
||||
* |
|
||||
* | |
|
||||
* P v
|
||||
* a --- +----------------------------+ <- radialOrigin (in radians)
|
||||
* g<------^-- x ------------>+ +
|
||||
* e | + +
|
||||
* | + +
|
||||
* E | + +
|
||||
* d | +<----------Fin------------->+ <- y+ (finRadialPosition - radialOrigin) / TWO_PI * circumferenceInPoints
|
||||
* g | + +
|
||||
* e | + +
|
||||
* | | + +
|
||||
* | | + +
|
||||
* | | + +
|
||||
* | | + +
|
||||
* | | + +
|
||||
* | | + +
|
||||
* | | + +
|
||||
* | | +<----------Fin------------->+
|
||||
* | | + +
|
||||
* | circumferenceInPoints + +
|
||||
* | | + +
|
||||
* | | + +
|
||||
* | | + +
|
||||
* | | +<------Launch Lug --------->+
|
||||
* | | + +
|
||||
* | | + +
|
||||
* | | + +
|
||||
* | | +<----------Fin------------->+
|
||||
* | | + +
|
||||
* | | + +
|
||||
* | | + +
|
||||
* | v + +
|
||||
* | --- +----------------------------+ <- radialOrigin + TWO_PI
|
||||
*
|
||||
* |<-------- width ----------->|
|
||||
*
|
||||
* yLLOffset is computed from the difference between the base rotation of the fin and the radial direction of the
|
||||
* lug.
|
||||
*
|
||||
* Note: There is a current limitation that a tube with multiple launch lugs may not render the lug lines
|
||||
* correctly.
|
||||
* </pre>
|
||||
*
|
||||
* @param g the Graphics context
|
||||
*/
|
||||
@Override
|
||||
public void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
paintFinMarkingGuide(g2);
|
||||
}
|
||||
|
||||
private void paintFinMarkingGuide(Graphics2D g2) {
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
||||
RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
g2.setColor(Color.BLACK);
|
||||
g2.setStroke(thinStroke);
|
||||
int x = MARGIN;
|
||||
int y = MARGIN;
|
||||
|
||||
int width = (int) PrintUnit.INCHES.toPoints(DEFAULT_GUIDE_WIDTH);
|
||||
|
||||
int column = 0;
|
||||
|
||||
for (BodyTube next : markingGuideItems.keySet()) {
|
||||
double circumferenceInPoints = PrintUnit.METERS.toPoints((next.getOuterRadius() + PAPER_THICKNESS_IN_METERS) *
|
||||
TWO_PI);
|
||||
List<ExternalComponent> componentList = markingGuideItems.get(next);
|
||||
//Don't draw the lug if there are no fins.
|
||||
if (hasFins(componentList)) {
|
||||
|
||||
drawMarkingGuide(g2, x, y, (int) Math.ceil(circumferenceInPoints), width);
|
||||
|
||||
double radialOrigin = findRadialOrigin(componentList);
|
||||
|
||||
boolean hasMultipleComponents = componentList.size() > 1;
|
||||
|
||||
//fin1: 42 fin2: 25
|
||||
for (ExternalComponent externalComponent : componentList) {
|
||||
if (externalComponent instanceof FinSet) {
|
||||
FinSet fins = (FinSet) externalComponent;
|
||||
int finCount = fins.getFinCount();
|
||||
double baseAngularSpacing = (TWO_PI / finCount);
|
||||
double baseAngularOffset = fins.getBaseRotation();
|
||||
//Draw the fin marking lines.
|
||||
for (int fin = 0; fin < finCount; fin++) {
|
||||
double angle = baseAngularOffset + fin * baseAngularSpacing - radialOrigin;
|
||||
// Translate angle into pixels using a linear transformation:
|
||||
// radialOrigin -> y
|
||||
// radialOrigin + TWO_PI -> y + circumferenceInPoints
|
||||
|
||||
while (angle < 0) {
|
||||
angle += TWO_PI;
|
||||
}
|
||||
while (angle > TWO_PI) {
|
||||
angle -= TWO_PI;
|
||||
}
|
||||
|
||||
int offset = (int) Math.round(y + angle / TWO_PI * circumferenceInPoints);
|
||||
|
||||
drawDoubleArrowLine(g2, x, offset, x + width, offset);
|
||||
// if (hasMultipleComponents) {
|
||||
g2.drawString(externalComponent.getName(), x + (width / 3), offset - 2);
|
||||
// }
|
||||
}
|
||||
}
|
||||
else if (externalComponent instanceof LaunchLug) {
|
||||
LaunchLug lug = (LaunchLug) externalComponent;
|
||||
double angle = lug.getRadialDirection() - radialOrigin;
|
||||
while (angle < 0) {
|
||||
angle += TWO_PI;
|
||||
}
|
||||
int yLLOffset = (int) Math.round(y + angle / TWO_PI * circumferenceInPoints);
|
||||
drawDoubleArrowLine(g2, x, (int) yLLOffset, x + width, (int) yLLOffset);
|
||||
g2.drawString(lug.getName(), x + (width / 3), (int) yLLOffset - 2);
|
||||
|
||||
}
|
||||
}
|
||||
//Only if the tube has a lug or multiple finsets does the orientation of the marking guide matter. So print 'Front'.
|
||||
if (hasMultipleComponents) {
|
||||
drawFrontIndication(g2, x, y, 0, (int) circumferenceInPoints, width);
|
||||
}
|
||||
|
||||
//At most, two marking guides horizontally. After that, move down and back to the left margin.
|
||||
column++;
|
||||
if (column % 2 == 0) {
|
||||
x = MARGIN;
|
||||
y += circumferenceInPoints + MARGIN;
|
||||
}
|
||||
else {
|
||||
x += MARGIN + width;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function finds a origin in radians for the template so no component is on the template seam.
|
||||
*
|
||||
* If no fin or launch lug is at 0.0 radians, then the origin is 0. If there is one, then half the distance
|
||||
* between the two are taken.
|
||||
*
|
||||
* @param components
|
||||
* @return
|
||||
*/
|
||||
private double findRadialOrigin(List<ExternalComponent> components) {
|
||||
|
||||
ArrayList<Double> positions = new ArrayList<Double>(3 * components.size());
|
||||
|
||||
for (ExternalComponent component : components) {
|
||||
|
||||
if (component instanceof LaunchLug) {
|
||||
double componentPosition = ((LaunchLug) component).getRadialDirection();
|
||||
|
||||
positions.add(makeZeroTwoPi(componentPosition));
|
||||
}
|
||||
|
||||
if (component instanceof FinSet) {
|
||||
FinSet fins = (FinSet) component;
|
||||
double basePosition = fins.getBaseRotation();
|
||||
double angle = TWO_PI / fins.getFinCount();
|
||||
for (int i = fins.getFinCount(); i > 0; i--) {
|
||||
positions.add(makeZeroTwoPi(basePosition));
|
||||
basePosition += angle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(positions);
|
||||
|
||||
Double[] pos = positions.toArray(new Double[0]);
|
||||
|
||||
if (pos.length == 1) {
|
||||
|
||||
return makeZeroTwoPi(pos[0] + PI);
|
||||
|
||||
}
|
||||
|
||||
double biggestDistance = TWO_PI - pos[pos.length - 1] + pos[0];
|
||||
double center = makeZeroTwoPi(pos[0] - biggestDistance / 2.0);
|
||||
|
||||
for (int i = 1; i < pos.length; i++) {
|
||||
|
||||
double d = pos[i] - pos[i - 1];
|
||||
if (d > biggestDistance) {
|
||||
biggestDistance = d;
|
||||
center = makeZeroTwoPi(pos[i - 1] + biggestDistance / 2.0);
|
||||
}
|
||||
}
|
||||
|
||||
return center;
|
||||
}
|
||||
|
||||
private static double makeZeroTwoPi(double value) {
|
||||
|
||||
double v = value;
|
||||
while (v < 0) {
|
||||
v += TWO_PI;
|
||||
}
|
||||
while (v > TWO_PI) {
|
||||
v -= TWO_PI;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the list contains a FinSet.
|
||||
*
|
||||
* @param list a list of ExternalComponent
|
||||
*
|
||||
* @return true if the list contains at least one FinSet
|
||||
*/
|
||||
private boolean hasFins(List<ExternalComponent> list) {
|
||||
for (ExternalComponent externalComponent : list) {
|
||||
if (externalComponent instanceof FinSet) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the marking guide outline.
|
||||
*
|
||||
* @param g2 the graphics context
|
||||
* @param x the starting x coordinate
|
||||
* @param y the starting y coordinate
|
||||
* @param length the length, or height, in print units of the marking guide; should be equivalent to the outer tube
|
||||
* circumference
|
||||
* @param width the width of the marking guide in print units; somewhat arbitrary
|
||||
*/
|
||||
private void drawMarkingGuide(Graphics2D g2, int x, int y, int length, int width) {
|
||||
Path2D outline = new Path2D.Float(GeneralPath.WIND_EVEN_ODD, 4);
|
||||
outline.moveTo(x, y);
|
||||
outline.lineTo(width + x, y);
|
||||
outline.lineTo(width + x, length + y);
|
||||
outline.lineTo(x, length + y);
|
||||
outline.closePath();
|
||||
g2.draw(outline);
|
||||
|
||||
//Draw tick marks for alignment, 1/4 of the width in from either edge
|
||||
int fromEdge = (width) / 4;
|
||||
final int tickLength = 8;
|
||||
//Upper left
|
||||
g2.drawLine(x + fromEdge, y, x + fromEdge, y + tickLength);
|
||||
//Upper right
|
||||
g2.drawLine(x + width - fromEdge, y, x + width - fromEdge, y + tickLength);
|
||||
//Lower left
|
||||
g2.drawLine(x + fromEdge, y + length - tickLength, x + fromEdge, y + length);
|
||||
//Lower right
|
||||
g2.drawLine(x + width - fromEdge, y + length - tickLength, x + width - fromEdge, y + length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a vertical string indicating the front of the rocket. This is necessary when a launch lug exists to give
|
||||
* proper orientation of the guide (assuming that the lug is asymmetrically positioned with respect to a fin).
|
||||
*
|
||||
* @param g2 the graphics context
|
||||
* @param x the starting x coordinate
|
||||
* @param y the starting y coordinate
|
||||
* @param spacing the space between fin lines
|
||||
* @param length the length, or height, in print units of the marking guide; should be equivalent to the outer tube
|
||||
* circumference
|
||||
* @param width the width of the marking guide in print units; somewhat arbitrary
|
||||
*/
|
||||
private void drawFrontIndication(Graphics2D g2, int x, int y, int spacing, int length, int width) {
|
||||
//The magic numbers here are fairly arbitrary. They're chosen in a manner that best positions 'Front' to be
|
||||
//readable, without going to complex string layout prediction logic.
|
||||
int rotateX = x + width - 16;
|
||||
int rotateY = y + (int) (spacing * 1.5) + 20;
|
||||
if (rotateY > y + length + 14) {
|
||||
rotateY = y + length / 2 - 10;
|
||||
}
|
||||
g2.translate(rotateX, rotateY);
|
||||
g2.rotate(Math.PI / 2);
|
||||
g2.drawString(trans.get("FinMarkingGuide.lbl.Front"), 0, 0);
|
||||
g2.rotate(-Math.PI / 2);
|
||||
g2.translate(-rotateX, -rotateY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a horizontal line with arrows on both endpoints. Depicts a fin alignment.
|
||||
*
|
||||
* @param g2 the graphics context
|
||||
* @param x1 the starting x coordinate
|
||||
* @param y1 the starting y coordinate
|
||||
* @param x2 the ending x coordinate
|
||||
* @param y2 the ending y coordinate
|
||||
*/
|
||||
void drawDoubleArrowLine(Graphics2D g2, int x1, int y1, int x2, int y2) {
|
||||
int len = x2 - x1;
|
||||
|
||||
g2.drawLine(x1, y1, x1 + len, y2);
|
||||
g2.fillPolygon(new int[] { x1 + len, x1 + len - ARROW_SIZE, x1 + len - ARROW_SIZE, x1 + len },
|
||||
new int[] { y2, y2 - ARROW_SIZE / 2, y2 + ARROW_SIZE / 2, y2 }, 4);
|
||||
|
||||
g2.fillPolygon(new int[] { x1, x1 + ARROW_SIZE, x1 + ARROW_SIZE, x1 },
|
||||
new int[] { y1, y1 - ARROW_SIZE / 2, y1 + ARROW_SIZE / 2, y1 }, 4);
|
||||
}
|
||||
}
|
@ -19,9 +19,25 @@ public class DesignationComparator implements Comparator<String> {
|
||||
COLLATOR = Collator.getInstance(Locale.US);
|
||||
COLLATOR.setStrength(Collator.PRIMARY);
|
||||
}
|
||||
|
||||
private Pattern pattern =
|
||||
Pattern.compile("^([0-9][0-9]+|1/([1-8]))?([a-zA-Z])([0-9]+)(.*?)$");
|
||||
|
||||
/*
|
||||
* Regexp to parse the multitude of designations. Supported types:
|
||||
*
|
||||
* 1/4A4...
|
||||
* 1/2A4...
|
||||
* A4...
|
||||
* 132G100...
|
||||
* 132-G100...
|
||||
*
|
||||
* Capture groups:
|
||||
* 1 = garbage (stuff before impulse class)
|
||||
* 2 = divisor number for 1/2A, 1/4A etc, otherwise null
|
||||
* 3 = impulse class letter
|
||||
* 4 = average thrust
|
||||
* 5 = stuff after thrust number
|
||||
*/
|
||||
private Pattern pattern =
|
||||
Pattern.compile("^([0-9]+-?|1/([1-8]))?([a-zA-Z])([0-9,]+)(.*?)$");
|
||||
|
||||
@Override
|
||||
public int compare(String o1, String o2) {
|
||||
@ -32,13 +48,13 @@ public class DesignationComparator implements Comparator<String> {
|
||||
m2 = pattern.matcher(o2);
|
||||
|
||||
if (m1.find() && m2.find()) {
|
||||
|
||||
|
||||
String o1Class = m1.group(3);
|
||||
int o1Thrust = Integer.parseInt(m1.group(4));
|
||||
int o1Thrust = Integer.parseInt(m1.group(4).replaceAll(",", ""));
|
||||
String o1Extra = m1.group(5);
|
||||
|
||||
String o2Class = m2.group(3);
|
||||
int o2Thrust = Integer.parseInt(m2.group(4));
|
||||
int o2Thrust = Integer.parseInt(m2.group(4).replaceAll(",", ""));
|
||||
String o2Extra = m2.group(5);
|
||||
|
||||
// 1. Motor class
|
||||
@ -46,18 +62,18 @@ public class DesignationComparator implements Comparator<String> {
|
||||
// 1/2A and 1/4A comparison
|
||||
String sub1 = m1.group(2);
|
||||
String sub2 = m2.group(2);
|
||||
|
||||
|
||||
if (sub1 != null || sub2 != null) {
|
||||
if (sub1 == null)
|
||||
sub1 = "1";
|
||||
if (sub2 == null)
|
||||
sub2 = "1";
|
||||
value = -COLLATOR.compare(sub1,sub2);
|
||||
value = -COLLATOR.compare(sub1, sub2);
|
||||
if (value != 0)
|
||||
return value;
|
||||
}
|
||||
}
|
||||
value = COLLATOR.compare(o1Class,o2Class);
|
||||
value = COLLATOR.compare(o1Class, o2Class);
|
||||
if (value != 0)
|
||||
return value;
|
||||
|
||||
|
16
core/src/net/sf/openrocket/plugin/AnnotationFinder.java
Normal file
16
core/src/net/sf/openrocket/plugin/AnnotationFinder.java
Normal file
@ -0,0 +1,16 @@
|
||||
package net.sf.openrocket.plugin;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Interface for finding annotated classes from the class path.
|
||||
*/
|
||||
public interface AnnotationFinder {
|
||||
|
||||
/**
|
||||
* Return a list of all types (classes and interfaces) that are annotated
|
||||
* with the provided annotation.
|
||||
*/
|
||||
public List<Class<?>> findAnnotatedTypes(Class<?> annotation);
|
||||
|
||||
}
|
91
core/src/net/sf/openrocket/plugin/AnnotationFinderImpl.java
Normal file
91
core/src/net/sf/openrocket/plugin/AnnotationFinderImpl.java
Normal file
@ -0,0 +1,91 @@
|
||||
package net.sf.openrocket.plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sf.openrocket.util.BugException;
|
||||
import net.sf.openrocket.util.JarUtil;
|
||||
import eu.infomas.annotation.AnnotationDetector;
|
||||
import eu.infomas.annotation.AnnotationDetector.TypeReporter;
|
||||
|
||||
/**
|
||||
* An AnnotationFinder that uses annotation-detector library to scan
|
||||
* the class path. Compatible with the JIJ loader.
|
||||
*/
|
||||
public class AnnotationFinderImpl implements AnnotationFinder {
|
||||
|
||||
@Override
|
||||
public List<Class<?>> findAnnotatedTypes(Class<?> annotation) {
|
||||
final List<Class<?>> classes = new ArrayList<Class<?>>();
|
||||
|
||||
TypeReporter reporter = new ListReporter(classes);
|
||||
final AnnotationDetector cf = new AnnotationDetector(reporter);
|
||||
try {
|
||||
ClassLoader loader = this.getClass().getClassLoader();
|
||||
if (loader instanceof URLClassLoader) {
|
||||
|
||||
/*
|
||||
* In case of URLClassLoader (which may be our own instantiation)
|
||||
* use the URLs from there, as java.class.path may not be up-to-date.
|
||||
*/
|
||||
|
||||
URLClassLoader urlClassLoader = (URLClassLoader) loader;
|
||||
URL[] urls = urlClassLoader.getURLs();
|
||||
|
||||
List<File> files = new ArrayList<File>();
|
||||
for (URL url : urls) {
|
||||
if (url.getProtocol().equals("file")) {
|
||||
files.add(JarUtil.urlToFile(url));
|
||||
}
|
||||
}
|
||||
|
||||
cf.detect(files.toArray(new File[0]));
|
||||
} else {
|
||||
|
||||
/*
|
||||
* If not using a URLClassLoader, just do the default.
|
||||
*/
|
||||
cf.detect();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new BugException("Unable to search class path", e);
|
||||
}
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
|
||||
private static class ListReporter implements TypeReporter {
|
||||
private final List<Class<?>> classes;
|
||||
private final Set<String> names = new HashSet<String>();
|
||||
|
||||
public ListReporter(List<Class<?>> classes) {
|
||||
this.classes = classes;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Class<? extends Annotation>[] annotations() {
|
||||
return new Class[] { Plugin.class };
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportTypeAnnotation(Class<? extends Annotation> annotation, String className) {
|
||||
if (names.add(className)) {
|
||||
try {
|
||||
classes.add(this.getClass().getClassLoader().loadClass(className));
|
||||
} catch (ClassNotFoundException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
package net.sf.openrocket.plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class JIJ {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String cp = System.getProperty("java.class.path");
|
||||
String[] cps = cp.split(File.pathSeparator);
|
||||
|
||||
URL[] urls = new URL[cps.length + 1];
|
||||
for (int i = 0; i < cps.length; i++) {
|
||||
urls[i] = new File(cps[i]).toURI().toURL();
|
||||
}
|
||||
urls[cps.length] = new File("/home/sampo/Projects/OpenRocket/core/example.jar").toURI().toURL();
|
||||
|
||||
System.out.println("Classpath: " + Arrays.toString(urls));
|
||||
|
||||
URLClassLoader loader = new URLClassLoader(urls, null);
|
||||
Class<?> c = loader.loadClass("net.sf.openrocket.plugin.Test");
|
||||
Method m = c.getMethod("main", args.getClass());
|
||||
m.invoke(null, (Object) args);
|
||||
}
|
||||
|
||||
}
|
@ -6,8 +6,10 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation that defines an interface to be a plugin interface.
|
||||
* Plugin interfaces are automatically discovered from plugin JARs and
|
||||
* Annotation that defines an interface to be a plugin interface and
|
||||
* classes as plugin implementations.
|
||||
* <p>
|
||||
* Plugin interfaces are automatically discovered from the classpath and
|
||||
* registered as plugins in Guice.
|
||||
*
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
|
@ -1,14 +1,9 @@
|
||||
package net.sf.openrocket.plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.multibindings.Multibinder;
|
||||
@ -21,63 +16,46 @@ import com.google.inject.multibindings.Multibinder;
|
||||
*/
|
||||
public class PluginModule extends AbstractModule {
|
||||
|
||||
private final List<File> jars;
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
private Map<Class<?>, Multibinder<?>> binders = new HashMap<Class<?>, Multibinder<?>>();
|
||||
|
||||
|
||||
/**
|
||||
* Sole constructor.
|
||||
*
|
||||
* @param jars the JAR files to search for plugins
|
||||
* @param classLoader the class loader used to load classes from the JAR files
|
||||
*/
|
||||
public PluginModule(List<File> jars, ClassLoader classLoader) {
|
||||
this.jars = jars;
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
for (File jar : jars) {
|
||||
List<String> classNames = readClassNames(jar);
|
||||
for (String className : classNames) {
|
||||
checkForPlugin(className);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private AnnotationFinder finder = new AnnotationFinderImpl();
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void checkForPlugin(String className) {
|
||||
try {
|
||||
@Override
|
||||
protected void configure() {
|
||||
|
||||
List<Class<?>> classes = finder.findAnnotatedTypes(Plugin.class);
|
||||
List<Class<?>> interfaces = new ArrayList<Class<?>>();
|
||||
List<Class<?>> unusedInterfaces;
|
||||
|
||||
// Find plugin interfaces
|
||||
for (Class<?> c : classes) {
|
||||
if (c.isInterface()) {
|
||||
interfaces.add(c);
|
||||
}
|
||||
}
|
||||
unusedInterfaces = new ArrayList<Class<?>>(interfaces);
|
||||
|
||||
// Find plugin implementations
|
||||
for (Class<?> c : classes) {
|
||||
if (c.isInterface())
|
||||
continue;
|
||||
|
||||
Class<?> c = classLoader.loadClass(className);
|
||||
for (Class<?> intf : c.getInterfaces()) {
|
||||
System.out.println("Testing class " + c + " interface " + intf);
|
||||
|
||||
if (isPluginInterface(intf)) {
|
||||
System.out.println("BINDING");
|
||||
if (interfaces.contains(intf)) {
|
||||
// Ugly hack to enable dynamic binding... Can this be done type-safely?
|
||||
Multibinder<Object> binder = (Multibinder<Object>) findBinder(intf);
|
||||
binder.addBinding().to(c);
|
||||
unusedInterfaces.remove(intf);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (ClassNotFoundException e) {
|
||||
System.err.println("Could not load class " + className + ": " + e);
|
||||
}
|
||||
|
||||
// TODO: Unused plugin interfaces should be bound to an empty set - how?
|
||||
}
|
||||
|
||||
|
||||
private boolean isPluginInterface(Class<?> intf) {
|
||||
return intf.isAnnotationPresent(Plugin.class);
|
||||
}
|
||||
|
||||
|
||||
private Multibinder<?> findBinder(Class<?> intf) {
|
||||
Multibinder<?> binder = binders.get(intf);
|
||||
if (binder == null) {
|
||||
@ -87,36 +65,4 @@ public class PluginModule extends AbstractModule {
|
||||
return binder;
|
||||
}
|
||||
|
||||
|
||||
private List<String> readClassNames(File jar) {
|
||||
List<String> classNames = new ArrayList<String>();
|
||||
|
||||
JarFile file = null;
|
||||
try {
|
||||
file = new JarFile(jar);
|
||||
Enumeration<JarEntry> entries = file.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
String name = entry.getName();
|
||||
if (name.toLowerCase().endsWith(".class") && !name.contains("$")) {
|
||||
name = name.substring(0, name.length() - 6);
|
||||
name = name.replace('/', '.');
|
||||
classNames.add(name);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("Error reading JAR file " + jar);
|
||||
} finally {
|
||||
if (file != null) {
|
||||
try {
|
||||
file.close();
|
||||
} catch (IOException e) {
|
||||
// Curse all checked exceptions...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return classNames;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,47 +0,0 @@
|
||||
package net.sf.openrocket.plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class Test {
|
||||
|
||||
@Inject
|
||||
private Set<ExamplePlugin> impls;
|
||||
|
||||
|
||||
public void run() {
|
||||
System.out.println("Plugin count: " + impls.size());
|
||||
for (ExamplePlugin i : impls) {
|
||||
i.doit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) throws MalformedURLException {
|
||||
// Properties p = System.getProperties();
|
||||
// Enumeration<Object> e = p.keys();
|
||||
// while (e.hasMoreElements()) {
|
||||
// Object key = e.nextElement();
|
||||
// Object value = p.get(key);
|
||||
// System.out.println(key + " = " + value);
|
||||
// }
|
||||
|
||||
List<File> jars = Arrays.asList(new File("/home/sampo/Projects/OpenRocket/core/example.jar"));
|
||||
URL[] urls = { new File("/home/sampo/Projects/OpenRocket/core/example.jar").toURI().toURL() };
|
||||
ClassLoader classLoader = new URLClassLoader(urls);
|
||||
|
||||
classLoader = Test.class.getClassLoader();
|
||||
|
||||
Injector injector = Guice.createInjector(new PluginModule(jars, classLoader));
|
||||
injector.getInstance(Test.class).run();
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ import net.sf.openrocket.models.gravity.GravityModel;
|
||||
import net.sf.openrocket.models.gravity.WGSGravityModel;
|
||||
import net.sf.openrocket.models.wind.PinkNoiseWindModel;
|
||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
import net.sf.openrocket.util.ChangeSource;
|
||||
import net.sf.openrocket.util.GeodeticComputationStrategy;
|
||||
@ -404,7 +405,7 @@ public class SimulationOptions implements ChangeSource, Cloneable {
|
||||
|
||||
if (src.rocket.hasMotors(src.motorID)) {
|
||||
// Try to find a closely matching motor ID
|
||||
MotorDescriptionSubstitutor formatter = new MotorDescriptionSubstitutor();
|
||||
MotorDescriptionSubstitutor formatter = Application.getInjector().getInstance(MotorDescriptionSubstitutor.class);
|
||||
|
||||
String motorDesc = formatter.substitute(MotorDescriptionSubstitutor.SUBSTITUTION, src.rocket, src.motorID);
|
||||
String matchID = null;
|
||||
|
@ -16,7 +16,6 @@ import net.sf.openrocket.logging.LogHelper;
|
||||
import net.sf.openrocket.logging.LogLevel;
|
||||
import net.sf.openrocket.logging.LogLevelBufferLogger;
|
||||
import net.sf.openrocket.logging.PrintStreamLogger;
|
||||
import net.sf.openrocket.plugin.PluginHelper;
|
||||
import net.sf.openrocket.plugin.PluginModule;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
@ -135,26 +134,27 @@ public class GuiceStartup {
|
||||
" " + LOG_STDERR_PROPERTY + "=" + System.getProperty(LOG_STDERR_PROPERTY) + ")";
|
||||
log.info(str);
|
||||
|
||||
|
||||
|
||||
|
||||
//Replace System.err with a PrintStream that logs lines to DEBUG, or VBOSE if they are indented.
|
||||
//If debug info is not being output to the console then the data is both logged and written to
|
||||
//stderr.
|
||||
final boolean writeToStderr = !( printer.getOutput(LogLevel.DEBUG) == System.out || printer.getOutput(LogLevel.DEBUG) == System.err);
|
||||
final PrintStream stdErr = System.err;
|
||||
final boolean writeToStderr = !(printer.getOutput(LogLevel.DEBUG) == System.out || printer.getOutput(LogLevel.DEBUG) == System.err);
|
||||
final PrintStream stdErr = System.err;
|
||||
System.setErr(new PrintStream(new OutputStream() {
|
||||
StringBuilder currentLine = new StringBuilder();
|
||||
|
||||
@Override
|
||||
public synchronized void write(int b) throws IOException {
|
||||
if ( writeToStderr ){
|
||||
if (writeToStderr) {
|
||||
//Write to real stderr
|
||||
stdErr.write(b);
|
||||
}
|
||||
if (b == '\r' || b == '\n') {
|
||||
//Line is complete, log it
|
||||
if (currentLine.toString().trim().length() > 0){
|
||||
if (currentLine.toString().trim().length() > 0) {
|
||||
String s = currentLine.toString();
|
||||
if ( Character.isWhitespace(s.charAt(0))){
|
||||
if (Character.isWhitespace(s.charAt(0))) {
|
||||
log.verbose(currentLine.toString());
|
||||
} else {
|
||||
log.debug(currentLine.toString());
|
||||
@ -247,7 +247,7 @@ public class GuiceStartup {
|
||||
|
||||
private static Injector initializeGuice() {
|
||||
Module applicationModule = new ApplicationModule();
|
||||
Module pluginModule = new PluginModule(PluginHelper.getPluginJars(), GuiceStartup.class.getClassLoader());
|
||||
Module pluginModule = new PluginModule();
|
||||
|
||||
return Guice.createInjector(applicationModule, pluginModule);
|
||||
}
|
||||
|
@ -1,6 +1,12 @@
|
||||
package net.sf.openrocket.unit;
|
||||
|
||||
import static net.sf.openrocket.util.Chars.*;
|
||||
import static net.sf.openrocket.util.Chars.CUBED;
|
||||
import static net.sf.openrocket.util.Chars.DEGREE;
|
||||
import static net.sf.openrocket.util.Chars.DOT;
|
||||
import static net.sf.openrocket.util.Chars.MICRO;
|
||||
import static net.sf.openrocket.util.Chars.PERMILLE;
|
||||
import static net.sf.openrocket.util.Chars.SQUARED;
|
||||
import static net.sf.openrocket.util.Chars.ZWSP;
|
||||
import static net.sf.openrocket.util.MathUtil.pow2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -86,6 +92,7 @@ public class UnitGroup {
|
||||
static {
|
||||
UNITS_NONE = new UnitGroup();
|
||||
UNITS_NONE.addUnit(Unit.NOUNIT);
|
||||
UNITS_NONE.setDefaultUnit(0);
|
||||
|
||||
UNITS_ENERGY = new UnitGroup();
|
||||
UNITS_ENERGY.addUnit(new GeneralUnit(1, "J"));
|
||||
@ -127,9 +134,9 @@ public class UnitGroup {
|
||||
UNITS_LENGTH.setDefaultUnit(1);
|
||||
|
||||
UNITS_MOTOR_DIMENSIONS = new UnitGroup();
|
||||
UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(1, "m")); // just added
|
||||
UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(0.001, "mm"));
|
||||
UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(0.01, "cm"));
|
||||
UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(1, "m"));
|
||||
UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(0.0254, "in"));
|
||||
UNITS_MOTOR_DIMENSIONS.setDefaultUnit(0);
|
||||
|
||||
@ -140,6 +147,7 @@ public class UnitGroup {
|
||||
UNITS_DISTANCE.addUnit(new GeneralUnit(0.9144, "yd"));
|
||||
UNITS_DISTANCE.addUnit(new GeneralUnit(1609.344, "mi"));
|
||||
UNITS_DISTANCE.addUnit(new GeneralUnit(1852, "nmi"));
|
||||
UNITS_DISTANCE.setDefaultUnit(0);
|
||||
|
||||
UNITS_ALL_LENGTHS = new UnitGroup();
|
||||
UNITS_ALL_LENGTHS.addUnit(new GeneralUnit(0.001, "mm"));
|
||||
@ -164,15 +172,16 @@ public class UnitGroup {
|
||||
|
||||
|
||||
UNITS_STABILITY = new UnitGroup();
|
||||
UNITS_STABILITY.addUnit(new GeneralUnit(1, "m"));
|
||||
UNITS_STABILITY.addUnit(new GeneralUnit(0.001, "mm"));
|
||||
UNITS_STABILITY.addUnit(new GeneralUnit(0.01, "cm"));
|
||||
UNITS_STABILITY.addUnit(new GeneralUnit(1, "m"));
|
||||
UNITS_STABILITY.addUnit(new GeneralUnit(0.0254, "in"));
|
||||
UNITS_STABILITY.addUnit(new CaliberUnit((Rocket) null));
|
||||
UNITS_STABILITY.setDefaultUnit(3);
|
||||
UNITS_STABILITY.setDefaultUnit(4);
|
||||
|
||||
UNITS_STABILITY_CALIBERS = new UnitGroup();
|
||||
UNITS_STABILITY_CALIBERS.addUnit(new GeneralUnit(1, "cal"));
|
||||
UNITS_STABILITY_CALIBERS.setDefaultUnit(0);
|
||||
|
||||
|
||||
UNITS_VELOCITY = new UnitGroup();
|
||||
@ -180,23 +189,27 @@ public class UnitGroup {
|
||||
UNITS_VELOCITY.addUnit(new GeneralUnit(1 / 3.6, "km/h"));
|
||||
UNITS_VELOCITY.addUnit(new GeneralUnit(0.3048, "ft/s"));
|
||||
UNITS_VELOCITY.addUnit(new GeneralUnit(0.44704, "mph"));
|
||||
UNITS_VELOCITY.setDefaultUnit(0);
|
||||
|
||||
UNITS_WINDSPEED = new UnitGroup();
|
||||
UNITS_WINDSPEED.addUnit(new GeneralUnit(1, "m/s"));
|
||||
UNITS_WINDSPEED.addUnit(new GeneralUnit(1 / 3.6, "km/h"));
|
||||
UNITS_WINDSPEED.addUnit(new GeneralUnit(0.3048, "ft/s"));
|
||||
UNITS_WINDSPEED.addUnit(new GeneralUnit(0.44704, "mph"));
|
||||
UNITS_WINDSPEED.setDefaultUnit(0);
|
||||
|
||||
UNITS_ACCELERATION = new UnitGroup();
|
||||
UNITS_ACCELERATION.addUnit(new GeneralUnit(1, "m/s" + SQUARED));
|
||||
UNITS_ACCELERATION.addUnit(new GeneralUnit(0.3048, "ft/s" + SQUARED));
|
||||
UNITS_ACCELERATION.addUnit(new GeneralUnit(9.80665, "G"));
|
||||
UNITS_ACCELERATION.setDefaultUnit(0);
|
||||
|
||||
UNITS_MASS = new UnitGroup();
|
||||
UNITS_MASS.addUnit(new GeneralUnit(0.001, "g"));
|
||||
UNITS_MASS.addUnit(new GeneralUnit(1, "kg"));
|
||||
UNITS_MASS.addUnit(new GeneralUnit(0.0283495231, "oz"));
|
||||
UNITS_MASS.addUnit(new GeneralUnit(0.45359237, "lb"));
|
||||
UNITS_MASS.setDefaultUnit(0);
|
||||
|
||||
UNITS_INERTIA = new UnitGroup();
|
||||
UNITS_INERTIA.addUnit(new GeneralUnit(0.0001, "kg" + DOT + "cm" + SQUARED));
|
||||
@ -211,6 +224,7 @@ public class UnitGroup {
|
||||
UNITS_ANGLE.addUnit(new DegreeUnit());
|
||||
UNITS_ANGLE.addUnit(new FixedPrecisionUnit("rad", 0.01));
|
||||
UNITS_ANGLE.addUnit(new GeneralUnit(1.0 / 3437.74677078, "arcmin"));
|
||||
UNITS_ANGLE.setDefaultUnit(0);
|
||||
|
||||
UNITS_DENSITY_BULK = new UnitGroup();
|
||||
UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1000, "g/cm" + CUBED));
|
||||
@ -218,6 +232,7 @@ public class UnitGroup {
|
||||
UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1, "kg/m" + CUBED));
|
||||
UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1729.99404, "oz/in" + CUBED));
|
||||
UNITS_DENSITY_BULK.addUnit(new GeneralUnit(16.0184634, "lb/ft" + CUBED));
|
||||
UNITS_DENSITY_BULK.setDefaultUnit(0);
|
||||
|
||||
UNITS_DENSITY_SURFACE = new UnitGroup();
|
||||
UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(10, "g/cm" + SQUARED));
|
||||
@ -232,15 +247,18 @@ public class UnitGroup {
|
||||
UNITS_DENSITY_LINE.addUnit(new GeneralUnit(0.001, "g/m"));
|
||||
UNITS_DENSITY_LINE.addUnit(new GeneralUnit(1, "kg/m"));
|
||||
UNITS_DENSITY_LINE.addUnit(new GeneralUnit(0.0930102465, "oz/ft"));
|
||||
UNITS_DENSITY_LINE.setDefaultUnit(0);
|
||||
|
||||
UNITS_FORCE = new UnitGroup();
|
||||
UNITS_FORCE.addUnit(new GeneralUnit(1, "N"));
|
||||
UNITS_FORCE.addUnit(new GeneralUnit(4.44822162, "lbf"));
|
||||
UNITS_FORCE.addUnit(new GeneralUnit(9.80665, "kgf"));
|
||||
UNITS_FORCE.setDefaultUnit(0);
|
||||
|
||||
UNITS_IMPULSE = new UnitGroup();
|
||||
UNITS_IMPULSE.addUnit(new GeneralUnit(1, "Ns"));
|
||||
UNITS_IMPULSE.addUnit(new GeneralUnit(4.44822162, "lbf" + DOT + "s"));
|
||||
UNITS_IMPULSE.setDefaultUnit(0);
|
||||
|
||||
UNITS_TIME_STEP = new UnitGroup();
|
||||
UNITS_TIME_STEP.addUnit(new FixedPrecisionUnit("ms", 1, 0.001));
|
||||
@ -249,10 +267,12 @@ public class UnitGroup {
|
||||
|
||||
UNITS_SHORT_TIME = new UnitGroup();
|
||||
UNITS_SHORT_TIME.addUnit(new GeneralUnit(1, "s"));
|
||||
UNITS_SHORT_TIME.setDefaultUnit(0);
|
||||
|
||||
UNITS_FLIGHT_TIME = new UnitGroup();
|
||||
UNITS_FLIGHT_TIME.addUnit(new GeneralUnit(1, "s"));
|
||||
UNITS_FLIGHT_TIME.addUnit(new GeneralUnit(60, "min"));
|
||||
UNITS_FLIGHT_TIME.setDefaultUnit(0);
|
||||
|
||||
UNITS_ROLL = new UnitGroup();
|
||||
UNITS_ROLL.addUnit(new GeneralUnit(1, "rad/s"));
|
||||
@ -274,32 +294,29 @@ public class UnitGroup {
|
||||
UNITS_PRESSURE.addUnit(new GeneralUnit(3386.389, "inHg"));
|
||||
UNITS_PRESSURE.addUnit(new GeneralUnit(6894.75729, "psi"));
|
||||
UNITS_PRESSURE.addUnit(new GeneralUnit(1, "Pa"));
|
||||
UNITS_PRESSURE.setDefaultUnit(0);
|
||||
|
||||
UNITS_RELATIVE = new UnitGroup();
|
||||
UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("" + ZWSP, 0.01, 1.0));
|
||||
UNITS_RELATIVE.addUnit(new GeneralUnit(0.01, "%"));
|
||||
UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("" + PERMILLE, 1, 0.001));
|
||||
// UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("" + ZWSP, 0.01, 1.0));
|
||||
// UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("%", 1, 0.01));
|
||||
// UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("" + PERMILLE, 1, 0.001));
|
||||
UNITS_RELATIVE.setDefaultUnit(1);
|
||||
|
||||
|
||||
UNITS_ROUGHNESS = new UnitGroup();
|
||||
UNITS_ROUGHNESS.addUnit(new GeneralUnit(1, "m")); // just added
|
||||
UNITS_ROUGHNESS.addUnit(new GeneralUnit(0.000001, MICRO + "m"));
|
||||
UNITS_ROUGHNESS.addUnit(new GeneralUnit(0.0000254, "mil"));
|
||||
UNITS_ROUGHNESS.addUnit(new GeneralUnit(1, "m"));
|
||||
UNITS_ROUGHNESS.setDefaultUnit(0);
|
||||
|
||||
|
||||
UNITS_COEFFICIENT = new UnitGroup();
|
||||
UNITS_COEFFICIENT.addUnit(new FixedPrecisionUnit("" + ZWSP, 0.01)); // zero-width space
|
||||
UNITS_COEFFICIENT.setDefaultUnit(0);
|
||||
|
||||
|
||||
// This is not used by OpenRocket, and not extensively tested:
|
||||
UNITS_FREQUENCY = new UnitGroup();
|
||||
// UNITS_FREQUENCY.addUnit(new GeneralUnit(1, "s"));
|
||||
// UNITS_FREQUENCY.addUnit(new GeneralUnit(0.001, "ms"));
|
||||
// UNITS_FREQUENCY.addUnit(new GeneralUnit(0.000001, MICRO + "s"));
|
||||
UNITS_FREQUENCY.addUnit(new FrequencyUnit(.001, "mHz"));
|
||||
UNITS_FREQUENCY.addUnit(new FrequencyUnit(1, "Hz"));
|
||||
UNITS_FREQUENCY.addUnit(new FrequencyUnit(1000, "kHz"));
|
||||
|
@ -1,6 +1,10 @@
|
||||
package net.sf.openrocket.database;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@ -14,31 +18,31 @@ import net.sf.openrocket.util.Coordinate;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ThrustCurveMotorSetTest {
|
||||
|
||||
|
||||
|
||||
|
||||
private static final ThrustCurveMotor motor1 = new ThrustCurveMotor(
|
||||
Manufacturer.getManufacturer("A"),
|
||||
"F12X", "Desc", Motor.Type.UNKNOWN, new double[] { },
|
||||
0.024, 0.07, new double[] { 0, 1, 2 }, new double[] {0, 1, 0},
|
||||
new Coordinate[] {Coordinate.NUL, Coordinate.NUL, Coordinate.NUL}, "digestA");
|
||||
"F12X", "Desc", Motor.Type.UNKNOWN, new double[] {},
|
||||
0.024, 0.07, new double[] { 0, 1, 2 }, new double[] { 0, 1, 0 },
|
||||
new Coordinate[] { Coordinate.NUL, Coordinate.NUL, Coordinate.NUL }, "digestA");
|
||||
|
||||
private static final ThrustCurveMotor motor2 = new ThrustCurveMotor(
|
||||
Manufacturer.getManufacturer("A"),
|
||||
"F12H", "Desc", Motor.Type.SINGLE, new double[] { 5 },
|
||||
0.024, 0.07, new double[] { 0, 1, 2 }, new double[] {0, 1, 0},
|
||||
new Coordinate[] {Coordinate.NUL, Coordinate.NUL, Coordinate.NUL}, "digestB");
|
||||
0.024, 0.07, new double[] { 0, 1, 2 }, new double[] { 0, 1, 0 },
|
||||
new Coordinate[] { Coordinate.NUL, Coordinate.NUL, Coordinate.NUL }, "digestB");
|
||||
|
||||
private static final ThrustCurveMotor motor3 = new ThrustCurveMotor(
|
||||
Manufacturer.getManufacturer("A"),
|
||||
"F12", "Desc", Motor.Type.UNKNOWN, new double[] { 0, Motor.PLUGGED },
|
||||
0.024, 0.07, new double[] { 0, 1, 2 }, new double[] {0, 2, 0},
|
||||
new Coordinate[] {Coordinate.NUL, Coordinate.NUL, Coordinate.NUL}, "digestC");
|
||||
|
||||
0.024, 0.07, new double[] { 0, 1, 2 }, new double[] { 0, 2, 0 },
|
||||
new Coordinate[] { Coordinate.NUL, Coordinate.NUL, Coordinate.NUL }, "digestC");
|
||||
|
||||
private static final ThrustCurveMotor motor4 = new ThrustCurveMotor(
|
||||
Manufacturer.getManufacturer("A"),
|
||||
"F12", "Desc", Motor.Type.HYBRID, new double[] { 0 },
|
||||
0.024, 0.07, new double[] { 0, 1, 2 }, new double[] {0, 2, 0},
|
||||
new Coordinate[] {Coordinate.NUL, Coordinate.NUL, Coordinate.NUL}, "digestD");
|
||||
0.024, 0.07, new double[] { 0, 1, 2 }, new double[] { 0, 2, 0 },
|
||||
new Coordinate[] { Coordinate.NUL, Coordinate.NUL, Coordinate.NUL }, "digestD");
|
||||
|
||||
|
||||
@Test
|
||||
@ -50,7 +54,7 @@ public class ThrustCurveMotorSetTest {
|
||||
assertEquals("J115", ThrustCurveMotorSet.simplifyDesignation("384-J115"));
|
||||
assertEquals("A2", ThrustCurveMotorSet.simplifyDesignation("A2T"));
|
||||
assertEquals("1/2A2T", ThrustCurveMotorSet.simplifyDesignation("1/2A2T"));
|
||||
assertEquals("Micro Maxx II", ThrustCurveMotorSet.simplifyDesignation("Micro Maxx II"));
|
||||
assertEquals("MicroMaxxII", ThrustCurveMotorSet.simplifyDesignation("Micro Maxx II"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
9
core/test/net/sf/openrocket/plugin/Example2Plugin.java
Normal file
9
core/test/net/sf/openrocket/plugin/Example2Plugin.java
Normal file
@ -0,0 +1,9 @@
|
||||
package net.sf.openrocket.plugin;
|
||||
|
||||
/**
|
||||
* Example plugin interface for testing purposes.
|
||||
*/
|
||||
@Plugin
|
||||
public interface Example2Plugin {
|
||||
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
package net.sf.openrocket.plugin;
|
||||
|
||||
/**
|
||||
* Example plugin for testing purposes.
|
||||
*/
|
||||
@Plugin
|
||||
public interface ExamplePlugin {
|
||||
|
||||
public void doit();
|
||||
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
package net.sf.openrocket.plugin;
|
||||
|
||||
/**
|
||||
* ExamplePlugin implementation for testing purposes.
|
||||
*/
|
||||
@Plugin
|
||||
public class ExamplePluginImpl implements ExamplePlugin {
|
||||
|
||||
@Override
|
||||
public void doit() {
|
||||
System.out.println("ExamplePluginImpl.doit() called");
|
||||
}
|
||||
|
||||
}
|
10
core/test/net/sf/openrocket/plugin/MultiPluginImpl.java
Normal file
10
core/test/net/sf/openrocket/plugin/MultiPluginImpl.java
Normal file
@ -0,0 +1,10 @@
|
||||
package net.sf.openrocket.plugin;
|
||||
|
||||
/**
|
||||
* Plugin that implements both ExamplePlugin and Example2Plugin
|
||||
* for testing purposes.
|
||||
*/
|
||||
@Plugin
|
||||
public class MultiPluginImpl implements ExamplePlugin, Example2Plugin {
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package net.sf.openrocket.plugin;
|
||||
|
||||
/**
|
||||
* An implementation of ExamplePlugin that is not annotated with @Plugin
|
||||
* for testing purposes. This should not be injected into the test class.
|
||||
*/
|
||||
public class NotAnExamplePluginImpl implements ExamplePlugin {
|
||||
|
||||
}
|
26
core/test/net/sf/openrocket/plugin/PluginTest.java
Normal file
26
core/test/net/sf/openrocket/plugin/PluginTest.java
Normal file
@ -0,0 +1,26 @@
|
||||
package net.sf.openrocket.plugin;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
/**
|
||||
* Test the plugin loading system using Guice.
|
||||
*
|
||||
* This is more of an integration than a unit test. It uses the
|
||||
* PluginModule to load a Guice injector, and then verifies that it
|
||||
* has found the appropriate plugins.
|
||||
*/
|
||||
public class PluginTest {
|
||||
|
||||
@Test
|
||||
public void testPluginModule() {
|
||||
|
||||
Injector injector = Guice.createInjector(new PluginModule());
|
||||
PluginTester tester = injector.getInstance(PluginTester.class);
|
||||
tester.testPlugins();
|
||||
|
||||
}
|
||||
|
||||
}
|
38
core/test/net/sf/openrocket/plugin/PluginTester.java
Normal file
38
core/test/net/sf/openrocket/plugin/PluginTester.java
Normal file
@ -0,0 +1,38 @@
|
||||
package net.sf.openrocket.plugin;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sf.openrocket.util.ArrayList;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
public class PluginTester {
|
||||
|
||||
@Inject
|
||||
private Set<ExamplePlugin> examplePlugins;
|
||||
@Inject
|
||||
private Set<Example2Plugin> example2Plugins;
|
||||
|
||||
|
||||
public void testPlugins() {
|
||||
assertContains(examplePlugins, ExamplePluginImpl.class, MultiPluginImpl.class, JarPluginImpl.class);
|
||||
assertContains(example2Plugins, MultiPluginImpl.class);
|
||||
}
|
||||
|
||||
|
||||
private void assertContains(Set<?> set, Class<?>... classes) {
|
||||
assertEquals(classes.length, set.size());
|
||||
|
||||
List<Class<?>> list = new ArrayList<Class<?>>(Arrays.asList(classes));
|
||||
for (Object o : set) {
|
||||
Class<?> c = o.getClass();
|
||||
assertTrue(list.contains(c));
|
||||
list.remove(c);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
package net.sf.openrocket.util;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@ -53,7 +57,7 @@ public class TestMutex {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private volatile int testState = 0;
|
||||
private volatile String failure = null;
|
||||
|
||||
@ -106,6 +110,7 @@ public class TestMutex {
|
||||
testState = 6;
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
failure = "Exception occurred in thread: " + e;
|
||||
return;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user