diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index f84c5e7b3..e4d779394 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -809,6 +809,10 @@ AppearanceCfg.lbl.texture.offset = Offset: AppearanceCfg.lbl.texture.center = Center: AppearanceCfg.lbl.texture.rotation = Rotation: AppearanceCfg.lbl.texture.repeat = Repeat: +AppearanceCfg.lbl.InsideSameAsOutside = Same as outside +AppearanceCfg.lbl.ttip.InsideSameAsOutside = Use the same appearance for the inside as for the outside +AppearanceCfg.lbl.EdgesSameAsInside = Use inside appearance for edges +AppearanceCfg.lbl.ttip.EdgesSameAsInside = Use the inside appearance (checked) or outside appearance (unchecked) for the edges ! Texture Wrap Modes TextureWrap.Repeat = Repeat @@ -871,6 +875,8 @@ RocketCompCfg.border.Foreshoulder = Fore shoulder !RocketCompCfg.lbl.Length = Length: RocketCompCfg.lbl.InstanceCount = Instance Count RocketCompCfg.lbl.InstanceSeparation = Instance Separation +RocketCompCfg.tab.Outside = Outside +RocketCompCfg.tab.Inside = Inside ! BulkheadConfig BulkheadCfg.tab.Diameter = Diameter: diff --git a/core/src/net/sf/openrocket/document/OpenRocketDocument.java b/core/src/net/sf/openrocket/document/OpenRocketDocument.java index 8efe1460e..022ad0902 100644 --- a/core/src/net/sf/openrocket/document/OpenRocketDocument.java +++ b/core/src/net/sf/openrocket/document/OpenRocketDocument.java @@ -9,6 +9,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Set; +import net.sf.openrocket.rocketcomponent.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,12 +20,6 @@ import net.sf.openrocket.document.events.DocumentChangeEvent; import net.sf.openrocket.document.events.DocumentChangeListener; import net.sf.openrocket.document.events.SimulationChangeEvent; import net.sf.openrocket.logging.Markers; -import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; -import net.sf.openrocket.rocketcomponent.ComponentChangeListener; -import net.sf.openrocket.rocketcomponent.FlightConfiguration; -import net.sf.openrocket.rocketcomponent.FlightConfigurationId; -import net.sf.openrocket.rocketcomponent.Rocket; -import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.simulation.FlightDataType; import net.sf.openrocket.simulation.customexpression.CustomExpression; import net.sf.openrocket.simulation.extension.SimulationExtension; @@ -259,7 +254,10 @@ public class OpenRocketDocument implements ComponentChangeListener { Iterator it = rocket.iterator(); while (it.hasNext()) { - if(hasDecal(it.next(),img)) + RocketComponent c = it.next(); + if(hasDecal(c ,img)) + count++; + else if (hasDecalInside(c, img)) count++; } return count; @@ -284,6 +282,29 @@ public class OpenRocketDocument implements ComponentChangeListener { return true; return false; } + + //TODO: LOW: move this method to rocketComponent, Appearance and decal + //I see 3 layers of object accessed, seems unsafe + /** + * checks if a rocket component has the given inside decalImage + * @param comp the RocketComponent to be searched + * @param img the DecalImage to be checked + * @return if the comp has img + */ + private boolean hasDecalInside(RocketComponent comp, DecalImage img) { + if (comp instanceof InsideColorComponent) { + Appearance a = ((InsideColorComponent)comp).getInsideColorComponentHandler().getInsideAppearance(); + if (a == null) + return false; + Decal d = a.getTexture(); + if (d == null) + return false; + if (img.equals(d.getImage())) + return true; + return false; + } + return false; + } /** * gets a unique identification for the given decal diff --git a/core/src/net/sf/openrocket/file/GeneralRocketSaver.java b/core/src/net/sf/openrocket/file/GeneralRocketSaver.java index 877435ab2..4a4ecbec6 100644 --- a/core/src/net/sf/openrocket/file/GeneralRocketSaver.java +++ b/core/src/net/sf/openrocket/file/GeneralRocketSaver.java @@ -20,6 +20,7 @@ import net.sf.openrocket.document.StorageOptions; import net.sf.openrocket.document.StorageOptions.FileType; import net.sf.openrocket.file.openrocket.OpenRocketSaver; import net.sf.openrocket.file.rocksim.export.RocksimSaver; +import net.sf.openrocket.rocketcomponent.InsideColorComponent; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.util.MathUtil; @@ -84,7 +85,7 @@ public class GeneralRocketSaver { * * @param dest the destination stream. * @param doc the document to save. - * @param options the storage options. + * @param opts the storage options. * @param progress a SavingProgress object used to provide progress information * @throws IOException in case of an I/O error. */ @@ -159,17 +160,22 @@ public class GeneralRocketSaver { // Look for all decals used in the rocket. for (RocketComponent c : document.getRocket()) { - if (c.getAppearance() == null) { - continue; - } Appearance ap = c.getAppearance(); - if (ap.getTexture() == null) { - continue; + Appearance ap_in = null; + if (c instanceof InsideColorComponent) + ap_in = ((InsideColorComponent)c).getInsideColorComponentHandler().getInsideAppearance(); + + if ((ap == null) && (ap_in == null)) continue; + if (ap != null) { + Decal decal = ap.getTexture(); + if (decal != null) + usedDecals.add(decal.getImage()); + } + if (ap_in != null) { + Decal decal = ap_in.getTexture(); + if (decal != null) + usedDecals.add(decal.getImage()); } - - Decal decal = ap.getTexture(); - - usedDecals.add(decal.getImage()); } saveAllPartsZipFile(output, document, options, usedDecals); diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/AppearanceHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/AppearanceHandler.java index b59cea9cd..4d7445d14 100644 --- a/core/src/net/sf/openrocket/file/openrocket/importt/AppearanceHandler.java +++ b/core/src/net/sf/openrocket/file/openrocket/importt/AppearanceHandler.java @@ -16,11 +16,11 @@ import net.sf.openrocket.util.Color; import org.xml.sax.SAXException; class AppearanceHandler extends AbstractElementHandler { - private final DocumentLoadingContext context; - private final RocketComponent component; - - private final AppearanceBuilder builder = new AppearanceBuilder(); - private boolean isInDecal = false; + protected final DocumentLoadingContext context; + protected final RocketComponent component; + + protected final AppearanceBuilder builder = new AppearanceBuilder(); + protected boolean isInDecal = false; public AppearanceHandler(RocketComponent component, DocumentLoadingContext context) { this.context = context; @@ -44,7 +44,7 @@ class AppearanceHandler extends AbstractElementHandler { } return PlainTextHandler.INSTANCE; } - + @Override public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { if ("paint".equals(element)) { @@ -99,8 +99,12 @@ class AppearanceHandler extends AbstractElementHandler { isInDecal = false; return; } - component.setAppearance(builder.getAppearance()); + setAppearance(); super.endHandler(element, attributes, content, warnings); } + + protected void setAppearance() { + component.setAppearance(builder.getAppearance()); + } } diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/ComponentParameterHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/ComponentParameterHandler.java index 7bc42fe54..89bb2fd6d 100644 --- a/core/src/net/sf/openrocket/file/openrocket/importt/ComponentParameterHandler.java +++ b/core/src/net/sf/openrocket/file/openrocket/importt/ComponentParameterHandler.java @@ -40,6 +40,9 @@ class ComponentParameterHandler extends AbstractElementHandler { if ( element.equals("appearance")) { return new AppearanceHandler(component,context); } + if (element.equals("inside-appearance")) { + return new InsideAppearanceHandler(component, context); + } if (element.equals("motormount")) { if (!(component instanceof MotorMount)) { warnings.add(Warning.fromString("Illegal component defined as motor mount.")); @@ -92,8 +95,8 @@ class ComponentParameterHandler extends AbstractElementHandler { if (element.equals("subcomponents") || element.equals("motormount") || element.equals("finpoints") || element.equals("motorconfiguration") || - element.equals("appearance") || element.equals("deploymentconfiguration") || - element.equals("separationconfiguration")) { + element.equals("appearance") || element.equals("inside-appearance") || + element.equals("deploymentconfiguration") || element.equals("separationconfiguration")) { return; } diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/InsideAppearanceHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/InsideAppearanceHandler.java new file mode 100644 index 000000000..37ec07d23 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/InsideAppearanceHandler.java @@ -0,0 +1,45 @@ +package net.sf.openrocket.file.openrocket.importt; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.appearance.AppearanceBuilder; +import net.sf.openrocket.appearance.Decal; +import net.sf.openrocket.document.Attachment; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.rocketcomponent.InsideColorComponent; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import org.xml.sax.SAXException; + +import java.util.HashMap; + +public class InsideAppearanceHandler extends AppearanceHandler { + public InsideAppearanceHandler(RocketComponent component, DocumentLoadingContext context) { + super(component, context); + } + + @Override + public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { + if ("edgesSameAsInside".equals(element)) { + boolean edgesSameAsInside = Boolean.parseBoolean(content); + if (component instanceof InsideColorComponent) + ((InsideColorComponent)component).getInsideColorComponentHandler().setEdgesSameAsInside(edgesSameAsInside); + return; + } + if ("insideSameAsOutside".equals(element)) { + boolean insideSameAsOutside = Boolean.parseBoolean(content); + if (component instanceof InsideColorComponent) + ((InsideColorComponent)component).getInsideColorComponentHandler().setInsideSameAsOutside(insideSameAsOutside); + return; + } + + super.closeElement(element, attributes, content, warnings); + } + + @Override + protected void setAppearance() { + if ((component instanceof InsideColorComponent)) + ((InsideColorComponent)component).getInsideColorComponentHandler().setInsideAppearance(builder.getAppearance()); + } +} diff --git a/core/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java b/core/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java index 293550cb6..1c4acc289 100644 --- a/core/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java +++ b/core/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java @@ -15,14 +15,7 @@ import net.sf.openrocket.motor.Motor; import net.sf.openrocket.motor.MotorConfiguration; import net.sf.openrocket.motor.ThrustCurveMotor; import net.sf.openrocket.preset.ComponentPreset; -import net.sf.openrocket.rocketcomponent.Clusterable; -import net.sf.openrocket.rocketcomponent.ComponentAssembly; -import net.sf.openrocket.rocketcomponent.FlightConfigurationId; -import net.sf.openrocket.rocketcomponent.Instanceable; -import net.sf.openrocket.rocketcomponent.LineInstanceable; -import net.sf.openrocket.rocketcomponent.MotorMount; -import net.sf.openrocket.rocketcomponent.Rocket; -import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.*; import net.sf.openrocket.rocketcomponent.position.AnglePositionable; import net.sf.openrocket.rocketcomponent.position.AxialMethod; import net.sf.openrocket.rocketcomponent.position.RadiusPositionable; @@ -49,29 +42,27 @@ public class RocketComponentSaver { "\" manufacturer=\"" + preset.getManufacturer().getSimpleName() + "\" partno=\"" + preset.getPartNo() + "\" digest=\"" + preset.getDigest() + "\"/>"); } - + + // Save outside appearance Appearance ap = c.getAppearance(); if (ap != null) { elements.add(""); - Color paint = ap.getPaint(); - emitColor("paint", elements, paint); - elements.add("" + ap.getShine() + ""); - Decal decal = ap.getTexture(); - if (decal != null) { - String name = decal.getImage().getName(); - double rotation = decal.getRotation(); - EdgeMode edgeMode = decal.getEdgeMode(); - elements.add(""); - Coordinate center = decal.getCenter(); - elements.add("
"); - Coordinate offset = decal.getOffset(); - elements.add(""); - Coordinate scale = decal.getScale(); - elements.add(""); - elements.add(""); - } + buildAppearanceElements(elements, ap); elements.add(""); } + + // Save inside appearance + if (c instanceof InsideColorComponent) { + InsideColorComponentHandler handler = ((InsideColorComponent)c).getInsideColorComponentHandler(); + Appearance ap_in = handler.getInsideAppearance(); + if (ap_in != null) { + elements.add(""); + elements.add("" + handler.isEdgesSameAsInside() + ""); + elements.add("" + handler.isInsideSameAsOutside() + ""); + buildAppearanceElements(elements, ap_in); + elements.add(""); + } + } // Save color and line style if significant if (!(c instanceof Rocket || c instanceof ComponentAssembly)) { @@ -147,10 +138,27 @@ public class RocketComponentSaver { } } - - - - + + private void buildAppearanceElements(List elements, Appearance a) { + Color paint = a.getPaint(); + emitColor("paint", elements, paint); + elements.add("" + a.getShine() + ""); + Decal decal = a.getTexture(); + if (decal != null) { + String name = decal.getImage().getName(); + double rotation = decal.getRotation(); + EdgeMode edgeMode = decal.getEdgeMode(); + elements.add(""); + Coordinate center = decal.getCenter(); + elements.add("
"); + Coordinate offset = decal.getOffset(); + elements.add(""); + Coordinate scale = decal.getScale(); + elements.add(""); + elements.add(""); + } + } + protected final String materialParam(Material mat) { return materialParam("material", mat); } diff --git a/core/src/net/sf/openrocket/rocketcomponent/BodyTube.java b/core/src/net/sf/openrocket/rocketcomponent/BodyTube.java index 4428dfac4..2a43e7279 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/BodyTube.java +++ b/core/src/net/sf/openrocket/rocketcomponent/BodyTube.java @@ -1,19 +1,17 @@ package net.sf.openrocket.rocketcomponent; -import java.util.ArrayList; -import java.util.Collection; +import java.util.EventObject; import java.util.Iterator; +import net.sf.openrocket.appearance.Appearance; +import net.sf.openrocket.appearance.Decal; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.motor.Motor; import net.sf.openrocket.motor.MotorConfiguration; import net.sf.openrocket.motor.MotorConfigurationSet; import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.startup.Application; -import net.sf.openrocket.util.BoundingBox; -import net.sf.openrocket.util.BugException; -import net.sf.openrocket.util.Coordinate; -import net.sf.openrocket.util.MathUtil; +import net.sf.openrocket.util.*; /** @@ -22,7 +20,7 @@ import net.sf.openrocket.util.MathUtil; * @author Sampo Niskanen */ -public class BodyTube extends SymmetricComponent implements BoxBounded, MotorMount, Coaxial { +public class BodyTube extends SymmetricComponent implements BoxBounded, MotorMount, Coaxial, InsideColorComponent { private static final Translator trans = Application.getTranslator(); private double outerRadius = 0; @@ -33,6 +31,8 @@ public class BodyTube extends SymmetricComponent implements BoxBounded, MotorMou private boolean isActingMount = false; private MotorConfigurationSet motors; + + private final InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); public BodyTube() { this(8 * DEFAULT_RADIUS, DEFAULT_RADIUS); @@ -455,4 +455,10 @@ public class BodyTube extends SymmetricComponent implements BoxBounded, MotorMou public ClusterConfiguration getClusterConfiguration() { return ClusterConfiguration.SINGLE; } + + + @Override + public InsideColorComponentHandler getInsideColorComponentHandler() { + return this.insideColorComponentHandler; + } } diff --git a/core/src/net/sf/openrocket/rocketcomponent/InsideColorComponent.java b/core/src/net/sf/openrocket/rocketcomponent/InsideColorComponent.java new file mode 100644 index 000000000..c964b902a --- /dev/null +++ b/core/src/net/sf/openrocket/rocketcomponent/InsideColorComponent.java @@ -0,0 +1,25 @@ +package net.sf.openrocket.rocketcomponent; + +import net.sf.openrocket.appearance.Appearance; +import net.sf.openrocket.appearance.Decal; +import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.position.AxialMethod; +import net.sf.openrocket.util.StateChangeListener; + +import java.util.EventObject; + +/** + * This is a marker interface which, if applied to a component, will mark that component as having the possibility to + * have a different inside and outside color. This will cause the appearance editor of that component to have a separate + * section for the inside and outside color and will consequently also render the inside and outside of that component + * (in a 3D figure) differently. + * + * @author Sibo Van Gool + */ +public interface InsideColorComponent { + /** + * @return the InsideColorComponentHandler + */ + InsideColorComponentHandler getInsideColorComponentHandler(); +} diff --git a/core/src/net/sf/openrocket/rocketcomponent/InsideColorComponentHandler.java b/core/src/net/sf/openrocket/rocketcomponent/InsideColorComponentHandler.java new file mode 100644 index 000000000..44f966418 --- /dev/null +++ b/core/src/net/sf/openrocket/rocketcomponent/InsideColorComponentHandler.java @@ -0,0 +1,97 @@ +package net.sf.openrocket.rocketcomponent; + +import net.sf.openrocket.appearance.Appearance; +import net.sf.openrocket.appearance.Decal; +import net.sf.openrocket.util.StateChangeListener; + +import java.util.EventObject; + +/** + * This component handles the necessary functionalities of an InsideColorComponent. + * + * @author Sibo Van Gool + */ +public class InsideColorComponentHandler { + private final RocketComponent component; + private Appearance insideAppearance = null; + private boolean insideSameAsOutside = true; + private boolean edgesSameAsInside = true; + + public InsideColorComponentHandler(RocketComponent component) { + this.component = component; + } + + /** + * Get the realistic inside appearance of this component. + * null = use the default for this material + * + * @return The component's realistic inner appearance, or null + */ + public Appearance getInsideAppearance() { + return this.insideAppearance; + } + + /** + * Set the realistic inside appearance of this component. + * Use null for default. + * + * @param appearance the inner appearance to be set + */ + public void setInsideAppearance(Appearance appearance) { + this.insideAppearance = appearance; + if (this.insideAppearance != null) { + Decal d = this.insideAppearance.getTexture(); + if (d != null) { + d.getImage().addChangeListener(new StateChangeListener() { + + @Override + public void stateChanged(EventObject e) { + component.fireComponentChangeEvent(ComponentChangeEvent.TEXTURE_CHANGE); + } + + }); + } + } + component.fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); + } + + /** + * Checks whether the component uses for the edges the same appearance as the inside (return true) or as the + * outside (return false) + * + * @return true if edges should use the same appearance as the inside, + * false if edges should use the same appearance as the outside + */ + public boolean isEdgesSameAsInside() { + return this.edgesSameAsInside; + } + + /** + * Sets the new state for edgesUseInsideAppearance to newState + * + * @param newState new edgesUseInsideAppearance value + */ + public void setEdgesSameAsInside(boolean newState) { + this.edgesSameAsInside = newState; + } + + /** + * Checks whether the component should use the same appearance for the inside as the outside (return true) or as the + * outside (return false) + * + * @return true if edges should use the same appearance as the inside, + * false if edges should use the same appearance as the outside + */ + public boolean isInsideSameAsOutside() { + return this.insideSameAsOutside; + } + + /** + * Sets the new state for insideSameAsOutside to newState + * + * @param newState new edgesUseInsideAppearance value + */ + public void setInsideSameAsOutside(boolean newState) { + this.insideSameAsOutside = newState; + } +} diff --git a/core/src/net/sf/openrocket/rocketcomponent/LaunchLug.java b/core/src/net/sf/openrocket/rocketcomponent/LaunchLug.java index 92ef86e72..26a4a126c 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/LaunchLug.java +++ b/core/src/net/sf/openrocket/rocketcomponent/LaunchLug.java @@ -2,7 +2,10 @@ package net.sf.openrocket.rocketcomponent; import java.util.ArrayList; import java.util.Collection; +import java.util.EventObject; +import net.sf.openrocket.appearance.Appearance; +import net.sf.openrocket.appearance.Decal; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.preset.ComponentPreset.Type; @@ -11,10 +14,10 @@ import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.BoundingBox; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.MathUtil; +import net.sf.openrocket.util.StateChangeListener; - -public class LaunchLug extends ExternalComponent implements AnglePositionable, BoxBounded, Coaxial, LineInstanceable { +public class LaunchLug extends ExternalComponent implements AnglePositionable, BoxBounded, Coaxial, LineInstanceable, InsideColorComponent { private static final Translator trans = Application.getTranslator(); @@ -26,6 +29,8 @@ public class LaunchLug extends ExternalComponent implements AnglePositionable, B private int instanceCount = 1; private double instanceSeparation = 0; // front-front along the positive rocket axis. i.e. [1,0,0]; + + private final InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); public LaunchLug() { super(AxialMethod.MIDDLE); @@ -279,4 +284,9 @@ public class LaunchLug extends ExternalComponent implements AnglePositionable, B public void setAngleMethod(AngleMethod newMethod) { // no-op } + + @Override + public InsideColorComponentHandler getInsideColorComponentHandler() { + return this.insideColorComponentHandler; + } } diff --git a/core/src/net/sf/openrocket/rocketcomponent/NoseCone.java b/core/src/net/sf/openrocket/rocketcomponent/NoseCone.java index f58f40bd2..bc9855953 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/NoseCone.java +++ b/core/src/net/sf/openrocket/rocketcomponent/NoseCone.java @@ -1,9 +1,14 @@ package net.sf.openrocket.rocketcomponent; +import net.sf.openrocket.appearance.Appearance; +import net.sf.openrocket.appearance.Decal; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.preset.ComponentPreset.Type; import net.sf.openrocket.startup.Application; +import net.sf.openrocket.util.StateChangeListener; + +import java.util.EventObject; /** * Rocket nose cones of various types. Implemented as a transition with the @@ -12,9 +17,10 @@ import net.sf.openrocket.startup.Application; * @author Sampo Niskanen */ -public class NoseCone extends Transition { +public class NoseCone extends Transition implements InsideColorComponent { private static final Translator trans = Application.getTranslator(); - + + private final InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); /********* Constructors **********/ public NoseCone() { @@ -135,5 +141,10 @@ public class NoseCone extends Transition { //// Nose cone return trans.get("NoseCone.NoseCone"); } - + + + @Override + public InsideColorComponentHandler getInsideColorComponentHandler() { + return this.insideColorComponentHandler; + } } diff --git a/core/src/net/sf/openrocket/rocketcomponent/Transition.java b/core/src/net/sf/openrocket/rocketcomponent/Transition.java index d89f6de69..f8852c12a 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Transition.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Transition.java @@ -5,16 +5,20 @@ import static net.sf.openrocket.util.MathUtil.pow2; import static net.sf.openrocket.util.MathUtil.pow3; import java.util.Collection; +import java.util.EventObject; +import net.sf.openrocket.appearance.Appearance; +import net.sf.openrocket.appearance.Decal; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.preset.ComponentPreset.Type; import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.MathUtil; +import net.sf.openrocket.util.StateChangeListener; -public class Transition extends SymmetricComponent { +public class Transition extends SymmetricComponent implements InsideColorComponent { private static final Translator trans = Application.getTranslator(); private static final double CLIP_PRECISION = 0.0001; @@ -40,6 +44,8 @@ public class Transition extends SymmetricComponent { // Used to cache the clip length private double clipLength = -1; + private final InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); + public Transition() { super(); @@ -598,6 +604,11 @@ public class Transition extends SymmetricComponent { } + @Override + public InsideColorComponentHandler getInsideColorComponentHandler() { + return this.insideColorComponentHandler; + } + /** * An enumeration listing the possible shapes of transitions. * @@ -938,4 +949,5 @@ public class Transition extends SymmetricComponent { } } + } diff --git a/core/src/net/sf/openrocket/rocketcomponent/TubeFinSet.java b/core/src/net/sf/openrocket/rocketcomponent/TubeFinSet.java index 820e6896d..4dd4843b6 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/TubeFinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/TubeFinSet.java @@ -2,8 +2,11 @@ package net.sf.openrocket.rocketcomponent; import java.util.ArrayList; import java.util.Collection; +import java.util.EventObject; import java.util.List; +import net.sf.openrocket.appearance.Appearance; +import net.sf.openrocket.appearance.Decal; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.preset.ComponentPreset.Type; @@ -12,12 +15,9 @@ import net.sf.openrocket.rocketcomponent.position.AxialMethod; import net.sf.openrocket.rocketcomponent.position.AxialPositionable; import net.sf.openrocket.rocketcomponent.position.RadiusMethod; import net.sf.openrocket.startup.Application; -import net.sf.openrocket.util.BoundingBox; -import net.sf.openrocket.util.Coordinate; -import net.sf.openrocket.util.MathUtil; -import net.sf.openrocket.util.Transformation; +import net.sf.openrocket.util.*; -public class TubeFinSet extends ExternalComponent implements AxialPositionable, BoxBounded, RingInstanceable { +public class TubeFinSet extends ExternalComponent implements AxialPositionable, BoxBounded, RingInstanceable, InsideColorComponent { private static final Translator trans = Application.getTranslator(); private final static double DEFAULT_RADIUS = 0.025; @@ -27,6 +27,8 @@ public class TubeFinSet extends ExternalComponent implements AxialPositionable, protected double thickness = 0.002; private AngleMethod angleMethod = AngleMethod.FIXED; protected RadiusMethod radiusMethod = RadiusMethod.RELATIVE; + + private final InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); /** * Rotation angle of the first fin. Zero corresponds to the positive y-axis. @@ -452,5 +454,10 @@ public class TubeFinSet extends ExternalComponent implements AxialPositionable, // TODO Auto-generated method stub } - + + + @Override + public InsideColorComponentHandler getInsideColorComponentHandler() { + return this.insideColorComponentHandler; + } } diff --git a/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java b/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java index 33fd7cac2..a0bee20fa 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java @@ -6,19 +6,7 @@ import java.awt.event.ActionListener; import java.lang.reflect.Method; import java.util.EventObject; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JColorChooser; -import javax.swing.JComboBox; -import javax.swing.JDialog; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JSeparator; -import javax.swing.JSlider; -import javax.swing.JSpinner; -import javax.swing.SwingConstants; -import javax.swing.SwingUtilities; +import javax.swing.*; import javax.swing.colorchooser.ColorSelectionModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; @@ -45,6 +33,9 @@ import net.sf.openrocket.gui.util.EditDecalHelper; import net.sf.openrocket.gui.util.EditDecalHelper.EditDecalHelperException; import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; +import net.sf.openrocket.rocketcomponent.InsideColorComponent; +import net.sf.openrocket.rocketcomponent.InsideColorComponentHandler; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.startup.Application; import net.sf.openrocket.unit.GeneralUnit; @@ -58,15 +49,18 @@ public class AppearancePanel extends JPanel { private static final Translator trans = Application.getTranslator(); - private EditDecalHelper editDecalHelper = Application.getInjector() + final private EditDecalHelper editDecalHelper = Application.getInjector() .getInstance(EditDecalHelper.class); - private AppearanceBuilder ab; + // Outside and inside appearance builder + final private AppearanceBuilder ab; + private AppearanceBuilder insideAb; // We hang on to the user selected appearance when switching to default // appearance. // this appearance is restored if the user unchecks the "default" button. private Appearance previousUserSelectedAppearance = null; + private Appearance previousUserSelectedInsideAppearance = null; // We cache the default appearance for this component to make switching // faster. @@ -105,7 +99,7 @@ public class AppearancePanel extends JPanel { } /** - Changes the color of the selected color to + Changes the color of the selected component to @param color: color to change the component to */ private void changeComponentColor(Color color) { @@ -182,8 +176,9 @@ public class AppearancePanel extends JPanel { final RocketComponent c) { super(new MigLayout("fill", "[150][grow][150][grow]")); - previousUserSelectedAppearance = c.getAppearance(); defaultAppearance = DefaultAppearance.getDefaultAppearance(c); + + previousUserSelectedAppearance = c.getAppearance(); if (previousUserSelectedAppearance == null) { previousUserSelectedAppearance = new AppearanceBuilder() .getAppearance(); @@ -192,6 +187,18 @@ public class AppearancePanel extends JPanel { ab = new AppearanceBuilder(previousUserSelectedAppearance); } + if (c instanceof InsideColorComponent) { + previousUserSelectedInsideAppearance = ((InsideColorComponent) c).getInsideColorComponentHandler() + .getInsideAppearance(); + if (previousUserSelectedInsideAppearance == null) { + previousUserSelectedInsideAppearance = new AppearanceBuilder() + .getAppearance(); + insideAb = new AppearanceBuilder(defaultAppearance); + } else { + insideAb = new AppearanceBuilder(previousUserSelectedInsideAppearance); + } + } + net.sf.openrocket.util.Color figureColor = c.getColor(); if (figureColor == null) { figureColor = Application.getPreferences().getDefaultColor( @@ -200,19 +207,9 @@ public class AppearancePanel extends JPanel { final JButton figureColorButton = new JButton( new ColorIcon(figureColor)); - final JButton colorButton = new JButton(new ColorIcon(ab.getPaint())); - - final DecalModel decalModel = new DecalModel(this, document, ab); - final JComboBox textureDropDown = new JComboBox(decalModel); - ab.addChangeListener(new StateChangeListener() { @Override - public void stateChanged(EventObject e) { - figureColorButton.setIcon(new ColorIcon(c.getColor())); - colorButton.setIcon(new ColorIcon(ab.getPaint())); - c.setAppearance(ab.getAppearance()); - decalModel.refresh(); - } + public void stateChanged(EventObject e) { figureColorButton.setIcon(new ColorIcon(c.getColor())); } }); c.addChangeListener(new StateChangeListener() { @@ -229,9 +226,7 @@ public class AppearancePanel extends JPanel { figureColorButton .addActionListener(new ColorActionListener(c, "Color")); - colorButton.addActionListener(new ColorActionListener(ab, "Paint")); - BooleanModel mDefault = new BooleanModel(c.getAppearance() == null); BooleanModel fDefault = new BooleanModel(c.getColor() == null); {// Style Header Row @@ -285,7 +280,6 @@ public class AppearancePanel extends JPanel { } {// Line Style - add(new JLabel(trans.get("RocketCompCfg.lbl.Complinestyle"))); LineStyle[] list = new LineStyle[LineStyle.values().length + 1]; @@ -304,152 +298,245 @@ public class AppearancePanel extends JPanel { add(new JSeparator(SwingConstants.HORIZONTAL), "span, wrap, growx"); - {// Texture Header Row - add(new StyledLabel(trans.get("AppearanceCfg.lbl.Appearance"), - Style.BOLD)); - final JCheckBox materialDefault = new JCheckBox(mDefault); - materialDefault.addActionListener(new ActionListener() { + // Display a tabbed panel for choosing the outside and inside appearance, if the object is of type InsideColorComponent + if (c instanceof InsideColorComponent) { + InsideColorComponentHandler handler = ((InsideColorComponent)c).getInsideColorComponentHandler(); + + JTabbedPane tabbedPane = new JTabbedPane(); + JPanel outsidePanel = new JPanel(new MigLayout("fill", "[150][grow][150][grow]")); + JPanel insidePanel = new JPanel(new MigLayout("fill", "[150][grow][150][grow]")); + + appearanceSection(document, c, false, outsidePanel); + appearanceSection(document, c, true, insidePanel); + + tabbedPane.addTab(trans.get("RocketCompCfg.tab.Outside"), null, outsidePanel, + "Outside Tool Tip"); + tabbedPane.addTab(trans.get("RocketCompCfg.tab.Inside"), null, insidePanel, + "Inside Tool Tip"); + add(tabbedPane, "span 4, growx, wrap"); + + // Checkbox to set edges the same as inside/outside + BooleanModel b = new BooleanModel(handler.isEdgesSameAsInside()); + JCheckBox edges = new JCheckBox(b); + edges.setText(trans.get("AppearanceCfg.lbl.EdgesSameAsInside")); + edges.setToolTipText(trans.get("AppearanceCfg.lbl.ttip.EdgesSameAsInside")); + add(edges, "wrap"); + + edges.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - if (materialDefault.isSelected()) { - previousUserSelectedAppearance = (ab == null) ? null - : ab.getAppearance(); - ab.setAppearance(defaultAppearance); - c.setAppearance(null); - } else { - ab.setAppearance(previousUserSelectedAppearance); - } + handler.setEdgesSameAsInside(edges.isSelected()); + c.fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } }); - materialDefault.setText(trans.get("AppearanceCfg.lbl.Usedefault")); - add(materialDefault, "wrap"); } + else + appearanceSection(document, c, false, this); + } - {// Texture File - add(new JLabel(trans.get("AppearanceCfg.lbl.Texture"))); - JPanel p = new JPanel(new MigLayout("fill, ins 0", "[grow][]")); - mDefault.addEnableComponent(textureDropDown, false); - p.add(textureDropDown, "grow"); - add(p, "span 3, growx, wrap"); - final JButton editBtn = new JButton( - trans.get("AppearanceCfg.but.edit")); - editBtn.setEnabled(ab.getImage() != null); - // Enable the editBtn only when the appearance builder has an Image - // assigned to it. - ab.addChangeListener(new StateChangeListener() { - @Override - public void stateChanged(EventObject e) { - editBtn.setEnabled(ab.getImage() != null); + /** + * + * @param document + * @param c + * @param insideBuilder flag to check whether you are on the inside builder (true) or outside builder + * @param panel + */ + private void appearanceSection(OpenRocketDocument document, RocketComponent c, + boolean insideBuilder, JPanel panel) { + AppearanceBuilder builder; + BooleanModel mDefault; + if (!insideBuilder) { + builder = ab; + mDefault = new BooleanModel(c.getAppearance() == null); + } + else if (c instanceof InsideColorComponent) { + builder = insideAb; + mDefault = new BooleanModel( + ((InsideColorComponent)c).getInsideColorComponentHandler().getInsideAppearance() == null); + } + else return; + + DecalModel decalModel = new DecalModel(panel, document, builder); + JComboBox textureDropDown = new JComboBox(decalModel); + + JButton colorButton = new JButton(new ColorIcon(builder.getPaint())); + + builder.addChangeListener(new StateChangeListener() { + @Override + public void stateChanged(EventObject e) { + colorButton.setIcon(new ColorIcon(builder.getPaint())); + if (!insideBuilder) + c.setAppearance(builder.getAppearance()); + else + ((InsideColorComponent)c).getInsideColorComponentHandler().setInsideAppearance(builder.getAppearance()); + decalModel.refresh(); + } + }); + + colorButton.addActionListener(new ColorActionListener(builder, "Paint")); + + // Texture Header Row + panel.add(new StyledLabel(trans.get("AppearanceCfg.lbl.Appearance"), + Style.BOLD)); + JCheckBox materialDefault = new JCheckBox(mDefault); + materialDefault.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (materialDefault.isSelected()) { + if (!insideBuilder) { + previousUserSelectedAppearance = (builder == null) ? null + : builder.getAppearance(); + } + else { + previousUserSelectedInsideAppearance = (builder == null) ? null + : builder.getAppearance(); + } + builder.setAppearance(defaultAppearance); + } else { + if (!insideBuilder) + builder.setAppearance(previousUserSelectedAppearance); + else + builder.setAppearance(previousUserSelectedInsideAppearance); } - }); - editBtn.addActionListener(new ActionListener() { + } + }); + materialDefault.setText(trans.get("AppearanceCfg.lbl.Usedefault")); + if (insideBuilder) + panel.add(materialDefault); + else + panel.add(materialDefault, "wrap"); + // Custom inside color + if (insideBuilder) { + InsideColorComponentHandler handler = ((InsideColorComponent)c).getInsideColorComponentHandler(); + BooleanModel b = new BooleanModel(handler.isInsideSameAsOutside()); + JCheckBox customInside = new JCheckBox(b); + customInside.setText(trans.get("AppearanceCfg.lbl.InsideSameAsOutside")); + customInside.setToolTipText(trans.get("AppearanceCfg.lbl.ttip.InsideSameAsOutside")); + customInside.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - try { - DecalImage newImage = editDecalHelper.editDecal( - SwingUtilities - .getWindowAncestor(AppearancePanel.this), - document, c, ab.getImage()); - ab.setImage(newImage); - } catch (EditDecalHelperException ex) { - JOptionPane.showMessageDialog(AppearancePanel.this, - ex.getMessage(), "", JOptionPane.ERROR_MESSAGE); - } + handler.setInsideSameAsOutside(customInside.isSelected()); + c.fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } - }); - p.add(editBtn); + panel.add(customInside, "wrap"); } - { // Color - add(new JLabel(trans.get("AppearanceCfg.lbl.color.Color"))); - mDefault.addEnableComponent(colorButton, false); - add(colorButton); - } + // Texture File + panel.add(new JLabel(trans.get("AppearanceCfg.lbl.Texture"))); + JPanel p = new JPanel(new MigLayout("fill, ins 0", "[grow][]")); + mDefault.addEnableComponent(textureDropDown, false); + p.add(textureDropDown, "grow"); + panel.add(p, "span 3, growx, wrap"); + JButton editBtn = new JButton( + trans.get("AppearanceCfg.but.edit")); + editBtn.setEnabled(builder.getImage() != null); + // Enable the editBtn only when the appearance builder has an Image + // assigned to it. + builder.addChangeListener(new StateChangeListener() { + @Override + public void stateChanged(EventObject e) { + editBtn.setEnabled(builder.getImage() != null); + } + }); - { // Scale - add(new JLabel(trans.get("AppearanceCfg.lbl.texture.scale"))); + editBtn.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + try { + DecalImage newImage = editDecalHelper.editDecal( + SwingUtilities + .getWindowAncestor(panel), + document, c, builder.getImage(), insideBuilder); + builder.setImage(newImage); + } catch (EditDecalHelperException ex) { + JOptionPane.showMessageDialog(panel, + ex.getMessage(), "", JOptionPane.ERROR_MESSAGE); + } + } - add(new JLabel("x:"), "split 4"); - JSpinner scaleU = new JSpinner(new DoubleModel(ab, "ScaleX", - TEXTURE_UNIT).getSpinnerModel()); - scaleU.setEditor(new SpinnerEditor(scaleU)); - mDefault.addEnableComponent(scaleU, false); - add(scaleU, "w 40"); + }); + p.add(editBtn); - add(new JLabel("y:")); - JSpinner scaleV = new JSpinner(new DoubleModel(ab, "ScaleY", - TEXTURE_UNIT).getSpinnerModel()); - scaleV.setEditor(new SpinnerEditor(scaleV)); - mDefault.addEnableComponent(scaleV, false); - add(scaleV, "wrap, w 40"); - } + // Color + panel.add(new JLabel(trans.get("AppearanceCfg.lbl.color.Color"))); + mDefault.addEnableComponent(colorButton, false); + panel.add(colorButton); - {// Shine - add(new JLabel(trans.get("AppearanceCfg.lbl.shine"))); - DoubleModel shineModel = new DoubleModel(ab, "Shine", - UnitGroup.UNITS_RELATIVE); - // Set the initial value to the reset state, not the shine value of the default appearance of this component - if (mDefault.getValue() && previousUserSelectedAppearance != null) - shineModel.setValue(previousUserSelectedAppearance.getShine()); - JSpinner spin = new JSpinner(shineModel.getSpinnerModel()); - spin.setEditor(new SpinnerEditor(spin)); - JSlider slide = new JSlider(shineModel.getSliderModel(0, 1)); - UnitSelector unit = new UnitSelector(shineModel); + // Scale + panel.add(new JLabel(trans.get("AppearanceCfg.lbl.texture.scale"))); - mDefault.addEnableComponent(slide, false); - mDefault.addEnableComponent(spin, false); - mDefault.addEnableComponent(unit, false); + panel.add(new JLabel("x:"), "split 4"); + JSpinner scaleU = new JSpinner(new DoubleModel(builder, "ScaleX", + TEXTURE_UNIT).getSpinnerModel()); + scaleU.setEditor(new SpinnerEditor(scaleU)); + mDefault.addEnableComponent(scaleU, false); + panel.add(scaleU, "w 40"); - add(spin, "split 3, w 50"); - add(unit); - add(slide, "w 50"); - } + panel.add(new JLabel("y:")); + JSpinner scaleV = new JSpinner(new DoubleModel(builder, "ScaleY", + TEXTURE_UNIT).getSpinnerModel()); + scaleV.setEditor(new SpinnerEditor(scaleV)); + mDefault.addEnableComponent(scaleV, false); + panel.add(scaleV, "wrap, w 40"); - { // Offset - add(new JLabel(trans.get("AppearanceCfg.lbl.texture.offset"))); + // Shine + panel.add(new JLabel(trans.get("AppearanceCfg.lbl.shine"))); + DoubleModel shineModel = new DoubleModel(builder, "Shine", + UnitGroup.UNITS_RELATIVE); + JSpinner spin = new JSpinner(shineModel.getSpinnerModel()); + spin.setEditor(new SpinnerEditor(spin)); + JSlider slide = new JSlider(shineModel.getSliderModel(0, 1)); + UnitSelector unit = new UnitSelector(shineModel); - add(new JLabel("x:"), "split 4"); - JSpinner offsetU = new JSpinner(new DoubleModel(ab, "OffsetU", - TEXTURE_UNIT).getSpinnerModel()); - offsetU.setEditor(new SpinnerEditor(offsetU)); - mDefault.addEnableComponent(offsetU, false); - add(offsetU, "w 40"); + mDefault.addEnableComponent(slide, false); + mDefault.addEnableComponent(spin, false); + mDefault.addEnableComponent(unit, false); - add(new JLabel("y:")); - JSpinner offsetV = new JSpinner(new DoubleModel(ab, "OffsetV", - TEXTURE_UNIT).getSpinnerModel()); - offsetV.setEditor(new SpinnerEditor(offsetV)); - mDefault.addEnableComponent(offsetV, false); - add(offsetV, "wrap, w 40"); - } + panel.add(spin, "split 3, w 50"); + panel.add(unit); + panel.add(slide, "w 50"); - { // Repeat - add(new JLabel(trans.get("AppearanceCfg.lbl.texture.repeat"))); - EdgeMode[] list = new EdgeMode[EdgeMode.values().length]; - System.arraycopy(EdgeMode.values(), 0, list, 0, - EdgeMode.values().length); - JComboBox combo = new JComboBox(new EnumModel(ab, - "EdgeMode", list)); - mDefault.addEnableComponent(combo, false); - add(combo); - } + // Offset + panel.add(new JLabel(trans.get("AppearanceCfg.lbl.texture.offset"))); - { // Rotation - add(new JLabel(trans.get("AppearanceCfg.lbl.texture.rotation"))); - DoubleModel rotationModel = new DoubleModel(ab, "Rotation", - UnitGroup.UNITS_ANGLE); - JSpinner rotation = new JSpinner(rotationModel.getSpinnerModel()); - rotation.setEditor(new SpinnerEditor(rotation)); - mDefault.addEnableComponent(rotation, false); - add(rotation, "split 3, w 50"); - add(new UnitSelector(rotationModel)); - BasicSlider bs = new BasicSlider(rotationModel.getSliderModel( - -Math.PI, Math.PI)); - mDefault.addEnableComponent(bs, false); - add(bs, "w 50, wrap"); - } + panel.add(new JLabel("x:"), "split 4"); + JSpinner offsetU = new JSpinner(new DoubleModel(builder, "OffsetU", + TEXTURE_UNIT).getSpinnerModel()); + offsetU.setEditor(new SpinnerEditor(offsetU)); + mDefault.addEnableComponent(offsetU, false); + panel.add(offsetU, "w 40"); + panel.add(new JLabel("y:")); + JSpinner offsetV = new JSpinner(new DoubleModel(builder, "OffsetV", + TEXTURE_UNIT).getSpinnerModel()); + offsetV.setEditor(new SpinnerEditor(offsetV)); + mDefault.addEnableComponent(offsetV, false); + panel.add(offsetV, "wrap, w 40"); + + // Repeat + panel.add(new JLabel(trans.get("AppearanceCfg.lbl.texture.repeat"))); + EdgeMode[] list = new EdgeMode[EdgeMode.values().length]; + System.arraycopy(EdgeMode.values(), 0, list, 0, + EdgeMode.values().length); + JComboBox combo = new JComboBox(new EnumModel(builder, + "EdgeMode", list)); + mDefault.addEnableComponent(combo, false); + panel.add(combo); + + // Rotation + panel.add(new JLabel(trans.get("AppearanceCfg.lbl.texture.rotation"))); + DoubleModel rotationModel = new DoubleModel(builder, "Rotation", + UnitGroup.UNITS_ANGLE); + JSpinner rotation = new JSpinner(rotationModel.getSpinnerModel()); + rotation.setEditor(new SpinnerEditor(rotation)); + mDefault.addEnableComponent(rotation, false); + panel.add(rotation, "split 3, w 50"); + panel.add(new UnitSelector(rotationModel)); + BasicSlider bs = new BasicSlider(rotationModel.getSliderModel( + -Math.PI, Math.PI)); + mDefault.addEnableComponent(bs, false); + panel.add(bs, "w 50, wrap"); } } diff --git a/swing/src/net/sf/openrocket/gui/figure3d/RealisticRenderer.java b/swing/src/net/sf/openrocket/gui/figure3d/RealisticRenderer.java index e584db68f..9b155748a 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/RealisticRenderer.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/RealisticRenderer.java @@ -14,6 +14,7 @@ import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.gui.figure3d.geometry.Geometry; import net.sf.openrocket.gui.figure3d.geometry.Geometry.Surface; import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.rocketcomponent.InsideColorComponent; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.util.Color; @@ -90,10 +91,24 @@ public class RealisticRenderer extends RocketRenderer { @Override public void renderComponent(final GL2 gl, Geometry geom, final float alpha) { - Appearance app = getAppearance( geom.getComponent() ); - render(gl, geom, Surface.INSIDE, app, true, alpha); + RocketComponent c = geom.getComponent(); + Appearance app = getAppearance(c); + if (c instanceof InsideColorComponent) { + Appearance innerApp = getInsideAppearance(c); + if (((InsideColorComponent) c).getInsideColorComponentHandler().isInsideSameAsOutside()) innerApp = app; + + render(gl, geom, Surface.INSIDE, innerApp, true, alpha); + if (((InsideColorComponent) c).getInsideColorComponentHandler().isEdgesSameAsInside()) + render(gl, geom, Surface.EDGES, innerApp, false, alpha); + else + render(gl, geom, Surface.EDGES, app, false, alpha); + } + else { + render(gl, geom, Surface.INSIDE, app, true, alpha); + render(gl, geom, Surface.EDGES, app, false, alpha); + } render(gl, geom, Surface.OUTSIDE, app, true, alpha); - render(gl, geom, Surface.EDGES, app, false, alpha); + } protected float[] convertColor(Appearance a, float alpha) { @@ -190,6 +205,19 @@ public class RealisticRenderer extends RocketRenderer { } return ret; } + + protected Appearance getInsideAppearance(RocketComponent c) { + if (c instanceof InsideColorComponent) { + Appearance ret = ((InsideColorComponent)c).getInsideColorComponentHandler().getInsideAppearance(); + if (ret == null) { + ret = DefaultAppearance.getDefaultAppearance(c); + } + return ret; + } + else { + return DefaultAppearance.getDefaultAppearance(c); + } + } private int toEdgeMode(Decal.EdgeMode m) { switch (m) { diff --git a/swing/src/net/sf/openrocket/gui/util/EditDecalHelper.java b/swing/src/net/sf/openrocket/gui/util/EditDecalHelper.java index 864f3b2a1..7fbf24e17 100644 --- a/swing/src/net/sf/openrocket/gui/util/EditDecalHelper.java +++ b/swing/src/net/sf/openrocket/gui/util/EditDecalHelper.java @@ -16,6 +16,8 @@ import net.sf.openrocket.gui.watcher.FileWatcher; import net.sf.openrocket.gui.watcher.WatchEvent; import net.sf.openrocket.gui.watcher.WatchService; import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.InsideColorComponent; +import net.sf.openrocket.rocketcomponent.InsideColorComponentHandler; import net.sf.openrocket.rocketcomponent.RocketComponent; import com.google.inject.Inject; @@ -67,10 +69,12 @@ public class EditDecalHelper { * @param doc * @param component * @param decal + * @param insideApp flag to check whether it is the inside appearance that is edited * @return * @throws EditDecalHelperException */ - public DecalImage editDecal(Window parent, OpenRocketDocument doc, RocketComponent component, DecalImage decal) throws EditDecalHelperException { + public DecalImage editDecal(Window parent, OpenRocketDocument doc, RocketComponent component, DecalImage decal, + boolean insideApp) throws EditDecalHelperException { boolean sysPrefSet = prefs.isDecalEditorPreferenceSet(); int usageCount = doc.countDecalUsage(decal); @@ -111,7 +115,10 @@ public class EditDecalHelper { } if (dialog.isEditOne()) { - decal = makeDecalUnique(doc, component, decal); + if (insideApp) + decal = makeDecalUnique(doc, component, decal); + else + decal = makeDecalUniqueInside(doc, component, decal); } launchEditor(useSystemEditor, commandLine, decal); @@ -131,6 +138,21 @@ public class EditDecalHelper { return newImage; } + + private static DecalImage makeDecalUniqueInside(OpenRocketDocument doc, RocketComponent component, DecalImage decal) { + + DecalImage newImage = doc.makeUniqueDecal(decal); + + if (component instanceof InsideColorComponent) { + InsideColorComponentHandler handler = ((InsideColorComponent)component).getInsideColorComponentHandler(); + AppearanceBuilder appearanceBuilder = new AppearanceBuilder(handler.getInsideAppearance()); + appearanceBuilder.setImage(newImage); + + handler.setInsideAppearance(appearanceBuilder.getAppearance()); + } + + return newImage; + } private void launchEditor(boolean useSystemEditor, String commandTemplate, final DecalImage decal) throws EditDecalHelperException {