Merge pull request #962 from SiboVG/issue-905

[fixes #905] Added ability to select a different color for the outside and inside of component
This commit is contained in:
Billy Olsen 2021-07-11 19:07:15 -07:00 committed by GitHub
commit a9ad4208f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 630 additions and 232 deletions

View File

@ -809,6 +809,10 @@ AppearanceCfg.lbl.texture.offset = Offset:
AppearanceCfg.lbl.texture.center = Center: AppearanceCfg.lbl.texture.center = Center:
AppearanceCfg.lbl.texture.rotation = Rotation: AppearanceCfg.lbl.texture.rotation = Rotation:
AppearanceCfg.lbl.texture.repeat = Repeat: 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 ! Texture Wrap Modes
TextureWrap.Repeat = Repeat TextureWrap.Repeat = Repeat
@ -871,6 +875,8 @@ RocketCompCfg.border.Foreshoulder = Fore shoulder
!RocketCompCfg.lbl.Length = Length: !RocketCompCfg.lbl.Length = Length:
RocketCompCfg.lbl.InstanceCount = Instance Count RocketCompCfg.lbl.InstanceCount = Instance Count
RocketCompCfg.lbl.InstanceSeparation = Instance Separation RocketCompCfg.lbl.InstanceSeparation = Instance Separation
RocketCompCfg.tab.Outside = Outside
RocketCompCfg.tab.Inside = Inside
! BulkheadConfig ! BulkheadConfig
BulkheadCfg.tab.Diameter = Diameter: BulkheadCfg.tab.Diameter = Diameter:

View File

@ -9,6 +9,7 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import net.sf.openrocket.rocketcomponent.*;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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.DocumentChangeListener;
import net.sf.openrocket.document.events.SimulationChangeEvent; import net.sf.openrocket.document.events.SimulationChangeEvent;
import net.sf.openrocket.logging.Markers; 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.FlightDataType;
import net.sf.openrocket.simulation.customexpression.CustomExpression; import net.sf.openrocket.simulation.customexpression.CustomExpression;
import net.sf.openrocket.simulation.extension.SimulationExtension; import net.sf.openrocket.simulation.extension.SimulationExtension;
@ -259,7 +254,10 @@ public class OpenRocketDocument implements ComponentChangeListener {
Iterator<RocketComponent> it = rocket.iterator(); Iterator<RocketComponent> it = rocket.iterator();
while (it.hasNext()) { while (it.hasNext()) {
if(hasDecal(it.next(),img)) RocketComponent c = it.next();
if(hasDecal(c ,img))
count++;
else if (hasDecalInside(c, img))
count++; count++;
} }
return count; return count;
@ -285,6 +283,29 @@ public class OpenRocketDocument implements ComponentChangeListener {
return false; 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 * gets a unique identification for the given decal
* @param img the decal to be made unique * @param img the decal to be made unique

View File

@ -20,6 +20,7 @@ import net.sf.openrocket.document.StorageOptions;
import net.sf.openrocket.document.StorageOptions.FileType; import net.sf.openrocket.document.StorageOptions.FileType;
import net.sf.openrocket.file.openrocket.OpenRocketSaver; import net.sf.openrocket.file.openrocket.OpenRocketSaver;
import net.sf.openrocket.file.rocksim.export.RocksimSaver; import net.sf.openrocket.file.rocksim.export.RocksimSaver;
import net.sf.openrocket.rocketcomponent.InsideColorComponent;
import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.MathUtil;
@ -84,7 +85,7 @@ public class GeneralRocketSaver {
* *
* @param dest the destination stream. * @param dest the destination stream.
* @param doc the document to save. * @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 * @param progress a SavingProgress object used to provide progress information
* @throws IOException in case of an I/O error. * @throws IOException in case of an I/O error.
*/ */
@ -159,18 +160,23 @@ public class GeneralRocketSaver {
// Look for all decals used in the rocket. // Look for all decals used in the rocket.
for (RocketComponent c : document.getRocket()) { for (RocketComponent c : document.getRocket()) {
if (c.getAppearance() == null) {
continue;
}
Appearance ap = c.getAppearance(); Appearance ap = c.getAppearance();
if (ap.getTexture() == null) { Appearance ap_in = null;
continue; if (c instanceof InsideColorComponent)
} ap_in = ((InsideColorComponent)c).getInsideColorComponentHandler().getInsideAppearance();
if ((ap == null) && (ap_in == null)) continue;
if (ap != null) {
Decal decal = ap.getTexture(); Decal decal = ap.getTexture();
if (decal != null)
usedDecals.add(decal.getImage()); usedDecals.add(decal.getImage());
} }
if (ap_in != null) {
Decal decal = ap_in.getTexture();
if (decal != null)
usedDecals.add(decal.getImage());
}
}
saveAllPartsZipFile(output, document, options, usedDecals); saveAllPartsZipFile(output, document, options, usedDecals);
} }

View File

@ -16,11 +16,11 @@ import net.sf.openrocket.util.Color;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
class AppearanceHandler extends AbstractElementHandler { class AppearanceHandler extends AbstractElementHandler {
private final DocumentLoadingContext context; protected final DocumentLoadingContext context;
private final RocketComponent component; protected final RocketComponent component;
private final AppearanceBuilder builder = new AppearanceBuilder(); protected final AppearanceBuilder builder = new AppearanceBuilder();
private boolean isInDecal = false; protected boolean isInDecal = false;
public AppearanceHandler(RocketComponent component, DocumentLoadingContext context) { public AppearanceHandler(RocketComponent component, DocumentLoadingContext context) {
this.context = context; this.context = context;
@ -99,8 +99,12 @@ class AppearanceHandler extends AbstractElementHandler {
isInDecal = false; isInDecal = false;
return; return;
} }
component.setAppearance(builder.getAppearance()); setAppearance();
super.endHandler(element, attributes, content, warnings); super.endHandler(element, attributes, content, warnings);
} }
protected void setAppearance() {
component.setAppearance(builder.getAppearance());
}
} }

View File

@ -40,6 +40,9 @@ class ComponentParameterHandler extends AbstractElementHandler {
if ( element.equals("appearance")) { if ( element.equals("appearance")) {
return new AppearanceHandler(component,context); return new AppearanceHandler(component,context);
} }
if (element.equals("inside-appearance")) {
return new InsideAppearanceHandler(component, context);
}
if (element.equals("motormount")) { if (element.equals("motormount")) {
if (!(component instanceof MotorMount)) { if (!(component instanceof MotorMount)) {
warnings.add(Warning.fromString("Illegal component defined as motor mount.")); 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") || if (element.equals("subcomponents") || element.equals("motormount") ||
element.equals("finpoints") || element.equals("motorconfiguration") || element.equals("finpoints") || element.equals("motorconfiguration") ||
element.equals("appearance") || element.equals("deploymentconfiguration") || element.equals("appearance") || element.equals("inside-appearance") ||
element.equals("separationconfiguration")) { element.equals("deploymentconfiguration") || element.equals("separationconfiguration")) {
return; return;
} }

View File

@ -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<String, String> 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());
}
}

View File

@ -15,14 +15,7 @@ import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.MotorConfiguration; import net.sf.openrocket.motor.MotorConfiguration;
import net.sf.openrocket.motor.ThrustCurveMotor; import net.sf.openrocket.motor.ThrustCurveMotor;
import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.preset.ComponentPreset;
import net.sf.openrocket.rocketcomponent.Clusterable; import net.sf.openrocket.rocketcomponent.*;
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.position.AnglePositionable; import net.sf.openrocket.rocketcomponent.position.AnglePositionable;
import net.sf.openrocket.rocketcomponent.position.AxialMethod; import net.sf.openrocket.rocketcomponent.position.AxialMethod;
import net.sf.openrocket.rocketcomponent.position.RadiusPositionable; import net.sf.openrocket.rocketcomponent.position.RadiusPositionable;
@ -50,29 +43,27 @@ public class RocketComponentSaver {
"\" partno=\"" + preset.getPartNo() + "\" digest=\"" + preset.getDigest() + "\"/>"); "\" partno=\"" + preset.getPartNo() + "\" digest=\"" + preset.getDigest() + "\"/>");
} }
// Save outside appearance
Appearance ap = c.getAppearance(); Appearance ap = c.getAppearance();
if (ap != null) { if (ap != null) {
elements.add("<appearance>"); elements.add("<appearance>");
Color paint = ap.getPaint(); buildAppearanceElements(elements, ap);
emitColor("paint", elements, paint);
elements.add("<shine>" + ap.getShine() + "</shine>");
Decal decal = ap.getTexture();
if (decal != null) {
String name = decal.getImage().getName();
double rotation = decal.getRotation();
EdgeMode edgeMode = decal.getEdgeMode();
elements.add("<decal name=\"" + TextUtil.escapeXML(name) + "\" rotation=\"" + rotation + "\" edgemode=\"" + edgeMode.name() + "\">");
Coordinate center = decal.getCenter();
elements.add("<center x=\"" + center.x + "\" y=\"" + center.y + "\"/>");
Coordinate offset = decal.getOffset();
elements.add("<offset x=\"" + offset.x + "\" y=\"" + offset.y + "\"/>");
Coordinate scale = decal.getScale();
elements.add("<scale x=\"" + scale.x + "\" y=\"" + scale.y + "\"/>");
elements.add("</decal>");
}
elements.add("</appearance>"); elements.add("</appearance>");
} }
// Save inside appearance
if (c instanceof InsideColorComponent) {
InsideColorComponentHandler handler = ((InsideColorComponent)c).getInsideColorComponentHandler();
Appearance ap_in = handler.getInsideAppearance();
if (ap_in != null) {
elements.add("<inside-appearance>");
elements.add("<edgesSameAsInside>" + handler.isEdgesSameAsInside() + "</edgesSameAsInside>");
elements.add("<insideSameAsOutside>" + handler.isInsideSameAsOutside() + "</insideSameAsOutside>");
buildAppearanceElements(elements, ap_in);
elements.add("</inside-appearance>");
}
}
// Save color and line style if significant // Save color and line style if significant
if (!(c instanceof Rocket || c instanceof ComponentAssembly)) { if (!(c instanceof Rocket || c instanceof ComponentAssembly)) {
Color color = c.getColor(); Color color = c.getColor();
@ -148,8 +139,25 @@ public class RocketComponentSaver {
} }
private void buildAppearanceElements(List<String> elements, Appearance a) {
Color paint = a.getPaint();
emitColor("paint", elements, paint);
elements.add("<shine>" + a.getShine() + "</shine>");
Decal decal = a.getTexture();
if (decal != null) {
String name = decal.getImage().getName();
double rotation = decal.getRotation();
EdgeMode edgeMode = decal.getEdgeMode();
elements.add("<decal name=\"" + TextUtil.escapeXML(name) + "\" rotation=\"" + rotation + "\" edgemode=\"" + edgeMode.name() + "\">");
Coordinate center = decal.getCenter();
elements.add("<center x=\"" + center.x + "\" y=\"" + center.y + "\"/>");
Coordinate offset = decal.getOffset();
elements.add("<offset x=\"" + offset.x + "\" y=\"" + offset.y + "\"/>");
Coordinate scale = decal.getScale();
elements.add("<scale x=\"" + scale.x + "\" y=\"" + scale.y + "\"/>");
elements.add("</decal>");
}
}
protected final String materialParam(Material mat) { protected final String materialParam(Material mat) {
return materialParam("material", mat); return materialParam("material", mat);

View File

@ -1,19 +1,17 @@
package net.sf.openrocket.rocketcomponent; package net.sf.openrocket.rocketcomponent;
import java.util.ArrayList; import java.util.EventObject;
import java.util.Collection;
import java.util.Iterator; 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.l10n.Translator;
import net.sf.openrocket.motor.Motor; import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.MotorConfiguration; import net.sf.openrocket.motor.MotorConfiguration;
import net.sf.openrocket.motor.MotorConfigurationSet; import net.sf.openrocket.motor.MotorConfigurationSet;
import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.preset.ComponentPreset;
import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.BoundingBox; import net.sf.openrocket.util.*;
import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
/** /**
@ -22,7 +20,7 @@ import net.sf.openrocket.util.MathUtil;
* @author Sampo Niskanen <sampo.niskanen@iki.fi> * @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/ */
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 static final Translator trans = Application.getTranslator();
private double outerRadius = 0; private double outerRadius = 0;
@ -34,6 +32,8 @@ public class BodyTube extends SymmetricComponent implements BoxBounded, MotorMou
private MotorConfigurationSet motors; private MotorConfigurationSet motors;
private final InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this);
public BodyTube() { public BodyTube() {
this(8 * DEFAULT_RADIUS, DEFAULT_RADIUS); this(8 * DEFAULT_RADIUS, DEFAULT_RADIUS);
this.autoRadius = true; this.autoRadius = true;
@ -455,4 +455,10 @@ public class BodyTube extends SymmetricComponent implements BoxBounded, MotorMou
public ClusterConfiguration getClusterConfiguration() { public ClusterConfiguration getClusterConfiguration() {
return ClusterConfiguration.SINGLE; return ClusterConfiguration.SINGLE;
} }
@Override
public InsideColorComponentHandler getInsideColorComponentHandler() {
return this.insideColorComponentHandler;
}
} }

View File

@ -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 <sibo.vangool@hotmail.com>
*/
public interface InsideColorComponent {
/**
* @return the InsideColorComponentHandler
*/
InsideColorComponentHandler getInsideColorComponentHandler();
}

View File

@ -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 <sibo.vangool@hotmail.com>
*/
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.
* <code>null</code> = use the default for this material
*
* @return The component's realistic inner appearance, or <code>null</code>
*/
public Appearance getInsideAppearance() {
return this.insideAppearance;
}
/**
* Set the realistic inside appearance of this component.
* Use <code>null</code> 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;
}
}

View File

@ -2,7 +2,10 @@ package net.sf.openrocket.rocketcomponent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; 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.l10n.Translator;
import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.preset.ComponentPreset;
import net.sf.openrocket.preset.ComponentPreset.Type; 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.BoundingBox;
import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.StateChangeListener;
public class LaunchLug extends ExternalComponent implements AnglePositionable, BoxBounded, Coaxial, LineInstanceable, InsideColorComponent {
public class LaunchLug extends ExternalComponent implements AnglePositionable, BoxBounded, Coaxial, LineInstanceable {
private static final Translator trans = Application.getTranslator(); private static final Translator trans = Application.getTranslator();
@ -27,6 +30,8 @@ public class LaunchLug extends ExternalComponent implements AnglePositionable, B
private int instanceCount = 1; private int instanceCount = 1;
private double instanceSeparation = 0; // front-front along the positive rocket axis. i.e. [1,0,0]; 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() { public LaunchLug() {
super(AxialMethod.MIDDLE); super(AxialMethod.MIDDLE);
radius = 0.01 / 2; radius = 0.01 / 2;
@ -279,4 +284,9 @@ public class LaunchLug extends ExternalComponent implements AnglePositionable, B
public void setAngleMethod(AngleMethod newMethod) { public void setAngleMethod(AngleMethod newMethod) {
// no-op // no-op
} }
@Override
public InsideColorComponentHandler getInsideColorComponentHandler() {
return this.insideColorComponentHandler;
}
} }

View File

@ -1,9 +1,14 @@
package net.sf.openrocket.rocketcomponent; 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.l10n.Translator;
import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.preset.ComponentPreset;
import net.sf.openrocket.preset.ComponentPreset.Type; import net.sf.openrocket.preset.ComponentPreset.Type;
import net.sf.openrocket.startup.Application; 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 * 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 <sampo.niskanen@iki.fi> * @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/ */
public class NoseCone extends Transition { public class NoseCone extends Transition implements InsideColorComponent {
private static final Translator trans = Application.getTranslator(); private static final Translator trans = Application.getTranslator();
private final InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this);
/********* Constructors **********/ /********* Constructors **********/
public NoseCone() { public NoseCone() {
@ -136,4 +142,9 @@ public class NoseCone extends Transition {
return trans.get("NoseCone.NoseCone"); return trans.get("NoseCone.NoseCone");
} }
@Override
public InsideColorComponentHandler getInsideColorComponentHandler() {
return this.insideColorComponentHandler;
}
} }

View File

@ -5,16 +5,20 @@ import static net.sf.openrocket.util.MathUtil.pow2;
import static net.sf.openrocket.util.MathUtil.pow3; import static net.sf.openrocket.util.MathUtil.pow3;
import java.util.Collection; 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.l10n.Translator;
import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.preset.ComponentPreset;
import net.sf.openrocket.preset.ComponentPreset.Type; import net.sf.openrocket.preset.ComponentPreset.Type;
import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil; 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 Translator trans = Application.getTranslator();
private static final double CLIP_PRECISION = 0.0001; private static final double CLIP_PRECISION = 0.0001;
@ -40,6 +44,8 @@ public class Transition extends SymmetricComponent {
// Used to cache the clip length // Used to cache the clip length
private double clipLength = -1; private double clipLength = -1;
private final InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this);
public Transition() { public Transition() {
super(); 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. * An enumeration listing the possible shapes of transitions.
* *
@ -938,4 +949,5 @@ public class Transition extends SymmetricComponent {
} }
} }
} }

View File

@ -2,8 +2,11 @@ package net.sf.openrocket.rocketcomponent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.EventObject;
import java.util.List; 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.l10n.Translator;
import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.preset.ComponentPreset;
import net.sf.openrocket.preset.ComponentPreset.Type; 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.AxialPositionable;
import net.sf.openrocket.rocketcomponent.position.RadiusMethod; import net.sf.openrocket.rocketcomponent.position.RadiusMethod;
import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.BoundingBox; import net.sf.openrocket.util.*;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.Transformation;
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 static final Translator trans = Application.getTranslator();
private final static double DEFAULT_RADIUS = 0.025; private final static double DEFAULT_RADIUS = 0.025;
@ -28,6 +28,8 @@ public class TubeFinSet extends ExternalComponent implements AxialPositionable,
private AngleMethod angleMethod = AngleMethod.FIXED; private AngleMethod angleMethod = AngleMethod.FIXED;
protected RadiusMethod radiusMethod = RadiusMethod.RELATIVE; 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. * Rotation angle of the first fin. Zero corresponds to the positive y-axis.
*/ */
@ -453,4 +455,9 @@ public class TubeFinSet extends ExternalComponent implements AxialPositionable,
} }
@Override
public InsideColorComponentHandler getInsideColorComponentHandler() {
return this.insideColorComponentHandler;
}
} }

View File

@ -6,19 +6,7 @@ import java.awt.event.ActionListener;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.EventObject; import java.util.EventObject;
import javax.swing.JButton; import javax.swing.*;
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.colorchooser.ColorSelectionModel; import javax.swing.colorchooser.ColorSelectionModel;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener; 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.EditDecalHelper.EditDecalHelperException;
import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.l10n.Translator; 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.rocketcomponent.RocketComponent;
import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Application;
import net.sf.openrocket.unit.GeneralUnit; import net.sf.openrocket.unit.GeneralUnit;
@ -58,15 +49,18 @@ public class AppearancePanel extends JPanel {
private static final Translator trans = Application.getTranslator(); private static final Translator trans = Application.getTranslator();
private EditDecalHelper editDecalHelper = Application.getInjector() final private EditDecalHelper editDecalHelper = Application.getInjector()
.getInstance(EditDecalHelper.class); .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 // We hang on to the user selected appearance when switching to default
// appearance. // appearance.
// this appearance is restored if the user unchecks the "default" button. // this appearance is restored if the user unchecks the "default" button.
private Appearance previousUserSelectedAppearance = null; private Appearance previousUserSelectedAppearance = null;
private Appearance previousUserSelectedInsideAppearance = null;
// We cache the default appearance for this component to make switching // We cache the default appearance for this component to make switching
// faster. // faster.
@ -105,7 +99,7 @@ public class AppearancePanel extends JPanel {
} }
/** /**
Changes the color of the selected color to <color> Changes the color of the selected component to <color>
@param color: color to change the component to @param color: color to change the component to
*/ */
private void changeComponentColor(Color color) { private void changeComponentColor(Color color) {
@ -182,8 +176,9 @@ public class AppearancePanel extends JPanel {
final RocketComponent c) { final RocketComponent c) {
super(new MigLayout("fill", "[150][grow][150][grow]")); super(new MigLayout("fill", "[150][grow][150][grow]"));
previousUserSelectedAppearance = c.getAppearance();
defaultAppearance = DefaultAppearance.getDefaultAppearance(c); defaultAppearance = DefaultAppearance.getDefaultAppearance(c);
previousUserSelectedAppearance = c.getAppearance();
if (previousUserSelectedAppearance == null) { if (previousUserSelectedAppearance == null) {
previousUserSelectedAppearance = new AppearanceBuilder() previousUserSelectedAppearance = new AppearanceBuilder()
.getAppearance(); .getAppearance();
@ -192,6 +187,18 @@ public class AppearancePanel extends JPanel {
ab = new AppearanceBuilder(previousUserSelectedAppearance); 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(); net.sf.openrocket.util.Color figureColor = c.getColor();
if (figureColor == null) { if (figureColor == null) {
figureColor = Application.getPreferences().getDefaultColor( figureColor = Application.getPreferences().getDefaultColor(
@ -200,19 +207,9 @@ public class AppearancePanel extends JPanel {
final JButton figureColorButton = new JButton( final JButton figureColorButton = new JButton(
new ColorIcon(figureColor)); new ColorIcon(figureColor));
final JButton colorButton = new JButton(new ColorIcon(ab.getPaint()));
final DecalModel decalModel = new DecalModel(this, document, ab);
final JComboBox<DecalImage> textureDropDown = new JComboBox<DecalImage>(decalModel);
ab.addChangeListener(new StateChangeListener() { ab.addChangeListener(new StateChangeListener() {
@Override @Override
public void stateChanged(EventObject e) { public void stateChanged(EventObject e) { figureColorButton.setIcon(new ColorIcon(c.getColor())); }
figureColorButton.setIcon(new ColorIcon(c.getColor()));
colorButton.setIcon(new ColorIcon(ab.getPaint()));
c.setAppearance(ab.getAppearance());
decalModel.refresh();
}
}); });
c.addChangeListener(new StateChangeListener() { c.addChangeListener(new StateChangeListener() {
@ -229,9 +226,7 @@ public class AppearancePanel extends JPanel {
figureColorButton figureColorButton
.addActionListener(new ColorActionListener(c, "Color")); .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); BooleanModel fDefault = new BooleanModel(c.getColor() == null);
{// Style Header Row {// Style Header Row
@ -285,7 +280,6 @@ public class AppearancePanel extends JPanel {
} }
{// Line Style {// Line Style
add(new JLabel(trans.get("RocketCompCfg.lbl.Complinestyle"))); add(new JLabel(trans.get("RocketCompCfg.lbl.Complinestyle")));
LineStyle[] list = new LineStyle[LineStyle.values().length + 1]; LineStyle[] list = new LineStyle[LineStyle.values().length + 1];
@ -304,95 +298,193 @@ public class AppearancePanel extends JPanel {
add(new JSeparator(SwingConstants.HORIZONTAL), "span, wrap, growx"); add(new JSeparator(SwingConstants.HORIZONTAL), "span, wrap, growx");
{// Texture Header Row // Display a tabbed panel for choosing the outside and inside appearance, if the object is of type InsideColorComponent
add(new StyledLabel(trans.get("AppearanceCfg.lbl.Appearance"), 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) {
handler.setEdgesSameAsInside(edges.isSelected());
c.fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
}
});
}
else
appearanceSection(document, c, false, this);
}
/**
*
* @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<DecalImage> textureDropDown = new JComboBox<DecalImage>(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)); Style.BOLD));
final JCheckBox materialDefault = new JCheckBox(mDefault); JCheckBox materialDefault = new JCheckBox(mDefault);
materialDefault.addActionListener(new ActionListener() { materialDefault.addActionListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (materialDefault.isSelected()) { if (materialDefault.isSelected()) {
previousUserSelectedAppearance = (ab == null) ? null if (!insideBuilder) {
: ab.getAppearance(); previousUserSelectedAppearance = (builder == null) ? null
ab.setAppearance(defaultAppearance); : builder.getAppearance();
c.setAppearance(null); }
else {
previousUserSelectedInsideAppearance = (builder == null) ? null
: builder.getAppearance();
}
builder.setAppearance(defaultAppearance);
} else { } else {
ab.setAppearance(previousUserSelectedAppearance); if (!insideBuilder)
builder.setAppearance(previousUserSelectedAppearance);
else
builder.setAppearance(previousUserSelectedInsideAppearance);
} }
} }
}); });
materialDefault.setText(trans.get("AppearanceCfg.lbl.Usedefault")); materialDefault.setText(trans.get("AppearanceCfg.lbl.Usedefault"));
add(materialDefault, "wrap"); 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) {
handler.setInsideSameAsOutside(customInside.isSelected());
c.fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
}
});
panel.add(customInside, "wrap");
} }
{// Texture File // Texture File
add(new JLabel(trans.get("AppearanceCfg.lbl.Texture"))); panel.add(new JLabel(trans.get("AppearanceCfg.lbl.Texture")));
JPanel p = new JPanel(new MigLayout("fill, ins 0", "[grow][]")); JPanel p = new JPanel(new MigLayout("fill, ins 0", "[grow][]"));
mDefault.addEnableComponent(textureDropDown, false); mDefault.addEnableComponent(textureDropDown, false);
p.add(textureDropDown, "grow"); p.add(textureDropDown, "grow");
add(p, "span 3, growx, wrap"); panel.add(p, "span 3, growx, wrap");
final JButton editBtn = new JButton( JButton editBtn = new JButton(
trans.get("AppearanceCfg.but.edit")); trans.get("AppearanceCfg.but.edit"));
editBtn.setEnabled(ab.getImage() != null); editBtn.setEnabled(builder.getImage() != null);
// Enable the editBtn only when the appearance builder has an Image // Enable the editBtn only when the appearance builder has an Image
// assigned to it. // assigned to it.
ab.addChangeListener(new StateChangeListener() { builder.addChangeListener(new StateChangeListener() {
@Override @Override
public void stateChanged(EventObject e) { public void stateChanged(EventObject e) {
editBtn.setEnabled(ab.getImage() != null); editBtn.setEnabled(builder.getImage() != null);
} }
}); });
editBtn.addActionListener(new ActionListener() {
editBtn.addActionListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
try { try {
DecalImage newImage = editDecalHelper.editDecal( DecalImage newImage = editDecalHelper.editDecal(
SwingUtilities SwingUtilities
.getWindowAncestor(AppearancePanel.this), .getWindowAncestor(panel),
document, c, ab.getImage()); document, c, builder.getImage(), insideBuilder);
ab.setImage(newImage); builder.setImage(newImage);
} catch (EditDecalHelperException ex) { } catch (EditDecalHelperException ex) {
JOptionPane.showMessageDialog(AppearancePanel.this, JOptionPane.showMessageDialog(panel,
ex.getMessage(), "", JOptionPane.ERROR_MESSAGE); ex.getMessage(), "", JOptionPane.ERROR_MESSAGE);
} }
} }
}); });
p.add(editBtn); p.add(editBtn);
}
{ // Color // Color
add(new JLabel(trans.get("AppearanceCfg.lbl.color.Color"))); panel.add(new JLabel(trans.get("AppearanceCfg.lbl.color.Color")));
mDefault.addEnableComponent(colorButton, false); mDefault.addEnableComponent(colorButton, false);
add(colorButton); panel.add(colorButton);
}
{ // Scale // Scale
add(new JLabel(trans.get("AppearanceCfg.lbl.texture.scale"))); panel.add(new JLabel(trans.get("AppearanceCfg.lbl.texture.scale")));
add(new JLabel("x:"), "split 4"); panel.add(new JLabel("x:"), "split 4");
JSpinner scaleU = new JSpinner(new DoubleModel(ab, "ScaleX", JSpinner scaleU = new JSpinner(new DoubleModel(builder, "ScaleX",
TEXTURE_UNIT).getSpinnerModel()); TEXTURE_UNIT).getSpinnerModel());
scaleU.setEditor(new SpinnerEditor(scaleU)); scaleU.setEditor(new SpinnerEditor(scaleU));
mDefault.addEnableComponent(scaleU, false); mDefault.addEnableComponent(scaleU, false);
add(scaleU, "w 40"); panel.add(scaleU, "w 40");
add(new JLabel("y:")); panel.add(new JLabel("y:"));
JSpinner scaleV = new JSpinner(new DoubleModel(ab, "ScaleY", JSpinner scaleV = new JSpinner(new DoubleModel(builder, "ScaleY",
TEXTURE_UNIT).getSpinnerModel()); TEXTURE_UNIT).getSpinnerModel());
scaleV.setEditor(new SpinnerEditor(scaleV)); scaleV.setEditor(new SpinnerEditor(scaleV));
mDefault.addEnableComponent(scaleV, false); mDefault.addEnableComponent(scaleV, false);
add(scaleV, "wrap, w 40"); panel.add(scaleV, "wrap, w 40");
}
{// Shine // Shine
add(new JLabel(trans.get("AppearanceCfg.lbl.shine"))); panel.add(new JLabel(trans.get("AppearanceCfg.lbl.shine")));
DoubleModel shineModel = new DoubleModel(ab, "Shine", DoubleModel shineModel = new DoubleModel(builder, "Shine",
UnitGroup.UNITS_RELATIVE); 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()); JSpinner spin = new JSpinner(shineModel.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin)); spin.setEditor(new SpinnerEditor(spin));
JSlider slide = new JSlider(shineModel.getSliderModel(0, 1)); JSlider slide = new JSlider(shineModel.getSliderModel(0, 1));
@ -402,54 +494,49 @@ public class AppearancePanel extends JPanel {
mDefault.addEnableComponent(spin, false); mDefault.addEnableComponent(spin, false);
mDefault.addEnableComponent(unit, false); mDefault.addEnableComponent(unit, false);
add(spin, "split 3, w 50"); panel.add(spin, "split 3, w 50");
add(unit); panel.add(unit);
add(slide, "w 50"); panel.add(slide, "w 50");
}
{ // Offset // Offset
add(new JLabel(trans.get("AppearanceCfg.lbl.texture.offset"))); panel.add(new JLabel(trans.get("AppearanceCfg.lbl.texture.offset")));
add(new JLabel("x:"), "split 4"); panel.add(new JLabel("x:"), "split 4");
JSpinner offsetU = new JSpinner(new DoubleModel(ab, "OffsetU", JSpinner offsetU = new JSpinner(new DoubleModel(builder, "OffsetU",
TEXTURE_UNIT).getSpinnerModel()); TEXTURE_UNIT).getSpinnerModel());
offsetU.setEditor(new SpinnerEditor(offsetU)); offsetU.setEditor(new SpinnerEditor(offsetU));
mDefault.addEnableComponent(offsetU, false); mDefault.addEnableComponent(offsetU, false);
add(offsetU, "w 40"); panel.add(offsetU, "w 40");
add(new JLabel("y:")); panel.add(new JLabel("y:"));
JSpinner offsetV = new JSpinner(new DoubleModel(ab, "OffsetV", JSpinner offsetV = new JSpinner(new DoubleModel(builder, "OffsetV",
TEXTURE_UNIT).getSpinnerModel()); TEXTURE_UNIT).getSpinnerModel());
offsetV.setEditor(new SpinnerEditor(offsetV)); offsetV.setEditor(new SpinnerEditor(offsetV));
mDefault.addEnableComponent(offsetV, false); mDefault.addEnableComponent(offsetV, false);
add(offsetV, "wrap, w 40"); panel.add(offsetV, "wrap, w 40");
}
{ // Repeat // Repeat
add(new JLabel(trans.get("AppearanceCfg.lbl.texture.repeat"))); panel.add(new JLabel(trans.get("AppearanceCfg.lbl.texture.repeat")));
EdgeMode[] list = new EdgeMode[EdgeMode.values().length]; EdgeMode[] list = new EdgeMode[EdgeMode.values().length];
System.arraycopy(EdgeMode.values(), 0, list, 0, System.arraycopy(EdgeMode.values(), 0, list, 0,
EdgeMode.values().length); EdgeMode.values().length);
JComboBox<EdgeMode> combo = new JComboBox<EdgeMode>(new EnumModel<EdgeMode>(ab, JComboBox<EdgeMode> combo = new JComboBox<EdgeMode>(new EnumModel<EdgeMode>(builder,
"EdgeMode", list)); "EdgeMode", list));
mDefault.addEnableComponent(combo, false); mDefault.addEnableComponent(combo, false);
add(combo); panel.add(combo);
}
{ // Rotation // Rotation
add(new JLabel(trans.get("AppearanceCfg.lbl.texture.rotation"))); panel.add(new JLabel(trans.get("AppearanceCfg.lbl.texture.rotation")));
DoubleModel rotationModel = new DoubleModel(ab, "Rotation", DoubleModel rotationModel = new DoubleModel(builder, "Rotation",
UnitGroup.UNITS_ANGLE); UnitGroup.UNITS_ANGLE);
JSpinner rotation = new JSpinner(rotationModel.getSpinnerModel()); JSpinner rotation = new JSpinner(rotationModel.getSpinnerModel());
rotation.setEditor(new SpinnerEditor(rotation)); rotation.setEditor(new SpinnerEditor(rotation));
mDefault.addEnableComponent(rotation, false); mDefault.addEnableComponent(rotation, false);
add(rotation, "split 3, w 50"); panel.add(rotation, "split 3, w 50");
add(new UnitSelector(rotationModel)); panel.add(new UnitSelector(rotationModel));
BasicSlider bs = new BasicSlider(rotationModel.getSliderModel( BasicSlider bs = new BasicSlider(rotationModel.getSliderModel(
-Math.PI, Math.PI)); -Math.PI, Math.PI));
mDefault.addEnableComponent(bs, false); mDefault.addEnableComponent(bs, false);
add(bs, "w 50, wrap"); panel.add(bs, "w 50, wrap");
}
} }
} }

View File

@ -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;
import net.sf.openrocket.gui.figure3d.geometry.Geometry.Surface; import net.sf.openrocket.gui.figure3d.geometry.Geometry.Surface;
import net.sf.openrocket.motor.Motor; import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.rocketcomponent.InsideColorComponent;
import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.util.Color; import net.sf.openrocket.util.Color;
@ -90,11 +91,25 @@ public class RealisticRenderer extends RocketRenderer {
@Override @Override
public void renderComponent(final GL2 gl, Geometry geom, final float alpha) { public void renderComponent(final GL2 gl, Geometry geom, final float alpha) {
Appearance app = getAppearance( geom.getComponent() ); RocketComponent c = geom.getComponent();
render(gl, geom, Surface.INSIDE, app, true, alpha); Appearance app = getAppearance(c);
render(gl, geom, Surface.OUTSIDE, app, true, alpha); 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); 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);
}
protected float[] convertColor(Appearance a, float alpha) { protected float[] convertColor(Appearance a, float alpha) {
float[] color = new float[4]; float[] color = new float[4];
@ -191,6 +206,19 @@ public class RealisticRenderer extends RocketRenderer {
return ret; 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) { private int toEdgeMode(Decal.EdgeMode m) {
switch (m) { switch (m) {
case REPEAT: case REPEAT:

View File

@ -16,6 +16,8 @@ import net.sf.openrocket.gui.watcher.FileWatcher;
import net.sf.openrocket.gui.watcher.WatchEvent; import net.sf.openrocket.gui.watcher.WatchEvent;
import net.sf.openrocket.gui.watcher.WatchService; import net.sf.openrocket.gui.watcher.WatchService;
import net.sf.openrocket.l10n.Translator; 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 net.sf.openrocket.rocketcomponent.RocketComponent;
import com.google.inject.Inject; import com.google.inject.Inject;
@ -67,10 +69,12 @@ public class EditDecalHelper {
* @param doc * @param doc
* @param component * @param component
* @param decal * @param decal
* @param insideApp flag to check whether it is the inside appearance that is edited
* @return * @return
* @throws EditDecalHelperException * @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(); boolean sysPrefSet = prefs.isDecalEditorPreferenceSet();
int usageCount = doc.countDecalUsage(decal); int usageCount = doc.countDecalUsage(decal);
@ -111,7 +115,10 @@ public class EditDecalHelper {
} }
if (dialog.isEditOne()) { if (dialog.isEditOne()) {
if (insideApp)
decal = makeDecalUnique(doc, component, decal); decal = makeDecalUnique(doc, component, decal);
else
decal = makeDecalUniqueInside(doc, component, decal);
} }
launchEditor(useSystemEditor, commandLine, decal); launchEditor(useSystemEditor, commandLine, decal);
@ -132,6 +139,21 @@ public class EditDecalHelper {
return newImage; 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 { private void launchEditor(boolean useSystemEditor, String commandTemplate, final DecalImage decal) throws EditDecalHelperException {
String decalId = decal.getName(); String decalId = decal.getName();