[#1146] Add 2D visualization for pod sets

This commit is contained in:
SiboVG 2022-07-16 02:40:12 +02:00
parent d78e595532
commit 959bbfaac3
5 changed files with 244 additions and 4 deletions

View File

@ -304,6 +304,17 @@ public class Rocket extends ComponentAssembly {
fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
}
}
@Override
public double getBoundingRadius() {
double bounding = 0;
for (RocketComponent comp : children) {
if (comp instanceof ComponentAssembly) {
bounding = Math.max(bounding, ((ComponentAssembly) comp).getBoundingRadius());
}
}
return bounding;
}

View File

@ -0,0 +1,122 @@
package net.sf.openrocket.gui.rocketfigure;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.Transformation;
import java.awt.Shape;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
/**
* Shapes of an "empty"/virtual object, e.g. a podset without any children.
* The shape is a center square with additional lines on the north, east, south and west side of the square.
*
* @author Sibo Van Gool <sibo.vangool@hotmail.com>
*/
public class EmptyShapes extends RocketComponentShape {
/**
* Returns the empty shape in the side view.
* @param radius radius of the center square
*/
public static Shape[] getShapesSide(final Transformation transformation, final double radius) {
final Coordinate instanceAbsoluteLocation = transformation.transform(Coordinate.ZERO);
double x = instanceAbsoluteLocation.x;
double y = instanceAbsoluteLocation.y;
double lineLength = getLineLength(radius); // Length of the line protruding the center square
final Shape[] s = new Shape[5];
// Center square
s[0] = new Rectangle2D.Double(x - radius, y - radius, 2 * radius, 2 * radius);
// Line North
s[1] = new Line2D.Double(x, y + radius, x, y + radius + lineLength);
// Line East
s[2] = new Line2D.Double(x + radius, y, x + radius + lineLength, y);
// Line South
s[3] = new Line2D.Double(x, y - radius, x, y - radius - lineLength);
// Line West
s[4] = new Line2D.Double(x - radius, y, x - radius - lineLength, y);
return s;
}
/**
* Returns the empty shape in the side view, with an additional square encompassing the shape that can be used
* for selecting the object.
* @param radius radius of the center square
*/
public static Shape[] getShapesSideWithSelectionSquare(final Transformation transformation, final double radius) {
final Coordinate instanceAbsoluteLocation = transformation.transform(Coordinate.ZERO);
double x = instanceAbsoluteLocation.x;
double y = instanceAbsoluteLocation.y;
double lineLength = getLineLength(radius); // Length of the line protruding the center square
Shape[] shapes = getShapesSide(transformation, radius);
// Invisible shape for selecting the component (= a square encompassing the component)
Shape selectionShape = new Rectangle2D.Double(x - radius - lineLength, y - radius - lineLength,
lineLength * 2 + radius * 2, lineLength * 2 + radius * 2);
Shape[] finalShapes = new Shape[shapes.length + 1];
System.arraycopy(shapes, 0, finalShapes, 0, shapes.length);
finalShapes[finalShapes.length - 1] = selectionShape;
return finalShapes;
}
/**
* Returns the empty shape in the side view.
* @param radius radius of the center square
*/
public static Shape[] getShapesBack(final Transformation transformation, final double radius) {
final Coordinate instanceAbsoluteLocation = transformation.transform(Coordinate.ZERO);
double z = instanceAbsoluteLocation.z;
double y = instanceAbsoluteLocation.y;
double lineLength = getLineLength(radius); // Length of the line protruding the center square
final Shape[] s = new Shape[5];
// Center square
s[0] = new Rectangle2D.Double(z - radius, y - radius, 2 * radius, 2 * radius);
// Line North
s[1] = new Line2D.Double(z, y + radius, z, y + radius + lineLength);
// Line East
s[2] = new Line2D.Double(z + radius, y, z + radius + lineLength, y);
// Line South
s[3] = new Line2D.Double(z, y - radius, z, y - radius - lineLength);
// Line West
s[4] = new Line2D.Double(z - radius, y, z - radius - lineLength, y);
return s;
}
/**
* Returns the empty shape in the back view, with an additional square encompassing the shape that can be used
* for selecting the object.
* @param radius radius of the center square
*/
public static Shape[] getShapesBackWithSelectionSquare(final Transformation transformation, final double radius) {
final Coordinate instanceAbsoluteLocation = transformation.transform(Coordinate.ZERO);
double z = instanceAbsoluteLocation.z;
double y = instanceAbsoluteLocation.y;
double lineLength = getLineLength(radius); // Length of the line protruding the center square
Shape[] shapes = getShapesBack(transformation, radius);
// Invisible shape for selecting the component (= a square encompassing the component)
Shape selectionShape = new Rectangle2D.Double(z - radius - lineLength, y - radius - lineLength,
lineLength * 2 + radius * 2, lineLength * 2 + radius * 2);
Shape[] finalShapes = new Shape[shapes.length + 1];
System.arraycopy(shapes, 0, finalShapes, 0, shapes.length);
finalShapes[finalShapes.length - 1] = selectionShape;
return finalShapes;
}
private static double getLineLength(double radius) {
return radius * 3;
}
}

View File

@ -0,0 +1,49 @@
package net.sf.openrocket.gui.rocketfigure;
import net.sf.openrocket.rocketcomponent.PodSet;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.util.Color;
import net.sf.openrocket.util.Transformation;
import java.awt.Shape;
public class PodSetShapes extends RocketComponentShape {
public static final Color podsetColor = new Color(160, 160, 160); // Normal color for the podset shape
public static final Color centerColor = new Color(200, 200, 200); // Color for the center shape of the podset
public static RocketComponentShape[] getShapesSide(final RocketComponent component, final Transformation transformation) {
PodSet podset = (PodSet)component;
double radius = getDisplayRadius(podset);
Shape[] s = EmptyShapes.getShapesSideWithSelectionSquare(transformation, radius);
RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component);
// Set the color of the shapes
for (int i = 0; i < shapes.length - 1; i++) {
shapes[i].setColor(podsetColor);
}
shapes[shapes.length - 1].setColor(Color.INVISIBLE);
return shapes;
}
public static RocketComponentShape[] getShapesBack(final RocketComponent component, final Transformation transformation) {
PodSet podset = (PodSet)component;
double radius = getDisplayRadius(podset);
Shape[] s = EmptyShapes.getShapesBackWithSelectionSquare(transformation, radius);
RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component);
// Set the color of the shapes
for (int i = 0; i < shapes.length - 1; i++) {
shapes[i].setColor(podsetColor);
}
shapes[shapes.length - 1].setColor(Color.INVISIBLE);
return shapes;
}
private static double getDisplayRadius(PodSet podset) {
return podset.getRocket().getBoundingRadius() * 0.03;
}
}

View File

@ -64,6 +64,10 @@ public class RocketComponentShape {
return new RocketComponentShape[0];
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}

View File

@ -17,6 +17,8 @@ import java.awt.geom.Rectangle2D;
import java.util.*;
import java.util.Map.Entry;
import net.sf.openrocket.gui.rocketfigure.PodSetShapes;
import net.sf.openrocket.rocketcomponent.PodSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -385,13 +387,38 @@ public class RocketFigure extends AbstractScaleFigure {
final Transformation currentTransform = this.axialRotation.applyTransformation(context.transform);
allShapes = addThisShape( allShapes, this.currentViewType, comp, currentTransform);
}
// PodSets require an additional shape for the center of the podset.
if (comp instanceof PodSet) {
Transformation parentTransform = null;
for (Entry<RocketComponent, ArrayList<InstanceContext>> entry2: config.getActiveInstances().entrySet()) {
final RocketComponent parent = entry2.getKey();
if (parent == comp.getParent()) {
parentTransform = entry2.getValue().get(0).transform; // TODO: normally only the first context should be used, unless we start doing fancy stuff like adding pods to individual fins
break;
}
}
if (parentTransform == null) {
parentTransform = Transformation.IDENTITY;
}
final Transformation compLocTransform = Transformation.getTranslationTransform(comp.getPosition());
final Transformation componentTransform = parentTransform.applyTransformation(compLocTransform);
final Transformation currentTransform = this.axialRotation.applyTransformation(componentTransform);
allShapes = addThisShape(allShapes, this.currentViewType, comp, currentTransform, PodSetShapes.centerColor);
}
}
}
/**
* Gets the shapes required to draw the component.
*
* @param component
*
* @param allShapes output buffer for the shapes to add to
* @param viewType the view type to draw the component in
* @param component component to draw and add to <allShapes>
* @param transformation transformation to apply to the component before drawing it
* @param color color to draw the component in
*
* @return the <code>ArrayList</code> containing all the shapes to draw.
*/
@ -399,10 +426,11 @@ public class RocketFigure extends AbstractScaleFigure {
PriorityQueue<RocketComponentShape> allShapes, // this is the output parameter
final RocketPanel.VIEW_TYPE viewType,
final RocketComponent component,
final Transformation transformation) {
final Transformation transformation,
final net.sf.openrocket.util.Color color) {
Reflection.Method m;
if(( component instanceof Rocket)||( component instanceof ComponentAssembly )){
if(( component instanceof Rocket)||( component instanceof ComponentAssembly && !(component instanceof PodSet))){
// no-op; no shapes here
return allShapes;
}
@ -431,9 +459,35 @@ public class RocketFigure extends AbstractScaleFigure {
RocketComponentShape[] returnValue = (RocketComponentShape[]) m.invokeStatic(component, transformation);
if (color != null) {
for (RocketComponentShape rcs : returnValue) {
if (rcs.getColor() == net.sf.openrocket.util.Color.INVISIBLE) continue; // don't change the color of invisible (often selection) components
rcs.setColor(color);
}
}
allShapes.addAll(Arrays.asList(returnValue));
return allShapes;
}
/**
* Gets the shapes required to draw the component.
*
* @param allShapes output buffer for the shapes to add to
* @param viewType the view type to draw the component in
* @param component component to draw and add to <allShapes>
* @param transformation transformation to apply to the component before drawing it
*
* @return the <code>ArrayList</code> containing all the shapes to draw.
*/
private static PriorityQueue<RocketComponentShape> addThisShape(
PriorityQueue<RocketComponentShape> allShapes, // this is the output parameter
final RocketPanel.VIEW_TYPE viewType,
final RocketComponent component,
final Transformation transformation) {
return addThisShape(allShapes, viewType, component, transformation, null);
}
/**