[resolves #323] Major patch to improve rendering of instanced+rotated components

- introduced interfaces for different types of positionable components:
  -- AnglePositionable
  -- AxialPositionable
  -- RadiusPositionable
- RingInstanceable now includes these other interfaces, making explicit the expectations of
   any implementing subclass.
- RingInstanceable now indicates its angles as an array instead of returning angles one-at-a-time
  -- commit includes updates to match this new API
- Added getAssembly() method to RocketComponent, to simplify instancing code
This commit is contained in:
Daniel_M_Williams 2017-09-16 11:49:51 -04:00
parent f469ee9586
commit c36ce2eae4
9 changed files with 235 additions and 81 deletions

View File

@ -4,8 +4,12 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sf.openrocket.l10n.Translator;
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.Coordinate;
@ -13,8 +17,9 @@ import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.Transformation;
public abstract class FinSet extends ExternalComponent {
public abstract class FinSet extends ExternalComponent implements RingInstanceable, AxialPositionable {
private static final Translator trans = Application.getTranslator();
private static final Logger log = LoggerFactory.getLogger(FinSet.class);
/**
@ -77,18 +82,12 @@ public abstract class FinSet extends ExternalComponent {
/**
* Rotation about the x-axis by 2*PI/fins.
*/
protected Transformation finRotation = Transformation.rotate_x(2 * Math.PI / fins);
protected Transformation finRotation = Transformation.IDENTITY;
/**
* Rotation angle of the first fin. Zero corresponds to the positive y-axis.
*/
protected double rotation = 0;
/**
* Rotation about the x-axis by angle this.rotation.
*/
protected Transformation baseRotation = Transformation.rotate_x(rotation);
protected double baseRotationValue = 0;
/**
* Cant angle of fins.
@ -182,7 +181,7 @@ public abstract class FinSet extends ExternalComponent {
* @return The base rotation amount.
*/
public double getBaseRotation() {
return rotation;
return getAngularOffset();
}
/**
@ -190,20 +189,9 @@ public abstract class FinSet extends ExternalComponent {
* @param r The base rotation amount.
*/
public void setBaseRotation(double r) {
r = MathUtil.reduce180(r);
if (MathUtil.equals(r, rotation))
return;
rotation = r;
baseRotation = Transformation.rotate_x(rotation);
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
setAngularOffset(r);
}
public Transformation getBaseRotationTransformation() {
return baseRotation;
}
public double getCantAngle() {
return cantAngle;
}
@ -230,7 +218,7 @@ public abstract class FinSet extends ExternalComponent {
}
return cantRotation;
}
public double getThickness() {
@ -431,16 +419,10 @@ public abstract class FinSet extends ExternalComponent {
double mass = getFinMass();
double filletMass = getFilletMass();
double filletCenter = length / 2;
double newCGx = (filletCenter * filletMass + finCGx * mass) / (filletMass + mass);
// FilletRadius/5 is a good estimate for where the vertical centroid of the fillet
// is. Finding the actual position is very involved and won't make a huge difference.
double newCGy = (filletRadius / 5 * filletMass + finCGy * mass) / (filletMass + mass);
if (fins == 1) {
return baseRotation.transform(
Transformation rotation = Transformation.rotate_x( getAngularOffset());
return rotation.transform(
new Coordinate(finCGx, finCGy + getBodyRadius(), 0, (filletMass + mass)));
} else {
return new Coordinate(finCGx, 0, 0, (filletMass + mass));
@ -640,32 +622,32 @@ public abstract class FinSet extends ExternalComponent {
return bounds;
}
/**
* 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);
}
}
// /**
// * 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) {
@ -763,6 +745,89 @@ public abstract class FinSet extends ExternalComponent {
return points;
}
@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;
}
@Override
public void setAngularOffset(double angle) {
angle = MathUtil.reduce180(angle);
if (MathUtil.equals(angle, baseRotationValue))
return;
baseRotationValue = angle;
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
@Override
public double getInstanceAngleIncrement(){
return ( 2*Math.PI / getFinCount());
}
@Override
public double[] getInstanceAngles(){
final double baseAngle = getAngularOffset();
final double incrAngle = getInstanceAngleIncrement();
double[] result = new double[ getFinCount()];
for( int i=0; i<getFinCount(); ++i){
result[i] = baseAngle + incrAngle*i;
}
return result;
}
@Override
public Position getAxialPositionMethod( ){
return getRelativePositionMethod();
}
@Override
public void setAxialPositionMethod( Position newMethod ){
setRelativePosition( newMethod );
}
@Override
public double getRadialOffset() {
return getBodyRadius();
}
@Override
public boolean getAutoRadialOffset() {
return true;
}
@Override
public void setRadialOffset(double radius) {
// no-op. Not allowed for fins
}
@Override
public void setInstanceCount(int newCount) {
setFinCount(newCount);
}
@Override
public int getInstanceCount() {
return getFinCount();
}
@Override
public String getPatternName() {
return (getInstanceCount() + "-ring");
}
/**
@ -777,8 +842,7 @@ public abstract class FinSet extends ExternalComponent {
FinSet src = (FinSet) c;
this.fins = src.fins;
this.finRotation = src.finRotation;
this.rotation = src.rotation;
this.baseRotation = src.baseRotation;
this.baseRotationValue = src.baseRotationValue;
this.cantAngle = src.cantAngle;
this.cantRotation = src.cantRotation;
this.thickness = src.thickness;

View File

@ -135,20 +135,32 @@ public class ParallelStage extends AxialStage implements FlightConfigurableCompo
Coordinate center = Coordinate.ZERO;
Coordinate[] toReturn = new Coordinate[this.instanceCount];
final double[] angles = getInstanceAngles();
for (int instanceNumber = 0; instanceNumber < this.instanceCount; instanceNumber++) {
final double curAngle = getInstanceAngle( instanceNumber);
final double curY = this.radialPosition_m * Math.cos(curAngle);
final double curZ = this.radialPosition_m * Math.sin(curAngle);
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 getInstanceAngle( final int instanceNumber){
return this.angularPosition_rad + ( instanceNumber * this.angularSeparation);
public double[] getInstanceAngles(){
final double baseAngle = getAngularOffset();
final double incrAngle = getInstanceAngleIncrement();
double[] result = new double[ getInstanceCount()];
for( int i=0; i<getInstanceCount(); ++i){
result[i] = baseAngle + incrAngle*i;
}
return result;
}
@Override

View File

@ -83,10 +83,10 @@ public class PodSet extends ComponentAssembly implements RingInstanceable {
Coordinate center = Coordinate.ZERO;
Coordinate[] toReturn = new Coordinate[this.instanceCount];
final double[] angles = getInstanceAngles();
for (int instanceNumber = 0; instanceNumber < this.instanceCount; instanceNumber++) {
final double curAngle = getInstanceAngle( instanceNumber);
final double curY = this.radialPosition_m * Math.cos(curAngle);
final double curZ = this.radialPosition_m * Math.sin(curAngle);
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 );
}
@ -94,8 +94,21 @@ public class PodSet extends ComponentAssembly implements RingInstanceable {
}
@Override
public double getInstanceAngle( final int instanceNumber){
return this.angularPosition_rad + ( instanceNumber * this.angularSeparation);
public double getInstanceAngleIncrement(){
return angularSeparation;
}
@Override
public double[] getInstanceAngles(){
final double baseAngle = getAngularOffset();
final double incrAngle = getInstanceAngleIncrement();
double[] result = new double[ getInstanceCount()];
for( int i=0; i<getInstanceCount(); ++i){
result[i] = baseAngle + incrAngle*i;
}
return result;
}
@Override

View File

@ -1,17 +1,25 @@
package net.sf.openrocket.rocketcomponent;
public interface RingInstanceable extends Instanceable {
import net.sf.openrocket.rocketcomponent.position.AnglePositionable;
import net.sf.openrocket.rocketcomponent.position.RadiusPositionable;
public interface RingInstanceable extends Instanceable, AnglePositionable, RadiusPositionable {
@Override
public double getAngularOffset();
public double getInstanceAngle( final int instanceNumber);
public double getRadialOffset();
public boolean getAutoRadialOffset();
@Override
public void setAngularOffset(final double angle);
public double getInstanceAngleIncrement();
public double[] getInstanceAngles();
@Override
public double getRadialOffset();
@Override
public boolean getAutoRadialOffset();
@Override
public void setRadialOffset(final double radius);
}

View File

@ -72,6 +72,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
this.title = title;
}
public Position[] getAxialOptions(){
return new Position[]{ TOP, MIDDLE, BOTTOM, ABSOLUTE};
}
@Override
public String toString() {
return title;
@ -1627,6 +1631,25 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
throw new IllegalStateException("getStage() called on hierarchy without an AxialStage.");
}
/**
* Return the first component assembly component that this component belongs to.
*
* @return The Stage component this component belongs to.
* @throws IllegalStateException if we cannot find an AxialStage above <code>this</code>
*/
public final ComponentAssembly getAssembly() {
checkState();
RocketComponent curComponent = this;
while ( null != curComponent ) {
if( ComponentAssembly.class.isAssignableFrom( curComponent.getClass()))
return (ComponentAssembly) curComponent;
curComponent = curComponent.parent;
}
throw new IllegalStateException("getAssembly() called on hierarchy without a ComponentAssembly.");
}
/**
* Return the stage number of the stage this component belongs to. The stages
* are numbered from zero upwards.

View File

@ -0,0 +1,11 @@
package net.sf.openrocket.rocketcomponent.position;
public interface AnglePositionable {
public double getAngularOffset();
public void setAngularOffset(final double angle);
// public Position getAnglePositionMethod( );
// public void setAnglePositionMethod( Position newMethod );
}

View File

@ -0,0 +1,14 @@
package net.sf.openrocket.rocketcomponent.position;
import net.sf.openrocket.rocketcomponent.RocketComponent.Position;
public interface AxialPositionable {
public double getAxialOffset();
public void setAxialOffset(final double newAxialOffset);
public Position getAxialPositionMethod( );
public void setAxialPositionMethod( Position newMethod );
}

View File

@ -0,0 +1,9 @@
package net.sf.openrocket.rocketcomponent.position;
public interface RadiusPositionable {
public double getRadialOffset();
public boolean getAutoRadialOffset();
public void setRadialOffset(final double radius);
}

View File

@ -21,7 +21,7 @@ public class FinSetShapes extends RocketComponentShape {
int finCount = finset.getFinCount();
Transformation cantRotation = finset.getCantRotation();
Transformation baseRotation = finset.getBaseRotationTransformation(); // rotation about x-axis
Transformation baseRotation = Transformation.rotate_x(finset.getAngularOffset()); // rotation about x-axis
Transformation finRotation = finset.getFinRotationTransformation();
Coordinate finSetFront = componentAbsoluteLocation;
@ -96,7 +96,7 @@ public class FinSetShapes extends RocketComponentShape {
double thickness = finset.getThickness();
double height = finset.getSpan();
Coordinate compCenter = location;
Transformation baseRotation = finset.getBaseRotationTransformation();
Transformation baseRotation = Transformation.rotate_x( finset.getAngularOffset());
Transformation finRotation = finset.getFinRotationTransformation();
@ -146,7 +146,7 @@ public class FinSetShapes extends RocketComponentShape {
double radius = finset.getBodyRadius();
double thickness = finset.getThickness();
Transformation baseRotation = finset.getBaseRotationTransformation();
Transformation baseRotation = Transformation.rotate_x( finset.getAngularOffset());
Transformation finRotation = finset.getFinRotationTransformation();
Transformation cantRotation = finset.getCantRotation();