Fix TubeFinSet 2D rendering

The TubeFinSetShapes were applying the supplied transform too many
times, resulting in the the shapes being offset off the viewing area.
The offset transform is already applied to the component instance
that is provided to the TubeFinSetShapes methods, so applying the
transform resulted in the offset being moved to the right.

This change removes the transform being applied to the component
locations when retrieved and only applies a linear transformation
to the component locations when applying the base rotational
transform. Using the linear transform avoids applying the offset
in addition to the rest of the transform.

The problem existed in both the side view and the back view, however
the back view was not as obvious due to the nature of the view. The
common code between both functions applying the transform is
refactored into a common location to reduce the amount of code.
Additionally, documentation was added explaining the transform process
that is being applied to the component locations for the final shapes.

Fixes #605

Signed-off-by: Billy Olsen <billy.olsen@gmail.com>
This commit is contained in:
Billy Olsen 2020-04-12 20:29:14 -07:00
parent 0f2edc0024
commit 486a7f94e1

View File

@ -10,72 +10,102 @@ import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.Transformation;
/**
* The TubeFinSetShapes is used for retrieving shapes of the TubeFins on a
* Rocket from multiple view points. The returned shapes will be translated
* and transformed to the correct locations for rendering the rocket in the
* 2D view space.
*/
public class TubeFinSetShapes extends RocketComponentShape {
/**
* Returns an array of RocketcomponentShapes that describe the shape of
* the TubeFinSet when viewed from the side of the rocket. TubeFins will
* appear as a Rectangle from the side view
*
* @param component the TubeFinSet to get the shapes for
* @param transformation the transformation to apply to the shapes
* @return an array of RocketComponentShapes that are used to draw the
* TubeFinSet from the side.
*/
public static RocketComponentShape[] getShapesSide( final RocketComponent component, final Transformation transformation) {
TubeFinSet finset = (net.sf.openrocket.rocketcomponent.TubeFinSet)component;
TubeFinSet finSet = (TubeFinSet) component;
int fins = finset.getFinCount();
double length = finset.getLength();
double outerRadius = finset.getOuterRadius();
double bodyRadius = finset.getBodyRadius();
// old version - Oct, 19 2015
//Coordinate[] instanceOffsets = new Coordinate[]{ transformation.transform( componentAbsoluteLocation )};
//instanceOffsets = component.shiftCoordinates(instanceOffsets);
// new version
Coordinate[] start = transformation.transform( component.getLocations());
final double outerRadius = finSet.getOuterRadius();
final double length = finSet.getLength();
Coordinate[] locations = transformLocations(finSet, transformation);
Transformation finRotation = finSet.getFinRotationTransformation();
Transformation baseRotation = finset.getBaseRotationTransformation();
Transformation finRotation = finset.getFinRotationTransformation();
// Translate & rotate the coordinates
for (int i=0; i<start.length; i++) {
start[i] = baseRotation.transform(transformation.transform(start[i].add(0,bodyRadius+outerRadius,0)));
Shape[] shapes = new Shape[finSet.getFinCount()];
for (int i=0; i < shapes.length; i++) {
shapes[i] = new Rectangle2D.Double(locations[0].x, (locations[0].y-outerRadius), length, 2*outerRadius);
locations = finRotation.transform(locations);
}
//start = baseRotation.transform(start);
Shape[] s = new Shape[fins];
for (int i=0; i<fins; i++) {
s[i] = new Rectangle2D.Double(start[0].x,(start[0].y-outerRadius),length,2*outerRadius);
start = finRotation.transform(start);
}
return RocketComponentShape.toArray(s, component);
return RocketComponentShape.toArray(shapes, component);
}
/**
* Returns an array of RocketComponentShapes that describe the shape of
* the TubeFinSet when viewed from the rear or the rocket. TubeFins will
* appear as an Ellipse/Circle from the back view.
*
* @param component the TubeFinSet to get the shapes for
* @param transformation the transformation to apply to the shapes
* @return an array of RocketComponentShapes that are used to draw the
* TubeFinSet from the back
*/
public static RocketComponentShape[] getShapesBack( final RocketComponent component, final Transformation transformation) {
TubeFinSet finset = (net.sf.openrocket.rocketcomponent.TubeFinSet)component;
TubeFinSet finSet = (TubeFinSet) component;
int fins = finset.getFinCount();
double outerradius = finset.getOuterRadius();
double bodyradius = finset.getBodyRadius();
// old version - Oct, 19 2015
//Coordinate[] instanceOffsets = new Coordinate[]{ transformation.transform( componentAbsoluteLocation )};
//instanceOffsets = component.shiftCoordinates(instanceOffsets);
// new version
Coordinate[] start = transformation.transform( component.getLocations());
final double outerRadius = finSet.getOuterRadius();
Coordinate[] locations = transformLocations(finSet, transformation);
Transformation finRotation = finSet.getFinRotationTransformation();
Transformation baseRotation = finset.getBaseRotationTransformation();
Transformation finRotation = finset.getFinRotationTransformation();
Shape[] shapes = new Shape[finSet.getFinCount()];
for (int i=0; i < shapes.length; i++) {
shapes[i] = new Ellipse2D.Double((locations[0].z - outerRadius), (locations[0].y - outerRadius), (2 * outerRadius), (2 * outerRadius));
locations = finRotation.transform(locations);
}
// Translate & rotate the coordinates
for (int i=0; i<start.length; i++) {
start[i] = baseRotation.transform(transformation.transform(start[i].add(0,bodyradius+outerradius,0)));
}
Shape[] s = new Shape[fins];
for (int i=0; i < fins; i++) {
s[i] = new Ellipse2D.Double((start[0].z-outerradius),(start[0].y-outerradius),2*outerradius,2*outerradius);
start = finRotation.transform(start);
}
return RocketComponentShape.toArray(s, component);
return RocketComponentShape.toArray(shapes, component);
}
/**
* Translates and rotates the coordinates as follows:
*
* 1. Ensure the coordinate accounts for the body and outer radius. This
* adjusts the Y value of the coordinate to place it in the correct
* position relative to the body tube.
*
* 2. Perform a linear transformation of the coordinate using the supplied
* transform. Using the linear transform ensures the coordinate is
* rotated per the view, but avoids applying an offset translation
* since that is already applied in the locations retrieved from the
* TubeFinSet.
*
* 3. Apply the base rotational transform described by the TubeFinSet
* component itself.
*
* @param finSet the TubeFinSet to apply the transformation to
* @param transformation the Transformation to apply to the TubeFinSet
*/
private static Coordinate[] transformLocations(final TubeFinSet finSet, final Transformation transformation) {
final double outerRadius = finSet.getOuterRadius();
final double bodyRadius = finSet.getBodyRadius();
Coordinate[] locations = finSet.getComponentLocations();
Transformation baseRotation = finSet.getBaseRotationTransformation();
for (int i=0; i < locations.length; i++) {
Coordinate c = locations[i].add(0, (bodyRadius + outerRadius), 0);
c = transformation.linearTransform(c);
c = baseRotation.transform(c);
locations[i] = c;
}
return locations;
}
}