X-Git-Url: https://mograsim.net/gitweb/?a=blobdiff_plain;f=net.mograsim.logic.model%2Fsrc%2Fnet%2Fmograsim%2Flogic%2Fmodel%2Fmodel%2Fcomponents%2Fsubmodels%2FSubmodelComponent.java;h=9388e2fb09fdc7e51ba8370f1b2c76e5700397f3;hb=8bed58cd47f4e53a0a83e066d38864aa6875502f;hp=b19f05ded9de7fc7065e65638f2f66bbdc43ffdc;hpb=47ea68ed5c444dd14864412639f6a6fd60ab8a0f;p=Mograsim.git diff --git a/net.mograsim.logic.model/src/net/mograsim/logic/model/model/components/submodels/SubmodelComponent.java b/net.mograsim.logic.model/src/net/mograsim/logic/model/model/components/submodels/SubmodelComponent.java index b19f05de..9388e2fb 100644 --- a/net.mograsim.logic.model/src/net/mograsim/logic/model/model/components/submodels/SubmodelComponent.java +++ b/net.mograsim.logic.model/src/net/mograsim/logic/model/model/components/submodels/SubmodelComponent.java @@ -2,47 +2,44 @@ package net.mograsim.logic.model.model.components.submodels; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Set; -import java.util.function.Function; +import java.util.function.Consumer; import net.haspamelodica.swt.helper.gcs.GCConfig; import net.haspamelodica.swt.helper.gcs.GeneralGC; import net.haspamelodica.swt.helper.gcs.TranslatedGC; -import net.haspamelodica.swt.helper.swtobjectwrappers.Point; import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle; import net.mograsim.logic.model.LogicUIRenderer; -import net.mograsim.logic.model.model.ViewModel; -import net.mograsim.logic.model.model.ViewModelModifiable; -import net.mograsim.logic.model.model.components.GUIComponent; -import net.mograsim.logic.model.model.wires.GUIWire; +import net.mograsim.logic.model.model.LogicModel; +import net.mograsim.logic.model.model.LogicModelModifiable; +import net.mograsim.logic.model.model.components.ModelComponent; import net.mograsim.logic.model.model.wires.MovablePin; import net.mograsim.logic.model.model.wires.Pin; +import net.mograsim.logic.model.model.wires.PinUsage; +import net.mograsim.logic.model.serializing.IdentifyParams; +import net.mograsim.logic.model.serializing.IndirectModelComponentCreator; import net.mograsim.logic.model.serializing.SubmodelComponentParams; -import net.mograsim.logic.model.serializing.SubmodelComponentParams.InterfacePinParams; -import net.mograsim.logic.model.serializing.SubmodelComponentParams.SubmodelParameters; -import net.mograsim.logic.model.serializing.SubmodelComponentParams.SubmodelParameters.InnerComponentParams; -import net.mograsim.logic.model.serializing.SubmodelComponentParams.SubmodelParameters.InnerWireParams; -import net.mograsim.logic.model.serializing.SubmodelComponentParams.SubmodelParameters.InnerWireParams.InnerPinParams; +import net.mograsim.logic.model.serializing.SubmodelComponentSerializer; +import net.mograsim.logic.model.snippets.Renderer; +import net.mograsim.logic.model.util.JsonHandler; +import net.mograsim.preferences.Preferences; /** - * A {@link GUIComponent} consisting of another model. A SubmodelComponent can have so-called "interface pins" connecting the + * A {@link ModelComponent} consisting of another model. A SubmodelComponent can have so-called "interface pins" connecting the * inner and outer models. */ -public abstract class SubmodelComponent extends GUIComponent +public abstract class SubmodelComponent extends ModelComponent { - private static final String SUBMODEL_INTERFACE_NAME = "_submodelinterface"; + public static final String SUBMODEL_INTERFACE_NAME = "_submodelinterface"; /** * A modifiable view of {@link #submodel}. */ - protected final ViewModelModifiable submodelModifiable; + protected final LogicModelModifiable submodelModifiable; /** * The model this {@link SubmodelComponent} consists of. */ - public final ViewModel submodel; + public final LogicModel submodel; /** * The list of all submodel interface pins of this {@link SubmodelComponent} on the submodel side. */ @@ -72,15 +69,6 @@ public abstract class SubmodelComponent extends GUIComponent */ private final SubmodelInterface submodelInterface; - /** - * The list of all high level state IDs this component supports without delegating to subcomponents. - */ - private final Set highLevelAtomicStates; - /** - * A map of high level state subcomponent IDs to the {@link GUIComponent} high level state access requests are delegated to. - */ - private final Map subcomponentsByHighLevelStateSubcomponentID; - /** * The factor by which the submodel is scaled when rendering. */ @@ -100,12 +88,26 @@ public abstract class SubmodelComponent extends GUIComponent */ private final LogicUIRenderer renderer; + /** + * The {@link Renderer} used to render the symbol of this SubmodelCoponent. + */ + private Renderer symbolRenderer; + /** + * The {@link Renderer} used to render the outline of this SubmodelCoponent. + */ + private Renderer outlineRenderer; + // creation and destruction - public SubmodelComponent(ViewModelModifiable model, String name) + public SubmodelComponent(LogicModelModifiable model, String name) + { + this(model, name, true); + } + + protected SubmodelComponent(LogicModelModifiable model, String name, boolean callInit) { - super(model, name); - this.submodelModifiable = new ViewModelModifiable(); + super(model, name, false); + this.submodelModifiable = new LogicModelModifiable(); this.submodel = submodelModifiable; this.submodelPins = new HashMap<>(); this.submodelMovablePinsUnmodifiable = Collections.unmodifiableMap(submodelPins); @@ -113,17 +115,24 @@ public abstract class SubmodelComponent extends GUIComponent this.supermodelPins = new HashMap<>(); this.supermodelMovablePinsUnmodifiable = Collections.unmodifiableMap(supermodelPins); this.supermodelUnmovablePinsUnmodifiable = Collections.unmodifiableMap(supermodelPins); - this.submodelInterface = new SubmodelInterface(submodelModifiable, SUBMODEL_INTERFACE_NAME); - - this.highLevelAtomicStates = new HashSet<>(); - this.subcomponentsByHighLevelStateSubcomponentID = new HashMap<>(); + this.submodelInterface = new SubmodelInterface(submodelModifiable); this.submodelScale = 1; - this.maxVisibleRegionFillRatioForAlpha0 = 0.4; - this.minVisibleRegionFillRatioForAlpha1 = 0.8; + this.maxVisibleRegionFillRatioForAlpha0 = 0.8; + this.minVisibleRegionFillRatioForAlpha1 = 0.9; this.renderer = new LogicUIRenderer(submodelModifiable); - submodelModifiable.addRedrawListener(this::requestRedraw); + Consumer redrawHandlerChangedListener = submodelModifiable::setRedrawHandler; + model.addRedrawHandlerChangedListener(redrawHandlerChangedListener); + model.addComponentRemovedListener(c -> + { + if (c == this) + model.removeRedrawHandlerChangedListener(redrawHandlerChangedListener); + }); + submodelModifiable.setRedrawHandler(model.getRedrawHandler()); + + if (callInit) + init(); } // pins @@ -142,8 +151,24 @@ public abstract class SubmodelComponent extends GUIComponent super.addPin(supermodelPin);// do this first to be fail-fast if the supermodel does not belong to this component String name = supermodelPin.name; - MovablePin submodelPin = new MovablePin(submodelInterface, name, supermodelPin.logicWidth, supermodelPin.getRelX() / submodelScale, - supermodelPin.getRelY() / submodelScale); + // TODO if we upgrade to Java 12, replace with switch-expression + PinUsage submodelPinUsage; + switch (supermodelPin.usage) + { + case INPUT: + submodelPinUsage = PinUsage.OUTPUT; + break; + case OUTPUT: + submodelPinUsage = PinUsage.INPUT; + break; + case TRISTATE: + submodelPinUsage = PinUsage.TRISTATE; + break; + default: + throw new IllegalArgumentException("Unknown enum constant: " + supermodelPin.usage); + } + MovablePin submodelPin = new MovablePin(submodelModifiable, submodelInterface, name, supermodelPin.logicWidth, submodelPinUsage, + supermodelPin.getRelX() / submodelScale, supermodelPin.getRelY() / submodelScale); submodelPin.addPinMovedListener(p -> { @@ -264,186 +289,92 @@ public abstract class SubmodelComponent extends GUIComponent return supermodelPins.get(name); } - // high-level access - - /** - * Adds the given subcomponent ID to the set of allowed subcomponent IDs and links the given {@link GUIComponent} as the delegate target - * for this subcomponent ID.
- * Note that this method does not affect whether {@link #setSubcomponentHighLevelState(String, String, Object) - * set}/{@link #getSubcomponentHighLevelState(String, String)} will be called.
- * See {@link GUIComponent#setHighLevelState(String, Object)} for details about subcomponent IDs. - * - * @author Daniel Kirschten - */ - protected void addHighLevelStateSubcomponentID(String subcomponentID, GUIComponent subcomponent) - { - checkHighLevelStateIDPart(subcomponentID); - subcomponentsByHighLevelStateSubcomponentID.put(subcomponentID, subcomponent); - } - - /** - * Removes the given subcomponent ID from the set of allowed subcomponent IDs.
- * Note that this method does not affect whether {@link #setSubcomponentHighLevelState(String, String, Object) - * set}/{@link #getSubcomponentHighLevelState(String, String)} will be called.
- * See {@link GUIComponent#setHighLevelState(String, Object)} for details about subcomponent IDs. - * - * @author Daniel Kirschten - */ - protected void removeHighLevelStateSubcomponentID(String subcomponentID) - { - subcomponentsByHighLevelStateSubcomponentID.remove(subcomponentID); - } + // "graphical" operations /** - * Adds the given atomic state ID to the set of allowed atomic state IDs.
- * See {@link GUIComponent#setHighLevelState(String, Object)} for details about atomic state IDs. + * Sets the factor by which the submodel is scaled when rendering and calls redrawListeners. Note that the submodel interface pins will + * stay at their position relative to the supermodel, which means they will move relative to the submodel. * * @author Daniel Kirschten */ - protected void addAtomicHighLevelStateID(String stateID) + protected void setSubmodelScale(double submodelScale) { - checkHighLevelStateIDPart(stateID); - highLevelAtomicStates.add(stateID); - } + this.submodelScale = submodelScale; - /** - * Removes the given atomic state ID from the set of allowed atomic state IDs.
- * See {@link GUIComponent#setHighLevelState(String, Object)} for details about atomic state IDs. - * - * @author Daniel Kirschten - */ - protected void removeAtomicHighLevelStateID(String stateID) - { - highLevelAtomicStates.remove(stateID); - } + for (Entry e : supermodelPins.entrySet()) + getSubmodelMovablePin(e.getKey()).setRelPos(e.getValue().getRelX() * submodelScale, e.getValue().getRelY() * submodelScale); - @Override - public final void setHighLevelState(String stateID, Object newState) - { - int indexOfDot = stateID.indexOf('.'); - if (indexOfDot == -1) - if (highLevelAtomicStates.contains(stateID)) - setAtomicHighLevelState(stateID, newState); - else - super.setHighLevelState(stateID, newState); - else - setSubcomponentHighLevelState(stateID.substring(0, indexOfDot), stateID.substring(indexOfDot + 1), newState); + model.requestRedraw();// needed if there is no submodel interface pin } /** - * This method is called in {@link #setHighLevelState(String, Object)} when the state ID is not atomic. The default implementation uses - * the information given to {@link #addHighLevelStateSubcomponentID(String, GUIComponent) - * add}/{@link #removeHighLevelStateSubcomponentID(String)} to decide which subcomponent to delegate to.
- * Note that {@link #addHighLevelStateSubcomponentID(String, GUIComponent) add}/{@link #removeHighLevelStateSubcomponentID(String)} - * don't affect whether this method will be called. + * Returns the current factor by which the submodel is scaled when rendering. * * @author Daniel Kirschten */ - protected void setSubcomponentHighLevelState(String subcomponentID, String subcomponentHighLevelStateID, Object newState) + public double getSubmodelScale() { - GUIComponent subcomponent = subcomponentsByHighLevelStateSubcomponentID.get(subcomponentID); - if (subcomponent != null) - subcomponent.setHighLevelState(subcomponentHighLevelStateID, newState); - else - super.setHighLevelState(subcomponentID + "." + subcomponentHighLevelStateID, newState); + return submodelScale; } /** - * This method is called in {@link #setHighLevelState(String, Object)} when the state ID is atomic and in the set of allowed atomic - * state IDs.
- * See {@link GUIComponent#setHighLevelState(String, Object)} for details about atomic state IDs. + * @see #renderSymbol(GeneralGC, Rectangle) * * @author Daniel Kirschten */ - @SuppressWarnings({ "static-method", "unused" }) // this method is intended to be overridden - protected void setAtomicHighLevelState(String stateID, Object newState) + protected void setSymbolRenderer(Renderer symbolRenderer) { - throw new IllegalStateException("Unknown high level state ID: " + stateID); - } - - @Override - public final Object getHighLevelState(String stateID) - { - int indexOfDot = stateID.indexOf('.'); - if (indexOfDot == -1) - { - if (highLevelAtomicStates.contains(stateID)) - return getAtomicHighLevelState(stateID); - return super.getHighLevelState(stateID); - } - return getSubcomponentHighLevelState(stateID.substring(0, indexOfDot), stateID.substring(indexOfDot + 1)); + this.symbolRenderer = symbolRenderer; + model.requestRedraw(); } /** - * This method is called in {@link #getHighLevelState(String, Object)} when the state ID is not atomic. The default implementation uses - * the information given to {@link #addHighLevelStateSubcomponentID(String, GUIComponent) - * add}/{@link #removeHighLevelStateSubcomponentID(String)} to decide which subcomponent to delegate to.
- * Note that {@link #addHighLevelStateSubcomponentID(String, GUIComponent) add}/{@link #removeHighLevelStateSubcomponentID(String)} - * don't affect whether this method will be called. + * @see #renderSymbol(GeneralGC, Rectangle) * * @author Daniel Kirschten */ - protected Object getSubcomponentHighLevelState(String subcomponentID, String subcomponentHighLevelStateID) + public Renderer getSymbolRenderer() { - GUIComponent subcomponent = subcomponentsByHighLevelStateSubcomponentID.get(subcomponentID); - if (subcomponent != null) - return subcomponent.getHighLevelState(subcomponentHighLevelStateID); - return super.getHighLevelState(subcomponentID + "." + subcomponentHighLevelStateID); + return symbolRenderer; } /** - * This method is called in {@link SubmodelComponent#getHighLevelState(String)} when the state ID is in the set of allowed atomic state - * IDs.
- * See {@link GUIComponent#setHighLevelState(String, Object)} for details about atomic state IDs. + * @see #renderOutline(GeneralGC, Rectangle) * * @author Daniel Kirschten */ - @SuppressWarnings("static-method") // this method is intended to be overridden - protected Object getAtomicHighLevelState(String stateID) + protected void setOutlineRenderer(Renderer outlineRenderer) { - throw new IllegalStateException("Unknown high level state ID: " + stateID); + this.outlineRenderer = outlineRenderer; + model.requestRedraw(); } - private static void checkHighLevelStateIDPart(String stateIDPart) - { - if (stateIDPart.indexOf('.') != -1) - throw new IllegalArgumentException("Illegal high level state ID part (contains dot): " + stateIDPart); - - } - - // "graphical" operations - /** - * Sets the factor by which the submodel is scaled when rendering and calls redrawListeners. Note that the submodel interface pins will - * stay at their position relative to the supermodel, which means they will move relative to the submodel. + * @see #renderOutline(GeneralGC, Rectangle) * * @author Daniel Kirschten */ - protected void setSubmodelScale(double submodelScale) + public Renderer getOutlineRenderer() { - this.submodelScale = submodelScale; - - for (Entry e : supermodelPins.entrySet()) - getSubmodelMovablePin(e.getKey()).setRelPos(e.getValue().getRelX() * submodelScale, e.getValue().getRelY() * submodelScale); - - requestRedraw();// needed if there is no submodel interface pin + return outlineRenderer; } - /** - * Returns the current factor by which the submodel is scaled when rendering. - * - * @author Daniel Kirschten - */ - protected double getSubmodelScale() + @Override + public boolean clicked(double x, double y) { - return submodelScale; + double scaledX = (x - getPosX()) / submodelScale; + double scaledY = (y - getPosY()) / submodelScale; + for (ModelComponent component : submodel.getComponentsByName().values()) + if (component.getBounds().contains(scaledX, scaledY) && component.clicked(scaledX, scaledY)) + return true; + return false; } @Override public void render(GeneralGC gc, Rectangle visibleRegion) { GCConfig conf = new GCConfig(gc); - TranslatedGC tgc = new TranslatedGC(gc, getPosX(), getPosY(), submodelScale, true); + GeneralGC tgc = new TranslatedGC(gc, getPosX(), getPosY(), submodelScale, true); conf.reset(tgc); double visibleRegionFillRatio = Math.max(getWidth() / visibleRegion.width, getHeight() / visibleRegion.height); double alphaFactor = map(visibleRegionFillRatio, maxVisibleRegionFillRatioForAlpha0, minVisibleRegionFillRatioForAlpha1, 0, 1); @@ -463,117 +394,65 @@ public abstract class SubmodelComponent extends GUIComponent renderSymbol(gc, visibleRegion); } conf.reset(gc); + // reset line width explicitly to avoid rounding errors causing weird glitches + gc.setLineWidth(Preferences.current().getDouble("net.mograsim.logic.model.linewidth.default")); // draw the outline after all other operations to make interface pins look better renderOutline(gc, visibleRegion); } - // TODO make this a path /** - * Render the outline of this {@link SubmodelComponent}, e.g. the graphical elements that should stay visible if the submodel is drawn. + * Render the symbol of this {@link SubmodelComponent}, e.g. the things that should be hidden if the submodel is drawn. * * @author Daniel Kirschten */ - protected abstract void renderOutline(GeneralGC gc, Rectangle visibleRegion); + protected void renderSymbol(GeneralGC gc, Rectangle visibleRegion) + { + if (symbolRenderer != null) + symbolRenderer.render(gc, visibleRegion); + } /** - * Render the symbol of this {@link SubmodelComponent}, e.g. the things that should be hidden if the submodel is drawn. + * Render the outline of this {@link SubmodelComponent}, e.g. the graphical elements that should stay visible if the submodel is drawn. * * @author Daniel Kirschten */ - protected abstract void renderSymbol(GeneralGC gc, Rectangle visibleRegion); - - private static double map(double val, double valMin, double valMax, double mapMin, double mapMax) + protected void renderOutline(GeneralGC gc, Rectangle visibleRegion) { - return mapMin + (val - valMin) * (mapMax - mapMin) / (valMax - valMin); + if (outlineRenderer != null) + outlineRenderer.render(gc, visibleRegion); } - @Override - public boolean clicked(double x, double y) + private static double map(double val, double valMin, double valMax, double mapMin, double mapMax) { - double scaledX = (x - getPosX()) / submodelScale; - double scaledY = (y - getPosY()) / submodelScale; - for (GUIComponent component : submodel.getComponentsByName().values()) - if (component.getBounds().contains(scaledX, scaledY) && component.clicked(scaledX, scaledY)) - return true; - return false; + return mapMin + (val - valMin) * (mapMax - mapMin) / (valMax - valMin); } // serializing - // TODO move the methods below to serializing classes - - public SubmodelComponentParams calculateParams() - { - return calculateParams(c -> "class:" + c.getClass().getCanonicalName()); - } - /** - * @return {@link SubmodelComponentParams}, which describe this {@link SubmodelComponent}. + * {@link SubmodelComponent}'s implementation of {@link ModelComponent#getIDForSerializing(IdentifyParams)} returns "submodel". It is + * recommended to override this behaviour. + * + * @see ModelComponent#getIDForSerializing(IdentifyParams) + * @see ModelComponent#getParamsForSerializing(IdentifyParams) */ - public SubmodelComponentParams calculateParams(Function getIdentifier) + @Override + public String getIDForSerializing(IdentifyParams idParams) { - SubmodelComponentParams params = new SubmodelComponentParams(); - params.submodel = calculateSubmodelParams(getIdentifier); - - params.width = getWidth(); - params.height = getHeight(); - - InterfacePinParams[] iPins = new InterfacePinParams[getPins().size()]; - int i = 0; - for (Pin p : getPins().values()) - { - InterfacePinParams iPinParams = new InterfacePinParams(); - iPins[i] = iPinParams; - iPinParams.location = p.getRelPos(); - iPinParams.name = p.name; - iPinParams.logicWidth = p.logicWidth; - i++; - } - params.interfacePins = iPins; - return params; + return "submodel";// TODO what ID? } - private SubmodelParameters calculateSubmodelParams(Function getIdentifier) + /** + * {@link SubmodelComponent}'s implementation of {@link ModelComponent#getParamsForSerializing(IdentifyParams)} returns an instance of + * {@link SubmodelComponentParams}. It is recommended to override this behaviour. + * + * @see ModelComponent#getIDForSerializing(IdentifyParams) + * @see ModelComponent#getParamsForSerializing(IdentifyParams) + */ + @Override + public Object getParamsForSerializing(IdentifyParams idParams) { - SubmodelParameters params = new SubmodelParameters(); - params.innerScale = getSubmodelScale(); - - Map components = new HashMap<>(submodel.getComponentsByName()); - components.remove(SUBMODEL_INTERFACE_NAME); - InnerComponentParams[] comps = new InnerComponentParams[components.size()]; - int i = 0; - for (GUIComponent component : components.values()) - { - InnerComponentParams inner = new InnerComponentParams(); - comps[i] = inner; - inner.pos = new Point(component.getPosX(), component.getPosY()); - inner.id = getIdentifier.apply(component); - inner.params = component.getParams(); - inner.name = component.name; - i++; - } - params.subComps = comps; - - List wireList = submodel.getWires(); - InnerWireParams wires[] = new InnerWireParams[wireList.size()]; - i = 0; - for (GUIWire wire : wireList) - { - InnerWireParams inner = new InnerWireParams(); - wires[i] = inner; - InnerPinParams pin1Params = new InnerPinParams(), pin2Params = new InnerPinParams(); - - pin1Params.pinName = wire.getPin1().name; - pin1Params.compName = wire.getPin1().component.name; - pin2Params.pinName = wire.getPin2().name; - pin2Params.compName = wire.getPin2().component.name; - inner.pin1 = pin1Params; - inner.pin2 = pin2Params; - inner.path = wire.getPath(); - i++; - } - params.innerWires = wires; - return params; + return SubmodelComponentSerializer.serialize(this, idParams); } // operations no longer supported @@ -589,4 +468,10 @@ public abstract class SubmodelComponent extends GUIComponent { throw new UnsupportedOperationException("Can't remove pins of a SubmodelComponent directly, call removeSubmodelInterface instead"); } + + static + { + IndirectModelComponentCreator.setComponentSupplier(SubmodelComponent.class.getCanonicalName(), + (m, p, n) -> SubmodelComponentSerializer.deserialize(m, JsonHandler.fromJsonTree(p, SubmodelComponentParams.class), n)); + } } \ No newline at end of file