[fix][refactor] simplified rocketfigure drawing code

Fixes Issues:
- https://github.com/openrocket/openrocket/issues/366
- https://github.com/openrocket/openrocket/issues/323

- RocketFigure no longer draws specific stages:
  Starts drawing rocket, and then propogates location, angle/transformation downwards
  - fixed active/inactive visibility toggling
  - Fixed Drowing Bounds for RocketFigure
    - Fix: FlightConfiguration#getBounds()
    - Fix: FinSet#getComponentBounds()
    - Fix: InnerTube#getInstanceCount()
    - Add: Coordinate#MIN, Coordinate#MAX
    - Add: net.sf.openrocket.util.BoundingBox

- RocketComponent:
  - implement: #getInstanceLocations() // relative to parent component
  - implement #getInstanceAngles()
  - implement: #getComponentLocations()  (Refactor/rename of #getLocations() )

- Implement <x>#getInstanceOffsets() // relative to component-reference-point
  - FinSet
  - PodSet
  - ParallelStage
  - RailButton
  - InnerTube:

- fixed drawing shapes:
  - TubeShapes
  - BodyTube
  - Launch Lug
  - RingComponent (InnerTube, EngineBlock, Coupler)
  - Finset
  - Transition
  - Rail Button
This commit is contained in:
Daniel_M_Williams 2017-09-23 12:28:18 -04:00
parent c36ce2eae4
commit eb72329c58
22 changed files with 555 additions and 495 deletions

View File

@ -1,6 +1,5 @@
package net.sf.openrocket.rocketcomponent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@ -12,6 +11,7 @@ import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.position.AxialPositionable;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.ArrayUtils;
import net.sf.openrocket.util.BoundingBox;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.Transformation;
@ -168,7 +168,6 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab
if (n > 8)
n = 8;
fins = n;
finRotation = Transformation.rotate_x(2 * Math.PI / fins);
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
@ -186,7 +185,7 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab
/**
* Sets the base rotation amount of the first fin.
* @param r The base rotation amount.
* @param r The base rotation in radians
*/
public void setBaseRotation(double r) {
setAngularOffset(r);
@ -596,59 +595,18 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab
*/
@Override
public Collection<Coordinate> getComponentBounds() {
Collection<Coordinate> bounds = new ArrayList<Coordinate>(8);
BoundingBox singleFinBounds= new BoundingBox( getFinPoints());
final double finLength = singleFinBounds.max.x;
final double finHeight = singleFinBounds.max.y;
// should simply return this component's bounds in this component's body frame.
BoundingBox compBox = new BoundingBox( getComponentLocations() );
double x_min = Double.MAX_VALUE;
double x_max = Double.MIN_VALUE;
double r_max = 0.0;
BoundingBox finSetBox = new BoundingBox( compBox.min.sub( 0, finHeight, finHeight ),
compBox.max.add( finLength, finHeight, finHeight ));
for (Coordinate point : getFinPoints()) {
double hypot = MathUtil.hypot(point.y, point.z);
double x_cur = point.x;
if (x_min > x_cur) {
x_min = x_cur;
}
if (x_max < x_cur) {
x_max = x_cur;
}
if (r_max < hypot) {
r_max = hypot;
}
}
addBoundingBox(bounds, x_min, x_max, r_max);
return bounds;
return finSetBox.toCollection();
}
// /**
// * Adds the 2d-coordinate bound (x,y) to the collection for both z-components and for
// * all fin rotations.
// */
// private void addFinBound(Collection<Coordinate> set, double x, double y) {
// Coordinate c;
// int i;
//
// c = new Coordinate(x, y, thickness / 2);
// c = baseRotation.transform(c);
// set.add(c);
// for (i = 1; i < fins; i++) {
// c = finRotation.transform(c);
// set.add(c);
// }
//
// c = new Coordinate(x, y, -thickness / 2);
// c = baseRotation.transform(c);
// set.add(c);
// for (i = 1; i < fins; i++) {
// c = finRotation.transform(c);
// set.add(c);
// }
// }
//
//
@Override
public void componentChanged(ComponentChangeEvent e) {
if (e.isAerodynamicChange()) {
@ -672,8 +630,7 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab
s = this.getParent();
while (s != null) {
if (s instanceof SymmetricComponent) {
double x = this.toRelative(new Coordinate(0, 0, 0), s)[0].x;
return ((SymmetricComponent) s).getRadius(x);
return ((SymmetricComponent) s).getRadius( this.position.x);
}
s = s.getParent();
}
@ -747,16 +704,6 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab
@Override
public double getAngularOffset() {
ComponentAssembly stage = this.getAssembly();
if( PodSet.class.isAssignableFrom( stage.getClass() )){
PodSet assembly= (PodSet)stage;
return assembly.getAngularOffset() + baseRotationValue;
}else if( ParallelStage.class.isAssignableFrom( stage.getClass())){
ParallelStage assembly = (ParallelStage)stage;
log.debug("detected p-stage with position: "+assembly.getAngularOffset());
return assembly.getAngularOffset() + baseRotationValue;
}
return baseRotationValue;
}
@ -782,12 +729,32 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab
double[] result = new double[ getFinCount()];
for( int i=0; i<getFinCount(); ++i){
result[i] = baseAngle + incrAngle*i;
double currentAngle = baseAngle + incrAngle*i;
if( Math.PI*2 <= currentAngle)
currentAngle -= Math.PI*2;
result[i] = currentAngle;
}
return result;
}
@Override
public Coordinate[] getInstanceOffsets(){
checkState();
final int finCount = getFinCount();
double radius = this.getBodyRadius();
Coordinate[] toReturn = new Coordinate[finCount];
final double[] angles = getInstanceAngles();
for (int instanceNumber = 0; instanceNumber < finCount; instanceNumber++) {
final double curY = radius * Math.cos(angles[instanceNumber]);
final double curZ = radius * Math.sin(angles[instanceNumber]);
toReturn[instanceNumber] = new Coordinate(0, curY, curZ );
}
return toReturn;
}
@Override
public Position getAxialPositionMethod( ){
return getRelativePositionMethod();

View File

@ -14,6 +14,7 @@ import net.sf.openrocket.motor.MotorConfiguration;
import net.sf.openrocket.motor.MotorConfigurationId;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.ArrayList;
import net.sf.openrocket.util.BoundingBox;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.Monitorable;
@ -60,7 +61,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
final protected HashMap<MotorConfigurationId, MotorConfiguration> motors = new HashMap<MotorConfigurationId, MotorConfiguration>();
private int boundsModID = -1;
private ArrayList<Coordinate> cachedBounds = new ArrayList<Coordinate>();
private BoundingBox cachedBounds = new BoundingBox();
private double cachedLength = -1;
private int refLengthModID = -1;
@ -159,8 +160,8 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
* Check whether the stage specified by the index is active.
*/
public boolean isStageActive(int stageNumber) {
if( ! stages.containsKey(stageNumber)){
throw new IllegalArgumentException(" Configuration does not contain stage number: "+stageNumber);
if( -1 == stageNumber ) {
return false;
}
return stages.get(stageNumber).active;
@ -397,30 +398,27 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
public Collection<Coordinate> getBounds() {
if (rocket.getModID() != boundsModID) {
boundsModID = rocket.getModID();
cachedBounds.clear();
double minX = Double.POSITIVE_INFINITY, maxX = Double.NEGATIVE_INFINITY;
// System.err.println(String.format(">> generating bounds for configuration: %s (%d)(%s)", getName(), this.instanceNumber, getId() ));
BoundingBox bounds = new BoundingBox();
for (RocketComponent component : this.getActiveComponents()) {
for (Coordinate coord : component.getComponentBounds()) {
cachedBounds.add(coord);
if (coord.x < minX){
minX = coord.x;
}else if (coord.x > maxX){
maxX = coord.x;
}
}
BoundingBox componentBounds = new BoundingBox( component.getComponentBounds() );
bounds.compare( componentBounds );
// System.err.println(String.format(" [%s] %s >> %s", component.getName(), componentBounds.toString(), bounds.toString() ));
}
if (Double.isInfinite(minX) || Double.isInfinite(maxX)) {
cachedLength = 0;
} else {
cachedLength = maxX - minX;
}
cachedLength = bounds.span().x;
cachedBounds.compare( bounds );
}
return cachedBounds.clone();
return cachedBounds.toCollection();
}
/**
* Returns the length of the rocket configuration, from the foremost bound X-coordinate
* to the aft-most X-coordinate. The value is cached.

View File

@ -145,7 +145,7 @@ public class InnerTube extends ThicknessRingComponent implements Clusterable, Ra
@Override
public int getInstanceCount() {
return this.getLocations().length;
return cluster.getClusterCount();
}
@Override
@ -226,23 +226,12 @@ public class InnerTube extends ThicknessRingComponent implements Clusterable, Ra
@Override
public Coordinate[] getInstanceOffsets(){
int instanceCount = getClusterCount();
if (instanceCount == 1)
return super.getInstanceOffsets();
if ( 1 == getClusterCount())
return new Coordinate[] { Coordinate.ZERO };
List<Coordinate> points = getClusterPoints();
if (points.size() != instanceCount) {
throw new BugException("Inconsistent cluster configuration, cluster count(" + instanceCount +
") != point count(" + points.size()+")");
}
Coordinate[] newArray = new Coordinate[ instanceCount];
for (int instanceNumber = 0; instanceNumber < instanceCount; instanceNumber++) {
newArray[ instanceNumber] = this.position.add( points.get(instanceNumber));
}
return newArray;
return points.toArray( new Coordinate[ points.size()]);
}
// @Override

View File

@ -3,17 +3,21 @@ package net.sf.openrocket.rocketcomponent;
import net.sf.openrocket.util.Coordinate;
public interface Instanceable {
/**
* Note: <code> this.getLocation().length == this.getInstanceCount() </code> should ALWAYS be true. If getInstanceCount() returns anything besides 1,
* this function should be override as well.
*
* Note: This is function has a concrete implementation in RocketComponent.java ... it is included here only as a reminder.
*
* @return coordinates of each instance of this component -- specifically the front center of each instance in global coordinates
*/
@Deprecated
public Coordinate[] getLocations();
/**
* Returns vector coordinates of each instance of this component relative to this component's parent
*
* Note: <code> this.getOffsets().length == this.getInstanceCount() </code> should ALWAYS be true.
* If getInstanceCount() returns anything besides 1 this function should be overridden as well.
*
*
* @return coordinates location of each instance relative to component's parent
*/
public Coordinate[] getInstanceLocations();
/**
* Returns vector coordinates of each instance of this component relative to this component's reference point (typically front center)
*

View File

@ -127,17 +127,15 @@ public class LaunchLug extends ExternalComponent implements Coaxial, LineInstanc
return ComponentPreset.Type.LAUNCH_LUG;
}
@Override
public Coordinate[] getInstanceOffsets(){
Coordinate[] toReturn = new Coordinate[this.getInstanceCount()];
final double xOffset = this.position.x;
final double yOffset = Math.cos(radialDirection) * (radialDistance);
final double zOffset = Math.sin(radialDirection) * (radialDistance);
for ( int index=0; index < this.getInstanceCount(); index++){
toReturn[index] = new Coordinate(xOffset + index*this.instanceSeparation, yOffset, zOffset);
toReturn[index] = new Coordinate(index*this.instanceSeparation, yOffset, zOffset);
}
return toReturn;

View File

@ -128,28 +128,7 @@ public class ParallelStage extends AxialStage implements FlightConfigurableCompo
public double getRadialOffset() {
return this.radialPosition_m;
}
@Override
public Coordinate[] getInstanceOffsets(){
checkState();
Coordinate center = Coordinate.ZERO;
Coordinate[] toReturn = new Coordinate[this.instanceCount];
final double[] angles = getInstanceAngles();
for (int instanceNumber = 0; instanceNumber < this.instanceCount; instanceNumber++) {
final double curY = this.radialPosition_m * Math.cos(angles[instanceNumber]);
final double curZ = this.radialPosition_m * Math.sin(angles[instanceNumber]);
toReturn[instanceNumber] = center.add(0, curY, curZ );
}
return toReturn;
}
@Override
public double getInstanceAngleIncrement(){
return this.angularSeparation;
}
@Override
public double[] getInstanceAngles(){
final double baseAngle = getAngularOffset();
@ -163,23 +142,21 @@ public class ParallelStage extends AxialStage implements FlightConfigurableCompo
return result;
}
@Override
public double getInstanceAngleIncrement(){
return this.angularSeparation;
}
@Override
public Coordinate[] getLocations() {
if (null == this.parent) {
throw new BugException(" Attempted to get absolute position Vector of a Stage without a parent. ");
}
public Coordinate[] getInstanceOffsets(){
checkState();
Coordinate[] parentInstances = this.parent.getLocations();
if (1 != parentInstances.length) {
throw new BugException(" OpenRocket does not (yet) support external stages attached to external stages. " +
"(assumed reason for getting multiple parent locations into an external stage.)");
}
final Coordinate center = parentInstances[0].add( this.position);
Coordinate[] instanceLocations = this.getInstanceOffsets();
Coordinate[] toReturn = new Coordinate[ instanceLocations.length];
for( int i = 0; i < toReturn.length; i++){
toReturn[i] = center.add( instanceLocations[i]);
Coordinate[] toReturn = new Coordinate[this.instanceCount];
final double[] angles = getInstanceAngles();
for (int instanceNumber = 0; instanceNumber < this.instanceCount; instanceNumber++) {
final double curY = this.radialPosition_m * Math.cos(angles[instanceNumber]);
final double curZ = this.radialPosition_m * Math.sin(angles[instanceNumber]);
toReturn[instanceNumber] = new Coordinate(0, curY, curZ );
}
return toReturn;

View File

@ -76,22 +76,7 @@ public class PodSet extends ComponentAssembly implements RingInstanceable {
public boolean isCompatible(Class<? extends RocketComponent> type) {
return BodyComponent.class.isAssignableFrom(type);
}
@Override
public Coordinate[] getInstanceOffsets(){
checkState();
Coordinate center = Coordinate.ZERO;
Coordinate[] toReturn = new Coordinate[this.instanceCount];
final double[] angles = getInstanceAngles();
for (int instanceNumber = 0; instanceNumber < this.instanceCount; instanceNumber++) {
final double curY = this.radialPosition_m * Math.cos(angles[instanceNumber]);
final double curZ = this.radialPosition_m * Math.sin(angles[instanceNumber]);
toReturn[instanceNumber] = center.add(0, curY, curZ );
}
return toReturn;
}
@Override
public double getInstanceAngleIncrement(){
@ -112,30 +97,18 @@ public class PodSet extends ComponentAssembly implements RingInstanceable {
}
@Override
public Coordinate[] getLocations() {
if (null == this.parent) {
throw new BugException(" Attempted to get absolute position Vector of a Stage without a parent. ");
}
if (this.isAfter()) {
return super.getLocations();
} else {
Coordinate[] parentInstances = this.parent.getLocations();
if (1 != parentInstances.length) {
throw new BugException(" OpenRocket does not (yet) support external stages attached to external stages. " +
"(assumed reason for getting multiple parent locations into an external stage.)");
}
final Coordinate center = parentInstances[0].add( this.position);
Coordinate[] instanceLocations = this.getInstanceOffsets();
Coordinate[] toReturn = new Coordinate[ instanceLocations.length];
for( int i = 0; i < toReturn.length; i++){
toReturn[i] = center.add( instanceLocations[i]);
}
return toReturn;
public Coordinate[] getInstanceOffsets(){
checkState();
Coordinate[] toReturn = new Coordinate[this.instanceCount];
final double[] angles = getInstanceAngles();
for (int instanceNumber = 0; instanceNumber < this.instanceCount; instanceNumber++) {
final double curY = this.radialPosition_m * Math.cos(angles[instanceNumber]);
final double curZ = this.radialPosition_m * Math.sin(angles[instanceNumber]);
toReturn[instanceNumber] = new Coordinate(0, curY, curZ );
}
return toReturn;
}
@Override

View File

@ -196,24 +196,15 @@ public class RailButton extends ExternalComponent implements LineInstanceable {
fireComponentChangeEvent(ComponentChangeEvent.AERODYNAMIC_CHANGE);
}
// @Override
// public void setPositionValue(double value) {
// super.setPositionValue(value);
// fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
// }
@Override
public Coordinate[] getInstanceOffsets(){
Coordinate[] toReturn = new Coordinate[this.getInstanceCount()];
final double xOffset = this.position.x;
final double yOffset = Math.cos(this.angle_rad) * ( this.radialDistance_m );
final double zOffset = Math.sin(this.angle_rad) * ( this.radialDistance_m );
for ( int index=0; index < this.getInstanceCount(); index++){
toReturn[index] = new Coordinate(xOffset + index*this.instanceSeparation, yOffset, zOffset);
toReturn[index] = new Coordinate(index*this.instanceSeparation, yOffset, zOffset);
}
return toReturn;

View File

@ -210,6 +210,11 @@ public class Rocket extends ComponentAssembly {
return (AxialStage) children.get( children.size()-1 );
}
@Override
public int getStageNumber() {
// invalid, error value
return -1;
}
private int getNewStageNumber() {
int guess = 0;
while (stageMap.containsKey(guess)) {

View File

@ -1174,20 +1174,47 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
* of the passed array and return the array itself.
*/
// @Override Me !
public Coordinate[] getInstanceLocations(){
int instanceCount = getInstanceCount();
if( 0 == instanceCount ){
return new Coordinate[]{this.position};
}
checkState();
Coordinate center = this.position;
Coordinate[] offsets = getInstanceOffsets();
Coordinate[] locations= new Coordinate[offsets.length];
for (int instanceNumber = 0; instanceNumber < locations.length; instanceNumber++) {
locations[instanceNumber] = center.add( offsets[instanceNumber] );
}
return locations;
}
public Coordinate[] getInstanceOffsets(){
return new Coordinate[]{this.position};
// According to the language specification, Java will initialized double values to 0.0
return new Coordinate[]{Coordinate.ZERO};
}
// this is an inefficient way to calculate all of the locations;
// it also breaks locality, (i.e. is a rocket-wide calculation )
@Deprecated
public Coordinate[] getLocations() {
return getComponentLocations();
}
public Coordinate[] getLocations() {
public Coordinate[] getComponentLocations() {
if (null == this.parent) {
// == improperly initialized components OR the root Rocket instance
return new Coordinate[] { Coordinate.ZERO };
return getInstanceOffsets();
} else {
Coordinate[] parentPositions = this.parent.getLocations();
int parentCount = parentPositions.length;
// override <instance>.getInstanceOffsets() in the subclass you want to fix.
Coordinate[] instanceOffsets = this.getInstanceOffsets();
Coordinate[] instanceOffsets = this.getInstanceLocations();
int instanceCount = instanceOffsets.length;
// usual case optimization
@ -1209,6 +1236,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
}
}
public double[] getInstanceAngles(){
return new double[getInstanceCount()];
}
/////////// Coordinate changes ///////////
/**
@ -1954,11 +1985,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
/**
* Helper method to add eight bounds in a box around the rocket centerline. This box will be (x_max - x_min) long, and 2*r wide/high.
* Helper method to add two points on opposite corners of a box around the rocket centerline. This box will be (x_max - x_min) long, and 2*r wide/high.
*/
protected static final void addBoundingBox(Collection<Coordinate> bounds, double x_min, double x_max, double r) {
addBound(bounds, x_min, r);
addBound(bounds, x_max, r);
bounds.add(new Coordinate(x_min, -r, -r));
bounds.add(new Coordinate(x_max, r, r));
}
/**

View File

@ -0,0 +1,91 @@
package net.sf.openrocket.util;
import java.util.ArrayList;
import java.util.Collection;
public class BoundingBox {
public Coordinate min;
public Coordinate max;
public BoundingBox() {
min = Coordinate.MAX.setWeight( 0.0);
max = Coordinate.MIN.setWeight( 0.0);
}
public BoundingBox( Coordinate _min, Coordinate _max) {
this();
this.min = _min.clone();
this.max = _max.clone();
}
public BoundingBox( Coordinate[] list ) {
this();
this.compare( list);
}
public BoundingBox( Collection<Coordinate> list ) {
this();
this.compare( list.toArray( new Coordinate[0] ));
}
@Override
public BoundingBox clone() {
return new BoundingBox( this.min, this.max );
}
public void compare( Coordinate c ) {
compare_against_min(c);
compare_against_max(c);
}
protected void compare_against_min( Coordinate c ) {
if( min.x > c.x )
min = min.setX( c.x );
if( min.y > c.y )
min = min.setY( c.y );
if( min.z > c.z )
min = min.setZ( c.z );
}
protected void compare_against_max( Coordinate c) {
if( max.x < c.x )
max = max.setX( c.x );
if( max.y < c.y )
max = max.setY( c.y );
if( max.z < c.z )
max = max.setZ( c.z );
}
public BoundingBox compare( Coordinate[] list ) {
for( Coordinate c: list ) {
compare( c );
}
return this;
}
public void compare( BoundingBox other ) {
compare_against_min( other.min);
compare_against_max( other.max);
}
public Coordinate span() { return max.sub( min ); }
public Coordinate[] toArray() {
return new Coordinate[] { this.min, this.max };
}
public Collection<Coordinate> toCollection(){
Collection<Coordinate> toReturn = new ArrayList<Coordinate>();
toReturn.add( this.max);
toReturn.add( this.min);
return toReturn;
}
@Override
public String toString() {
// return String.format("[( %6.4f, %6.4f, %6.4f) < ( %6.4f, %6.4f, %6.4f)]",
return String.format("[( %g, %g, %g) < ( %g, %g, %g)]",
min.x, min.y, min.z,
max.x, max.y, max.z );
}
}

View File

@ -63,6 +63,8 @@ public final class Coordinate implements Cloneable, Serializable {
public static final Coordinate NUL = new Coordinate(0, 0, 0, 0);
public static final Coordinate NaN = new Coordinate(Double.NaN, Double.NaN,
Double.NaN, Double.NaN);
public static final Coordinate MAX = new Coordinate(Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE);
public static final Coordinate MIN = new Coordinate(Double.MIN_VALUE,Double.MIN_VALUE,Double.MIN_VALUE,Double.MIN_VALUE);
public final double x, y, z;
public final double weight;

View File

@ -242,6 +242,19 @@ public class Transformation implements java.io.Serializable {
System.out.println();
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(String.format("[%3.2f %3.2f %3.2f] [%3.2f]\n",
rotation[X][X],rotation[X][Y],rotation[X][Z],translate.x));
sb.append(String.format("[%3.2f %3.2f %3.2f] + [%3.2f]\n",
rotation[Y][X],rotation[Y][Y],rotation[Y][Z],translate.y));
sb.append(String.format("[%3.2f %3.2f %3.2f] [%3.2f]\n",
rotation[Z][X],rotation[Z][Y],rotation[Z][Z],translate.z));
return sb.toString();
}
@Override
public boolean equals(Object other) {

View File

@ -57,6 +57,83 @@ public class FinSetTest extends BaseTestCase {
}
@Test
public void testInstancePoints_PI_2_BaseRotation() {
// This is a simple square fin with sides of 1.0.
TrapezoidFinSet fins = new TrapezoidFinSet();
fins.setFinCount(4);
fins.setFinShape(1.0, 1.0, 0.0, 1.0, .005);
fins.setBaseRotation( Math.PI/2 );
BodyTube body = new BodyTube(1.0, 0.05 );
body.addChild( fins );
Coordinate[] points = fins.getInstanceOffsets();
assertEquals( 0, points[0].x, 0.00001);
assertEquals( 0, points[0].y, 0.00001);
assertEquals( 0.05, points[0].z, 0.00001);
assertEquals( 0, points[1].x, 0.00001);
assertEquals( -0.05, points[1].y, 0.00001);
assertEquals( 0, points[1].z, 0.00001);
}
@Test
public void testInstancePoints_PI_4_BaseRotation() {
// This is a simple square fin with sides of 1.0.
TrapezoidFinSet fins = new TrapezoidFinSet();
fins.setFinCount(4);
fins.setFinShape(1.0, 1.0, 0.0, 1.0, .005);
fins.setBaseRotation( Math.PI/4 );
BodyTube body = new BodyTube(1.0, 0.05 );
body.addChild( fins );
Coordinate[] points = fins.getInstanceOffsets();
assertEquals( 0, points[0].x, 0.0001);
assertEquals( 0.03535, points[0].y, 0.0001);
assertEquals( 0.03535, points[0].z, 0.0001);
assertEquals( 0, points[1].x, 0.0001);
assertEquals( -0.03535, points[1].y, 0.0001);
assertEquals( 0.03535, points[1].z, 0.0001);
}
@Test
public void testInstanceAngles_zeroBaseRotation() {
// This is a simple square fin with sides of 1.0.
TrapezoidFinSet fins = new TrapezoidFinSet();
fins.setFinCount(4);
fins.setFinShape(1.0, 1.0, 0.0, 1.0, .005);
fins.setBaseRotation( 0.0 );
double[] angles = fins.getInstanceAngles();
assertEquals( angles[0], 0, 0.000001 );
assertEquals( angles[1], Math.PI/2, 0.000001 );
assertEquals( angles[2], Math.PI, 0.000001 );
assertEquals( angles[3], 1.5*Math.PI, 0.000001 );
}
@Test
public void testInstanceAngles_90_BaseRotation() {
// This is a simple square fin with sides of 1.0.
TrapezoidFinSet fins = new TrapezoidFinSet();
fins.setFinCount(4);
fins.setFinShape(1.0, 1.0, 0.0, 1.0, .005);
fins.setBaseRotation( Math.PI/2 );
double[] angles = fins.getInstanceAngles();
assertEquals( angles[0], Math.PI/2, 0.000001 );
assertEquals( angles[1], Math.PI, 0.000001 );
assertEquals( angles[2], 1.5*Math.PI, 0.000001 );
assertEquals( angles[3], 0, 0.000001 );
}
@Test
public void testFreeformCGComputation() throws Exception {

View File

@ -1,60 +1,41 @@
package net.sf.openrocket.gui.rocketfigure;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import net.sf.openrocket.rocketcomponent.BodyTube;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.Transformation;
public class BodyTubeShapes extends RocketComponentShape {
public static RocketComponentShape[] getShapesSide(
net.sf.openrocket.rocketcomponent.RocketComponent component,
RocketComponent component,
Transformation transformation,
Coordinate componentAbsoluteLocation) {
net.sf.openrocket.rocketcomponent.BodyTube tube = (net.sf.openrocket.rocketcomponent.BodyTube)component;
Coordinate componentAbsoluteLocation){
BodyTube tube = (BodyTube)component;
double length = tube.getLength();
double radius = tube.getOuterRadius();
// old version
//Coordinate[] instanceOffsets = new Coordinate[]{ transformation.transform( componentAbsoluteLocation )};
//instanceOffsets = component.shiftCoordinates(instanceOffsets);
// new version
Coordinate[] instanceOffsets = transformation.transform( component.getLocations());
Shape[] s = new Shape[1];
s[0] = TubeShapes.getShapesSide( transformation, componentAbsoluteLocation, length, radius );
Shape[] s = new Shape[instanceOffsets.length];
for (int i=0; i < instanceOffsets.length; i++) {
s[i] = new Rectangle2D.Double((instanceOffsets[i].x)*S, //x - the X coordinate of the upper-left corner of the newly constructed Rectangle2D
(instanceOffsets[i].y-radius)*S, // y - the Y coordinate of the upper-left corner of the newly constructed Rectangle2D
length*S, // w - the width of the newly constructed Rectangle2D
2*radius*S); // h - the height of the newly constructed Rectangle2D
}
return RocketComponentShape.toArray(s, component);
return RocketComponentShape.toArray(s, component);
}
public static RocketComponentShape[] getShapesBack(
net.sf.openrocket.rocketcomponent.RocketComponent component,
RocketComponent component,
Transformation transformation,
Coordinate componentAbsoluteLocation) {
net.sf.openrocket.rocketcomponent.BodyTube tube = (net.sf.openrocket.rocketcomponent.BodyTube)component;
double or = tube.getOuterRadius();
Coordinate[] instanceOffsets = new Coordinate[]{ transformation.transform( componentAbsoluteLocation )};
//instanceOffsets = component.shiftCoordinates(instanceOffsets);
instanceOffsets = component.getLocations();
BodyTube tube = (BodyTube)component;
double radius = tube.getOuterRadius();
Shape[] s = new Shape[instanceOffsets.length];
for (int i=0; i < instanceOffsets.length; i++) {
s[i] = new Ellipse2D.Double((instanceOffsets[i].z-or)*S,(instanceOffsets[i].y-or)*S,2*or*S,2*or*S);
}
Shape[] s = new Shape[1];
s[0] = TubeShapes.getShapesBack( transformation, componentAbsoluteLocation, radius);
return RocketComponentShape.toArray(s, component);
}

View File

@ -15,56 +15,34 @@ public class FinSetShapes extends RocketComponentShape {
public static RocketComponentShape[] getShapesSide(
net.sf.openrocket.rocketcomponent.RocketComponent component,
Transformation transformation,
Coordinate componentAbsoluteLocation) {
Coordinate instanceAbsoluteLocation) {
net.sf.openrocket.rocketcomponent.FinSet finset = (net.sf.openrocket.rocketcomponent.FinSet)component;
int finCount = finset.getFinCount();
Transformation cantRotation = finset.getCantRotation();
Transformation baseRotation = Transformation.rotate_x(finset.getAngularOffset()); // rotation about x-axis
Transformation finRotation = finset.getFinRotationTransformation();
Coordinate finSetFront = componentAbsoluteLocation;
Coordinate finSetFront = instanceAbsoluteLocation;
Coordinate finPoints[] = finset.getFinPointsWithTab();
// TODO: MEDIUM: sloping radius
double radius = finset.getBodyRadius();
// Translate & rotate the coordinates
for (int i=0; i<finPoints.length; i++) {
finPoints[i] = cantRotation.transform(finPoints[i]);
finPoints[i] = baseRotation.transform(finPoints[i].add(0,radius,0));
}
Transformation cantRotation = finset.getCantRotation();
finPoints = cantRotation.transform(finPoints);
finPoints = transformation.transform(finPoints);
// Generate shapes
RocketComponentShape[] finShape = new RocketComponentShape[ finCount];
for (int finNum=0; finNum<finCount; finNum++) {
Coordinate a;
Path2D.Float p;
Path2D.Float p;
{
// Make polygon
p = new Path2D.Float();
for (int i=0; i<finPoints.length; i++) {
// previous version
// a = transformation.transform(finset.toAbsolute(finPoints[i])[0]);
a = transformation.transform(finSetFront.add(finPoints[i]));
Coordinate c = finSetFront.add(finPoints[i]);
if (i==0)
p.moveTo(a.x*S, a.y*S);
p.moveTo(c.x*S, c.y*S);
else
p.lineTo(a.x*S, a.y*S);
p.lineTo(c.x*S, c.y*S);
}
p.closePath();
finShape[finNum] = new RocketComponentShape( p, finset);
// Rotate fin coordinates
for (int i=0; i<finPoints.length; i++)
finPoints[i] = finRotation.transform(finPoints[i]);
}
return finShape;
return new RocketComponentShape[] {new RocketComponentShape(p, finset)};
}
public static RocketComponentShape[] getShapesBack(
@ -73,7 +51,7 @@ public class FinSetShapes extends RocketComponentShape {
Coordinate location) {
net.sf.openrocket.rocketcomponent.FinSet finset = (net.sf.openrocket.rocketcomponent.FinSet)component;
Shape[] toReturn;
if (MathUtil.equals(finset.getCantAngle(),0)){
@ -89,51 +67,38 @@ public class FinSetShapes extends RocketComponentShape {
private static Shape[] uncantedShapesBack(net.sf.openrocket.rocketcomponent.FinSet finset,
Transformation transformation,
Coordinate location) {
Coordinate finFront) {
int fins = finset.getFinCount();
double radius = finset.getBodyRadius();
double thickness = finset.getThickness();
double height = finset.getSpan();
Coordinate compCenter = location;
Transformation baseRotation = Transformation.rotate_x( finset.getAngularOffset());
Transformation finRotation = finset.getFinRotationTransformation();
// Generate base coordinates for a single fin
Coordinate c[] = new Coordinate[4];
c[0]=new Coordinate(0,radius,-thickness/2);
c[1]=new Coordinate(0,radius,thickness/2);
c[2]=new Coordinate(0,height+radius,thickness/2);
c[3]=new Coordinate(0,height+radius,-thickness/2);
c[0]=new Coordinate(0, 0,-thickness/2);
c[1]=new Coordinate(0, 0,thickness/2);
c[2]=new Coordinate(0,height,thickness/2);
c[3]=new Coordinate(0,height,-thickness/2);
System.err.println(String.format(" -- %s", transformation.toString() ));
// Apply base rotation
transformPoints(c,baseRotation);
c = transformation.transform(c);
// Make polygon
Coordinate a;
Path2D.Double p = new Path2D.Double();
// Generate shapes
Shape[] s = new Shape[fins];
for (int fin=0; fin<fins; fin++) {
Coordinate a;
Path2D.Double p;
// Make polygon
p = new Path2D.Double();
a = transformation.transform(compCenter.add( c[0] ));
p.moveTo(a.z*S, a.y*S);
a = transformation.transform(compCenter.add( c[1] ));
p.lineTo(a.z*S, a.y*S);
a = transformation.transform(compCenter.add( c[2] ));
p.lineTo(a.z*S, a.y*S);
a = transformation.transform(compCenter.add( c[3] ));
p.lineTo(a.z*S, a.y*S);
p.closePath();
s[fin] = p;
// Rotate fin coordinates
transformPoints(c,finRotation);
}
a = finFront.add( c[0] );
p.moveTo(a.z*S, a.y*S);
a = finFront.add( c[1] );
p.lineTo(a.z*S, a.y*S);
a = finFront.add( c[2] );
p.lineTo(a.z*S, a.y*S);
a = finFront.add( c[3] );
p.lineTo(a.z*S, a.y*S);
p.closePath();
return s;
return new Shape[]{p};
}
@ -143,11 +108,9 @@ public class FinSetShapes extends RocketComponentShape {
Coordinate location) {
int i;
int fins = finset.getFinCount();
double radius = finset.getBodyRadius();
// double radius = finset.getBodyRadius();
double thickness = finset.getThickness();
Transformation baseRotation = Transformation.rotate_x( finset.getAngularOffset());
Transformation finRotation = finset.getFinRotationTransformation();
Transformation cantRotation = finset.getCantRotation();
Coordinate[] sidePoints;
@ -160,9 +123,9 @@ public class FinSetShapes extends RocketComponentShape {
break;
}
transformPoints(points,cantRotation);
transformPoints(points,new Transformation(0,radius,0));
transformPoints(points,baseRotation);
points = cantRotation.transform( points );
// transformPoints(points,new Transformation(0,radius,0));
points = transformation.transform( points );
sidePoints = new Coordinate[points.length];
@ -197,10 +160,6 @@ public class FinSetShapes extends RocketComponentShape {
s[2*fin] = makePolygonBack(sidePoints,finset,transformation, location);
s[2*fin+1] = makePolygonBack(backPoints,finset,transformation, location);
// Rotate fin coordinates
transformPoints(sidePoints,finRotation);
transformPoints(backPoints,finRotation);
}
} else {
@ -208,7 +167,6 @@ public class FinSetShapes extends RocketComponentShape {
s = new Shape[fins];
for (int fin=0; fin<fins; fin++) {
s[fin] = makePolygonBack(sidePoints,finset,transformation, location);
transformPoints(sidePoints,finRotation);
}
}

View File

@ -1,52 +1,42 @@
package net.sf.openrocket.gui.rocketfigure;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.Transformation;
import net.sf.openrocket.rocketcomponent.LaunchLug;
import net.sf.openrocket.rocketcomponent.RocketComponent;
public class LaunchLugShapes extends RocketComponentShape {
public static RocketComponentShape[] getShapesSide(
net.sf.openrocket.rocketcomponent.RocketComponent component,
RocketComponent component,
Transformation transformation,
Coordinate componentAbsoluteLocation) {
Coordinate instanceAbsoluteLocation) {
net.sf.openrocket.rocketcomponent.LaunchLug lug = (net.sf.openrocket.rocketcomponent.LaunchLug)component;
double length = lug.getLength();
LaunchLug lug = (LaunchLug)component;
double length = lug.getLength();
double radius = lug.getOuterRadius();
Coordinate[] start = transformation.transform( lug.getLocations());
Shape[] s = new Shape[start.length];
for (int i=0; i < start.length; i++) {
s[i] = new Rectangle2D.Double(start[i].x*S,(start[i].y-radius)*S,
length*S,2*radius*S);
}
return RocketComponentShape.toArray(s, component);
Shape[] s = new Shape[]{
TubeShapes.getShapesSide( transformation, instanceAbsoluteLocation, length, radius )
};
return RocketComponentShape.toArray(s, component);
}
public static RocketComponentShape[] getShapesBack(
net.sf.openrocket.rocketcomponent.RocketComponent component,
RocketComponent component,
Transformation transformation,
Coordinate instanceOffset) {
Coordinate instanceAbsoluteLocation) {
net.sf.openrocket.rocketcomponent.LaunchLug lug = (net.sf.openrocket.rocketcomponent.LaunchLug)component;
double or = lug.getOuterRadius();
Coordinate[] start = transformation.transform( lug.getLocations());
Shape[] s = new Shape[start.length];
for (int i=0; i < start.length; i++) {
s[i] = new Ellipse2D.Double((start[i].z-or)*S,(start[i].y-or)*S,2*or*S,2*or*S);
}
LaunchLug lug = (LaunchLug)component;
double radius = lug.getOuterRadius();
Shape[] s = new Shape[]{TubeShapes.getShapesBack( transformation, instanceAbsoluteLocation, radius)};
return RocketComponentShape.toArray(s, component);
}
}

View File

@ -6,6 +6,8 @@ import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import net.sf.openrocket.rocketcomponent.RailButton;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.Transformation;
@ -13,11 +15,11 @@ import net.sf.openrocket.util.Transformation;
public class RailButtonShapes extends RocketComponentShape {
public static RocketComponentShape[] getShapesSide(
net.sf.openrocket.rocketcomponent.RocketComponent component,
RocketComponent component,
Transformation transformation,
Coordinate componentAbsoluteLocation) {
Coordinate instanceAbsoluteLocation) {
net.sf.openrocket.rocketcomponent.RailButton btn = (net.sf.openrocket.rocketcomponent.RailButton)component;
RailButton btn = (RailButton)component;
final double rotation_rad = btn.getAngularOffset();
final double baseHeight = btn.getStandoff();
@ -27,9 +29,6 @@ public class RailButtonShapes extends RocketComponentShape {
final double outerRadius = outerDiameter/2;
final double innerDiameter = btn.getInnerDiameter();
final double innerRadius = innerDiameter/2;
Coordinate[] inst = transformation.transform( btn.getLocations());
Shape[] s = new Shape[inst.length];
final double sinr = Math.abs(Math.sin(rotation_rad));
final double cosr = Math.cos(rotation_rad);
@ -38,49 +37,46 @@ public class RailButtonShapes extends RocketComponentShape {
final double flangeHeightcos = flangeHeight*cosr;
for (int i=0; i < inst.length; i++) {
Path2D.Double compound = new Path2D.Double();
s[i] = compound;
{// base
final double drawWidth = outerDiameter;
final double drawHeight = outerDiameter*sinr;
final Point2D.Double center = new Point2D.Double( inst[i].x, inst[i].y);
Point2D.Double lowerLeft = new Point2D.Double( center.x - outerRadius, center.y-outerRadius*sinr);
compound.append( new Ellipse2D.Double( lowerLeft.x*S, lowerLeft.y*S, drawWidth*S, drawHeight*S), false);
compound.append( new Line2D.Double( lowerLeft.x*S, center.y*S, lowerLeft.x*S, (center.y+baseHeightcos)*S ), false);
compound.append( new Line2D.Double( (center.x+outerRadius)*S, center.y*S, (center.x+outerRadius)*S, (center.y+baseHeightcos)*S ), false);
compound.append( new Ellipse2D.Double( lowerLeft.x*S, (lowerLeft.y+baseHeightcos)*S, drawWidth*S, drawHeight*S), false);
}
Path2D.Double path = new Path2D.Double();
{// central pillar
final double drawWidth = outerDiameter;
final double drawHeight = outerDiameter*sinr;
final Point2D.Double center = new Point2D.Double( instanceAbsoluteLocation.x, instanceAbsoluteLocation.y );
Point2D.Double lowerLeft = new Point2D.Double( center.x - outerRadius, center.y-outerRadius*sinr);
path.append( new Ellipse2D.Double( lowerLeft.x*S, lowerLeft.y*S, drawWidth*S, drawHeight*S), false);
{// inner
final double drawWidth = innerDiameter;
final double drawHeight = innerDiameter*sinr;
final Point2D.Double center = new Point2D.Double( inst[i].x, inst[i].y+baseHeightcos);
final Point2D.Double lowerLeft = new Point2D.Double( center.x - innerRadius, center.y-innerRadius*sinr);
compound.append( new Ellipse2D.Double( lowerLeft.x*S, lowerLeft.y*S, drawWidth*S, drawHeight*S), false);
compound.append( new Line2D.Double( lowerLeft.x*S, center.y*S, lowerLeft.x*S, (center.y+innerHeightcos)*S ), false);
compound.append( new Line2D.Double( (center.x+innerRadius)*S, center.y*S, (center.x+innerRadius)*S, (center.y+innerHeightcos)*S ), false);
compound.append( new Ellipse2D.Double( lowerLeft.x*S, (lowerLeft.y+innerHeightcos)*S, drawWidth*S, drawHeight*S), false);
}
{// outer flange
final double drawWidth = outerDiameter;
final double drawHeight = outerDiameter*sinr;
final Point2D.Double center = new Point2D.Double( inst[i].x, inst[i].y+(baseHeightcos+innerHeightcos));
final Point2D.Double lowerLeft = new Point2D.Double( center.x - outerRadius, center.y-outerRadius*sinr);
compound.append( new Ellipse2D.Double( lowerLeft.x*S, lowerLeft.y*S, drawWidth*S, drawHeight*S), false);
compound.append( new Line2D.Double( lowerLeft.x*S, center.y*S, lowerLeft.x*S, (center.y+flangeHeightcos)*S ), false);
compound.append( new Line2D.Double( (center.x+outerRadius)*S, center.y*S, (center.x+outerRadius)*S, (center.y+flangeHeightcos)*S ), false);
compound.append( new Ellipse2D.Double( lowerLeft.x*S, (lowerLeft.y+flangeHeightcos)*S, drawWidth*S, drawHeight*S), false);
}
path.append( new Line2D.Double( lowerLeft.x*S, center.y*S, lowerLeft.x*S, (center.y+baseHeightcos)*S ), false);
path.append( new Line2D.Double( (center.x+outerRadius)*S, center.y*S, (center.x+outerRadius)*S, (center.y+baseHeightcos)*S ), false);
path.append( new Ellipse2D.Double( lowerLeft.x*S, (lowerLeft.y+baseHeightcos)*S, drawWidth*S, drawHeight*S), false);
}
{// inner
final double drawWidth = innerDiameter;
final double drawHeight = innerDiameter*sinr;
final Point2D.Double center = new Point2D.Double( instanceAbsoluteLocation.x, instanceAbsoluteLocation.y + baseHeightcos);
final Point2D.Double lowerLeft = new Point2D.Double( center.x - innerRadius, center.y-innerRadius*sinr);
path.append( new Ellipse2D.Double( lowerLeft.x*S, lowerLeft.y*S, drawWidth*S, drawHeight*S), false);
path.append( new Line2D.Double( lowerLeft.x*S, center.y*S, lowerLeft.x*S, (center.y+innerHeightcos)*S ), false);
path.append( new Line2D.Double( (center.x+innerRadius)*S, center.y*S, (center.x+innerRadius)*S, (center.y+innerHeightcos)*S ), false);
path.append( new Ellipse2D.Double( lowerLeft.x*S, (lowerLeft.y+innerHeightcos)*S, drawWidth*S, drawHeight*S), false);
}
{// outer flange
final double drawWidth = outerDiameter;
final double drawHeight = outerDiameter*sinr;
final Point2D.Double center = new Point2D.Double( instanceAbsoluteLocation.x, instanceAbsoluteLocation.y+baseHeightcos+innerHeightcos);
final Point2D.Double lowerLeft = new Point2D.Double( center.x - outerRadius, center.y-outerRadius*sinr);
path.append( new Ellipse2D.Double( lowerLeft.x*S, lowerLeft.y*S, drawWidth*S, drawHeight*S), false);
path.append( new Line2D.Double( lowerLeft.x*S, center.y*S, lowerLeft.x*S, (center.y+flangeHeightcos)*S ), false);
path.append( new Line2D.Double( (center.x+outerRadius)*S, center.y*S, (center.x+outerRadius)*S, (center.y+flangeHeightcos)*S ), false);
path.append( new Ellipse2D.Double( lowerLeft.x*S, (lowerLeft.y+flangeHeightcos)*S, drawWidth*S, drawHeight*S), false);
}
return RocketComponentShape.toArray(s, component);
return RocketComponentShape.toArray( new Shape[]{ path }, component );
}

View File

@ -14,39 +14,26 @@ public class RingComponentShapes extends RocketComponentShape {
public static RocketComponentShape[] getShapesSide(
net.sf.openrocket.rocketcomponent.RocketComponent component,
Transformation transformation,
Coordinate componentAbsoluteLocation) {
Coordinate instanceAbsoluteLocation) {
net.sf.openrocket.rocketcomponent.RingComponent tube = (net.sf.openrocket.rocketcomponent.RingComponent)component;
Shape[] s;
double length = tube.getLength();
double or = tube.getOuterRadius();
double ir = tube.getInnerRadius();
double outerRadius = tube.getOuterRadius();
double innerRadius = tube.getInnerRadius();
// old version
//Coordinate[] instanceOffsets = new Coordinate[]{ transformation.transform( componentAbsoluteLocation )};
//instanceOffsets = component.shiftCoordinates(instanceOffsets);
// new version
Coordinate[] instanceOffsets = transformation.transform( component.getLocations());
if ((or-ir >= 0.0012) && (ir > 0)) {
if ((outerRadius-innerRadius >= 0.0012) && (innerRadius > 0)) {
// Draw outer and inner
s = new Shape[instanceOffsets.length*2];
for (int i=0; i < instanceOffsets.length; i++) {
s[2*i] = new Rectangle2D.Double(instanceOffsets[i].x*S,(instanceOffsets[i].y-or)*S,
length*S,2*or*S);
s[2*i+1] = new Rectangle2D.Double(instanceOffsets[i].x*S,(instanceOffsets[i].y-ir)*S,
length*S,2*ir*S);
}
s = new Shape[] {
TubeShapes.getShapesSide(transformation, instanceAbsoluteLocation, length, outerRadius),
TubeShapes.getShapesSide(transformation, instanceAbsoluteLocation, length, innerRadius)
};
} else {
// Draw only outer
s = new Shape[instanceOffsets.length];
for (int i=0; i < instanceOffsets.length; i++) {
s[i] = new Rectangle2D.Double(instanceOffsets[i].x*S,(instanceOffsets[i].y-or)*S,
length*S,2*or*S);
}
s = new Shape[] {
TubeShapes.getShapesSide(transformation, instanceAbsoluteLocation, length, outerRadius)
};
}
return RocketComponentShape.toArray( s, component);
}
@ -55,37 +42,24 @@ public class RingComponentShapes extends RocketComponentShape {
public static RocketComponentShape[] getShapesBack(
net.sf.openrocket.rocketcomponent.RocketComponent component,
Transformation transformation,
Coordinate componentAbsoluteLocation) {
Coordinate instanceAbsoluteLocation) {
net.sf.openrocket.rocketcomponent.RingComponent tube = (net.sf.openrocket.rocketcomponent.RingComponent)component;
Shape[] s;
double or = tube.getOuterRadius();
double ir = tube.getInnerRadius();
Coordinate[] instanceOffsets = new Coordinate[]{ transformation.transform( componentAbsoluteLocation )};
double outerRadius = tube.getOuterRadius();
double innerRadius = tube.getInnerRadius();
if ((outerRadius-innerRadius >= 0.0012) && (innerRadius > 0)) {
s = new Shape[] {
TubeShapes.getShapesBack(transformation, instanceAbsoluteLocation, outerRadius),
TubeShapes.getShapesBack(transformation, instanceAbsoluteLocation, innerRadius)
};
}else {
s = new Shape[] {
TubeShapes.getShapesBack(transformation, instanceAbsoluteLocation, outerRadius)
};
}
// old version
//instanceOffsets = component.shiftCoordinates(instanceOffsets);
// new version
instanceOffsets = component.getLocations();
if ((ir < or) && (ir > 0)) {
// Draw inner and outer
s = new Shape[instanceOffsets.length*2];
for (int i=0; i < instanceOffsets.length; i++) {
s[2*i] = new Ellipse2D.Double((instanceOffsets[i].z-or)*S, (instanceOffsets[i].y-or)*S,
2*or*S, 2*or*S);
s[2*i+1] = new Ellipse2D.Double((instanceOffsets[i].z-ir)*S, (instanceOffsets[i].y-ir)*S,
2*ir*S, 2*ir*S);
}
} else {
// Draw only outer
s = new Shape[instanceOffsets.length];
for (int i=0; i < instanceOffsets.length; i++) {
s[i] = new Ellipse2D.Double((instanceOffsets[i].z-or)*S,(instanceOffsets[i].y-or)*S,2*or*S,2*or*S);
}
}
return RocketComponentShape.toArray( s, component);
}

View File

@ -1,5 +1,6 @@
package net.sf.openrocket.gui.rocketfigure;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.Transition;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.Transformation;
@ -17,21 +18,21 @@ public class TransitionShapes extends RocketComponentShape {
public static RocketComponentShape[] getShapesSide(
net.sf.openrocket.rocketcomponent.RocketComponent component,
Transformation transformation,
Coordinate instanceOffset) {
return getShapesSide(component, transformation, instanceOffset, S);
Coordinate instanceLocation) {
return getShapesSide(component, transformation, instanceLocation, S);
}
public static RocketComponentShape[] getShapesSide(
net.sf.openrocket.rocketcomponent.RocketComponent component,
RocketComponent component,
Transformation transformation,
Coordinate componentAbsoluteLocation,
Coordinate instanceAbsoluteLocation,
final double scaleFactor) {
net.sf.openrocket.rocketcomponent.Transition transition = (net.sf.openrocket.rocketcomponent.Transition)component;
Transition transition = (Transition)component;
RocketComponentShape[] mainShapes;
Coordinate frontCenter = transformation.transform( componentAbsoluteLocation );
// this component type does not allow multiple instances
Coordinate frontCenter = instanceAbsoluteLocation;
// Simpler shape for conical transition, others use the method from SymmetricComponent
if (transition.getType() == Transition.Shape.CONICAL) {
@ -48,14 +49,14 @@ public class TransitionShapes extends RocketComponentShape {
mainShapes = new RocketComponentShape[] { new RocketComponentShape( path, component) };
} else {
mainShapes = SymmetricComponentShapes.getShapesSide(component, transformation, componentAbsoluteLocation, scaleFactor);
mainShapes = SymmetricComponentShapes.getShapesSide(component, transformation, instanceAbsoluteLocation, scaleFactor);
}
Rectangle2D.Double foreShoulder=null, aftShoulder=null;
int arrayLength = mainShapes.length;
if (transition.getForeShoulderLength() > 0.0005) {
Coordinate foreTransitionShoulderCenter = componentAbsoluteLocation.sub( transition.getForeShoulderLength()/2, 0, 0);
Coordinate foreTransitionShoulderCenter = instanceAbsoluteLocation.sub( transition.getForeShoulderLength()/2, 0, 0);
frontCenter = transformation.transform( foreTransitionShoulderCenter);
double rad = transition.getForeShoulderRadius();
@ -64,7 +65,7 @@ public class TransitionShapes extends RocketComponentShape {
arrayLength++;
}
if (transition.getAftShoulderLength() > 0.0005) {
Coordinate aftTransitionShoulderCenter = componentAbsoluteLocation.add( transition.getLength() + (transition.getAftShoulderLength())/2, 0, 0);
Coordinate aftTransitionShoulderCenter = instanceAbsoluteLocation.add( transition.getLength() + (transition.getAftShoulderLength())/2, 0, 0);
frontCenter= transformation.transform( aftTransitionShoulderCenter );
double rad = transition.getAftShoulderRadius();

View File

@ -0,0 +1,33 @@
package net.sf.openrocket.gui.rocketfigure;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.Transformation;
public class TubeShapes extends RocketComponentShape {
public static Shape getShapesSide(
Transformation transformation,
Coordinate instanceAbsoluteLocation,
final double length, final double radius ){
return new Rectangle2D.Double((instanceAbsoluteLocation.x)*S, //x - the X coordinate of the upper-left corner of the newly constructed Rectangle2D
(instanceAbsoluteLocation.y-radius)*S, // y - the Y coordinate of the upper-left corner of the newly constructed Rectangle2D
length*S, // w - the width of the newly constructed Rectangle2D
2*radius*S); // h - the height of the newly constructed Rectangle2D
}
public static Shape getShapesBack(
Transformation transformation,
Coordinate instanceAbsoluteLocation,
final double radius ) {
return new Ellipse2D.Double((instanceAbsoluteLocation.z-radius)*S, (instanceAbsoluteLocation.y-radius)*S, 2*radius*S, 2*radius*S);
}
}

View File

@ -183,9 +183,9 @@ public class RocketFigure extends AbstractScaleFigure {
figureShapes.clear();
calculateSize();
FlightConfiguration config = rocket.getSelectedConfiguration();
getShapes( figureShapes, config);
getShapeTree( this.figureShapes, rocket, this.transformation, Coordinate.ZERO);
repaint();
fireChangeEvent();
}
@ -347,23 +347,23 @@ public class RocketFigure extends AbstractScaleFigure {
// <component>.getLocation() will return all the parent instances of this owning component, AND all of it's own instances as well.
// so, just draw a motor once for each Coordinate returned...
Coordinate[] mountLocations = mountComponent.getLocations();
Coordinate[] mountLocations = mount.getLocations();
double mountLength = mountComponent.getLength();
//System.err.println("Drawing motor from here. Motor: "+motor.getDesignation()+" of length: "+motor.getLength());
// System.err.println("Drawing Motor: "+motor.getDesignation()+" (x"+mountLocations.length+")");
for ( Coordinate curMountLocation : mountLocations ){
Coordinate curMotorLocation = curMountLocation.add( mountLength - motorLength + mount.getMotorOverhang(), 0, 0);
Coordinate coord = curMotorLocation;
Coordinate curMotorLocation = curMountLocation.add( mountLength - motorLength + mount.getMotorOverhang(), 0, 0);
// System.err.println(String.format(" mount instance: %s => %s", curMountLocation.toString(), curMotorLocation.toString() ));
{
Shape s;
if (currentViewType == RocketPanel.VIEW_TYPE.SideView) {
s = new Rectangle2D.Double(EXTRA_SCALE * coord.x,
EXTRA_SCALE * (coord.y - motorRadius), EXTRA_SCALE * motorLength,
s = new Rectangle2D.Double(EXTRA_SCALE * curMotorLocation.x,
EXTRA_SCALE * (curMotorLocation.y - motorRadius), EXTRA_SCALE * motorLength,
EXTRA_SCALE * 2 * motorRadius);
} else {
s = new Ellipse2D.Double(EXTRA_SCALE * (coord.z - motorRadius),
EXTRA_SCALE * (coord.y - motorRadius), EXTRA_SCALE * 2 * motorRadius,
s = new Ellipse2D.Double(EXTRA_SCALE * (curMotorLocation.z - motorRadius),
EXTRA_SCALE * (curMotorLocation.y - motorRadius), EXTRA_SCALE * 2 * motorRadius,
EXTRA_SCALE * 2 * motorRadius);
}
g2.setColor(fillColor);
@ -420,41 +420,52 @@ public class RocketFigure extends AbstractScaleFigure {
return l.toArray(new RocketComponent[0]);
}
// facade for the recursive function below
private void getShapes(ArrayList<RocketComponentShape> allShapes, FlightConfiguration configuration){
for( AxialStage stage : configuration.getActiveStages()){
getShapeTree( allShapes, stage, Coordinate.ZERO);
}
}
// NOTE: Recursive function
private void getShapeTree(
ArrayList<RocketComponentShape> allShapes, // output parameter
final RocketComponent comp,
final Coordinate parentLocation){
RocketPanel.VIEW_TYPE viewType = this.currentViewType;
Transformation viewTransform = this.transformation;
Coordinate[] locs = comp.getLocations();
// generate shapes
for( Coordinate curLocation : locs){
allShapes = addThisShape( allShapes, viewType, comp, curLocation, viewTransform);
}
ArrayList<RocketComponentShape> allShapes, // output parameter
final RocketComponent comp,
final Transformation parentTransform,
final Coordinate parentLocation){
// recurse into component's children
for( RocketComponent child: comp.getChildren() ){
if( child instanceof AxialStage ){
// recursing into BoosterSet here would double count its tree
continue;
}
for( Coordinate curLocation : locs){
getShapeTree( allShapes, child, curLocation);
}
}
return;
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.getConfiguration().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 );
}
}
}
/**