X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.g2d%2Fsrc%2Forg%2Fsimantics%2Fg2d%2Felementclass%2Foperationsymbols%2FOperationSymbols.java;fp=bundles%2Forg.simantics.g2d%2Fsrc%2Forg%2Fsimantics%2Fg2d%2Felementclass%2Foperationsymbols%2FOperationSymbols.java;h=db8d6350e454f50d35f459918aa6c0f36820cd88;hb=0ae2b770234dfc3cbb18bd38f324125cf0faca07;hp=b057ef9f51ee06d23c2a6ad8e4731fbd04ba6eda;hpb=24e2b34260f219f0d1644ca7a138894980e25b14;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/elementclass/operationsymbols/OperationSymbols.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/elementclass/operationsymbols/OperationSymbols.java index b057ef9f5..db8d6350e 100644 --- a/bundles/org.simantics.g2d/src/org/simantics/g2d/elementclass/operationsymbols/OperationSymbols.java +++ b/bundles/org.simantics.g2d/src/org/simantics/g2d/elementclass/operationsymbols/OperationSymbols.java @@ -1,674 +1,674 @@ -/******************************************************************************* - * Copyright (c) 2007, 2010 Association for Decentralized Information Management - * in Industry THTH ry. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * VTT Technical Research Centre of Finland - initial API and implementation - *******************************************************************************/ -package org.simantics.g2d.elementclass.operationsymbols; - -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Font; -import java.awt.Graphics2D; -import java.awt.Polygon; -import java.awt.Shape; -import java.awt.Stroke; -import java.awt.geom.Ellipse2D; -import java.awt.geom.Path2D; -import java.awt.geom.Rectangle2D; -import java.util.EnumSet; - -import org.simantics.g2d.image.Image; -import org.simantics.g2d.image.impl.Shadow; -import org.simantics.g2d.image.impl.Shadow.ShadowParameters; -import org.simantics.g2d.utils.GeometryUtils; -import org.simantics.g2d.utils.PathUtils; -import org.simantics.scenegraph.Node; -import org.simantics.scenegraph.g2d.G2DParentNode; -import org.simantics.utils.ObjectUtils; - -/** - * @author Toni Kalajainen - */ -public class OperationSymbols { - - static final double SHADOW_SIZE = 100; - static final Color BORDER_COLOR = Color.GRAY; - static final Color FILL_COLOR = Color.GRAY; - static final Color SECONDARY_COLOR = Color.GREEN; - static final ShadowParameters SHADOW = new ShadowParameters(0.5, Color.BLACK, 2); - - static final Color HOT = new Color(50, 50, 200); - static final Color COLD = new Color(190, 190, 255); - - public static final BoilerHeatExchanger[] BOILER_HES = new BoilerHeatExchanger[] { - new BoilerHeatExchanger(null, HOT, HOT, null, HOT), - new BoilerHeatExchanger(HOT, null, COLD, COLD, HOT), - new BoilerHeatExchanger(HOT, null, COLD, null, HOT), - new BoilerHeatExchanger(HOT, null, null, HOT, HOT) - }; - - public static final Image BOILER_SYMBOL = new BoilerSymbol2(FILL_COLOR, FILL_COLOR, BOILER_HES); - public static final Image BOILER_SHADOW = new Shadow(BOILER_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); - - public static final Image COMPRESSOR_SYMBOL = new CompressorSymbol(FILL_COLOR, BORDER_COLOR); - public static final Image COMPRESSOR_SHADOW = new Shadow(COMPRESSOR_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); - - public static final Image NO_SPIN_SYMBOL = new NoSpinSymbol(FILL_COLOR, BORDER_COLOR); - public static final Image NO_SPIN_SHADOW = new Shadow(NO_SPIN_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); - - public static final Image CONTROLVALVE_SYMBOL = new ControlValveSymbol(BORDER_COLOR, FILL_COLOR); - public static final Image CONTROLVALVE_SHADOW = new Shadow(CONTROLVALVE_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); - - public static final Image THREEWAYVALVE_SYMBOL = new ThreeWayValveSymbol(BORDER_COLOR, FILL_COLOR); - public static final Image THREEWAYVALVE_SHADOW = new Shadow(THREEWAYVALVE_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); - - public static final Image HEATEXCHANGER_SYMBOL = new HeatExchangerSymbol(BORDER_COLOR, FILL_COLOR); - public static final Image HEATEXCHANGER_SHADOW = new Shadow(HEATEXCHANGER_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); - - public static final Image PUMP_SYMBOL = new PumpSymbol(BORDER_COLOR, FILL_COLOR); - public static final Image PUMP_SHADOW = new Shadow(PUMP_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); - - public static final Image SHUTOFFVALVE_SYMBOL = new ShutoffValveSymbol(BORDER_COLOR, FILL_COLOR); - public static final Image SHUTOFFVALVE_SHADOW = new Shadow(SHUTOFFVALVE_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); - - public static final Image SWITCH_SYMBOL = new SwitchSymbol(BORDER_COLOR); - public static final Image SWITCH_SHADOW = new Shadow(SWITCH_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); - - public static final Image TURBINE_SYMBOL = new TurbineSymbol(BORDER_COLOR, FILL_COLOR); - public static final Image TURBINE_SHADOW = new Shadow(TURBINE_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); - - public static final Image VOLTAGETRANSFORMER_SYMBOL = new VoltageTransformerSymbol(SECONDARY_COLOR, BORDER_COLOR); - public static final Image VOLTAGETRANSFORMER_SHADOW = new Shadow(VOLTAGETRANSFORMER_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); - - public static final Image CHIMNEY_SYMBOL = new ChimneySymbol(SECONDARY_COLOR, BORDER_COLOR); - public static final Image CHIMNEY_SHADOW = new Shadow(CHIMNEY_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); - - public static final Image GENERATOR_SYMBOL = new GeneratorSymbol(SECONDARY_COLOR, BORDER_COLOR); - public static final Image GENERATOR_SHADOW = new Shadow(GENERATOR_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); - - public static final Image TANK_SYMBOL = new TankSymbol(new Rectangle2D.Double(0,0, 20.0, 8.0), 2.0, SECONDARY_COLOR, BORDER_COLOR); - public static final Image TANK_SHADOW = new Shadow(TANK_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); - - public static final Image TANK2_SYMBOL = new Tank2Symbol(new Rectangle2D.Double(0,0, 20.0, 8.0), 2.0, SECONDARY_COLOR, BORDER_COLOR, HOT); - public static final Image TANK2_SHADOW = new Shadow(TANK2_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); - - public static class BoilerSymbol extends PS implements Image { - public static final int DIM = 10; - public static final Polygon SHAPE = - new Polygon(new int[] {-DIM, -DIM, 0, DIM, DIM, 0}, new int[] {2*DIM, -2*DIM, -3*DIM, -2*DIM, 2*DIM, 3*DIM}, 6); - public static final Rectangle2D BOUNDS = SHAPE.getBounds2D(); - - public BoilerSymbol(Color bc, Color fc) { - super(SHAPE.getBounds2D(), bc, fc, null, null); - } - @Override - public void paint(Graphics2D g) { -// if (fc!=null) { -// g.setColor(fc); -// g.fill(SHAPE); -// } -// if (bc!=null) { -// g.setColor(bc); -// g.draw(SHAPE); -// } - } - } - - public static class BoilerHeatExchanger { - public final Color color; - public final Color topLeft, topRight, bottomLeft, bottomRight; - public BoilerHeatExchanger(Color topLeft, Color topRight, - Color bottomLeft, Color bottomRight, - Color color) { - this.bottomLeft = bottomLeft; - this.bottomRight = bottomRight; - this.color = color; - this.topLeft = topLeft; - this.topRight = topRight; - } - } - - public static class BoilerSymbol2 extends PS implements Image { - - public static Path2D HE = PathUtils.path(10,0, 0,0, 0,5, 5,10, -5,10, 0,15, 0,20); - - public static Rectangle2D getBoilerRect(BoilerHeatExchanger ... hes) - { - return new Rectangle2D.Double(-10, 0, 20, hes.length*25+25); - } - - public static Shape getBoilerShape(BoilerHeatExchanger ... hes) - { - Path2D p = new Path2D.Double(); - p.moveTo(-10, 10); - p.lineTo( 0, 0); - p.lineTo( 10, 10); - double h = hes.length * 25; - p.lineTo( 10, h+15); - p.lineTo( 0, h+25); - p.lineTo(-10, h+15); - p.closePath(); - return p; - } - - private final Shape shape; - private final BoilerHeatExchanger [] hes; - - public BoilerSymbol2(Color fc, Color bc, BoilerHeatExchanger ... hes) { - super( getBoilerRect(hes), bc, fc, null, null); - shape = getBoilerShape(hes); - this.hes = hes; - } - - @Override - public void paint(Graphics2D g) { - if (fc!=null) { - g.setColor(fc); - g.fill(shape); - } - if (bc!=null) { - g.setColor(bc); - g.draw(shape); - } - // paint heat exchangers - g.translate(0, 15); - for (BoilerHeatExchanger he : hes) - { - g.setColor(he.color); - g.draw(HE); - if (he.topLeft!=null) { - g.setColor(he.topLeft); - g.drawLine(-10, 0, 0, 0); - } - if (he.topRight!=null) { - g.setColor(he.topRight); - g.drawLine(0, 0, 10, 0); - } - if (he.bottomLeft!=null) { - g.setColor(he.bottomLeft); - g.drawLine(-10, 20, 0, 20); - } - if (he.bottomRight!=null) { - g.setColor(he.bottomRight); - g.drawLine(0, 20, 10, 20); - } - g.translate(0, 25); - } - } - } - - public static class ChimneySymbol extends PS implements Image { - - public static final int DIM = 10; - public static final int D2 = 8; - public static final Shape C1 = new Ellipse2D.Double(-DIM, -DIM, DIM*2, DIM*2); - public static final Shape C2 = new Ellipse2D.Double(-D2, -D2, D2*2, D2*2); - - public static final Rectangle2D BOUNDS = new Rectangle2D.Double(-10, -10, 20, 40); - - public ChimneySymbol(Color fc, Color bc) { - super( BOUNDS, bc, fc, null, null); - assert(bc!=null); - } - - @Override - public void paint(Graphics2D g) { - g.setColor(fc); - g.fill(C2); - g.fillRect(-5, 14, 10, 8); - g.setColor(bc); - g.draw(C1); - g.draw(C2); - - g.drawLine(0, DIM, 0, 15); - g.drawRect(-5, 14, 10, 8); - g.drawLine(-5, 14, 5, 22); - } - } - - - public static class GeneratorSymbol extends PS implements Image { - - public static final int DIM = 10; - public static final Shape C1 = new Ellipse2D.Double(-DIM, -DIM, DIM*2, DIM*2); - - public static final Rectangle2D BOUNDS = C1.getBounds2D(); - private final static Font FONT = Font.decode("Serif-12"); - - public GeneratorSymbol(Color fc, Color bc) { - super( BOUNDS, bc, fc, null, null); - assert(bc!=null && fc!=null); - } - - @Override - public void paint(Graphics2D g) { - g.setColor(fc); - g.fill(C1); - g.setColor(bc); - g.draw(C1); - g.setFont(FONT); - g.drawString("G", -4, 4); - } - } - - public static class NoSpinSymbol extends PS { - public NoSpinSymbol(Color fc, Color bc) { - super(BOUNDS, bc, fc, null, null); - } - public static final Stroke STROKE = new BasicStroke(1.5f); - public static final int DIM = 10; - public static final int D2 = 8; - public static final Rectangle2D BOUNDS = new Rectangle2D.Double(-DIM, -DIM, 2*DIM, 2*DIM); - public static final Shape CIRCLE = new Ellipse2D.Double(-D2, -D2, D2*2, D2*2); - - @Override - public void paint(Graphics2D g) { - g.setStroke(STROKE); - g.setColor(fc); - g.fillRect(-DIM, -DIM, DIM*2, DIM*2); - g.setColor(bc); - g.drawRect(-DIM, -DIM, DIM*2, DIM*2); - g.draw(CIRCLE); - g.drawLine(-DIM/3, -DIM/3, DIM/3, DIM/3); - g.drawLine(DIM/3, -DIM/3, -DIM/3, DIM/3); - } - } - - public static class ControlValveSymbol extends PS implements Image { - - public static final Stroke STROKE = new BasicStroke(1.5f); - public static final int D = 10; - public static final Path2D SHAPE = - PathUtils.closedPath(-D,-D, -D,D, D,-D, D,D ); - public static final Rectangle2D BOUNDS = SHAPE.getBounds2D(); - - public ControlValveSymbol(Color bc, Color fc) { - super(BOUNDS, bc, fc, null, null); - } - @Override - public void paint(Graphics2D g) { - if (fc!=null) { - g.setColor(fc); - g.fill(SHAPE); - } - if (bc!=null) { - g.setColor(bc); - g.draw(SHAPE); - } - } - } - - public static class ThreeWayValveSymbol extends PS implements Image { - - public static final Stroke STROKE = new BasicStroke(1.5f); - public static final int D = 10; - public static final Path2D SHAPE = - PathUtils.closedPath(-D,D*2/3, -D,-D*2/3, 0,0, -D*2/3,-D, D*2/3,-D, 0,0, D,D*2/3, D,-D*2/3, 0,0 ); - public static final Rectangle2D BOUNDS = SHAPE.getBounds2D(); - - public ThreeWayValveSymbol(Color bc, Color fc) { - super(BOUNDS, bc, fc, null, null); - } - @Override - public void paint(Graphics2D g) { - if (fc!=null) { - g.setColor(fc); - g.fill(SHAPE); - } - if (bc!=null) { - g.setColor(bc); - g.draw(SHAPE); - } - } - } - - - public static class HeatExchangerSymbol extends PS { - - public HeatExchangerSymbol(Color bc, Color fc) { - super(BOUNDS, bc, fc, null, null); - } - public static final Stroke STROKE = new BasicStroke(1.5f); - public static final int D = 10; - public static final Rectangle2D BOUNDS = new Rectangle2D.Double(-D, -D, 2*D, 2*D); - public static final int xPoints[] = new int [] {-D, 0, 0, -D/2, D/2, 0, 0, D}; - public static final int yPoints[] = new int [] {D/2, D/2, D/8, 0, 0, -D/8, -D/2, -D/2}; - - @Override - public void paint(Graphics2D g) { - g.setColor(fc); - g.fill(BOUNDS); - g.setColor(bc); - g.draw(BOUNDS); - g.setStroke(STROKE); - g.drawPolyline(xPoints, yPoints, xPoints.length); - } - } - - public static class PumpSymbol extends PS { - - public PumpSymbol(Color bc, Color fc) { - super(BOUNDS, bc, fc, null, null); - } - public static final Stroke STROKE = new BasicStroke(1.5f); - public static final int DIM = 10; - public static final int D2 = 12; - public static final Rectangle2D BOUNDS = new Rectangle2D.Double(-D2, -D2, 2*D2, 2*D2); - public static final Shape CIRCLE = new Ellipse2D.Double(-D2, -D2, D2*2, D2*2); - public static final Polygon SHAPE = - new Polygon(new int[] {-DIM, -DIM, 0, 0, DIM, 0, 0}, new int[] {DIM/4, -DIM/4, -DIM/4, -DIM, 0, DIM, DIM/4}, 7); - - @Override - public void paint(Graphics2D g) { - g.setColor(fc); - g.fill(CIRCLE); - g.setColor(bc); - g.setStroke( STROKE ); - g.draw(CIRCLE); - g.draw(SHAPE); - } - } - - - public static class ShutoffValveSymbol extends PS implements Image { - - public static final Stroke STROKE = new BasicStroke(1.5f); - public static final int DIM = 10; - public static final Path2D SHAPE = - PathUtils.closedPath(-DIM,-DIM, -DIM,DIM, DIM,-DIM, DIM,DIM ); - public static final Rectangle2D BOUNDS = SHAPE.getBounds2D(); - - public ShutoffValveSymbol(Color bc, Color fc) { - super(BOUNDS, bc, fc, null, null); - } - @Override - public void paint(Graphics2D g) { - if (fc!=null) { - g.setColor(fc); - g.fill(SHAPE); - } - if (bc!=null) { - g.setColor(bc); - g.draw(SHAPE); - g.drawLine(0, -DIM, 0, DIM); - } - } - } - - - public static class SwitchSymbol extends PS { - - public SwitchSymbol(Color bc) { - super(BOUNDS, bc, null, null, null); - } - public static final Stroke STROKE = new BasicStroke(1.5f); - public static final int D = 10; - public static final Rectangle2D BOUNDS = new Rectangle2D.Double(-D, -D, 2*D, 2*D); - - @Override - public void paint(Graphics2D g) { - g.setColor(bc); - g.setStroke(STROKE); - - g.drawLine(0, -D, 0, D); - g.drawLine(-D/4, -D/2, D/4, -D/2); - g.drawLine(-D, 0, D, 0); - g.drawLine(-D/4, D/2, D/4, D/2); - } - } - - - public static class TurbineSymbol extends PS implements Image { - - public static final int DIM = 10; - public static final Polygon SHAPE = - new Polygon(new int[] {-DIM, -DIM, DIM, DIM}, new int[] {-DIM, DIM, DIM*2, -DIM*2}, 4); - public static final Rectangle2D BOUNDS = SHAPE.getBounds2D(); - - public TurbineSymbol(Color bc, Color fc) { - super(BOUNDS, bc, fc, null, null); - } - @Override - public void paint(Graphics2D g) { - if (fc!=null) { - g.setColor(fc); - g.fill(SHAPE); - } - if (bc!=null) { - g.setColor(bc); - g.draw(SHAPE); - } - } - } - - public static class CompressorSymbol extends PS implements Image { - - public static final int DIM = 10; - public static final Polygon SHAPE = - new Polygon(new int[] {DIM, DIM, -DIM, -DIM}, new int[] {-DIM, DIM, DIM*2, -DIM*2}, 4); - public static final Rectangle2D BOUNDS = SHAPE.getBounds2D(); - - public CompressorSymbol(Color bc, Color fc) { - super(BOUNDS, bc, fc, null, null); - } - @Override - public void paint(Graphics2D g) { - if (fc!=null) { - g.setColor(fc); - g.fill(SHAPE); - } - if (bc!=null) { - g.setColor(bc); - g.draw(SHAPE); - } - } - } - - public static class VoltageTransformerSymbol extends PS implements Image { - - public static final Stroke STROKE = new BasicStroke(1.5f); - public static final int DIM = 10; - public static final Rectangle2D BOUNDS = new Rectangle2D.Double(-DIM, -DIM*1.7, 2*DIM, 2*DIM*1.7); - public static final Shape CIRCLE = new Ellipse2D.Double(-DIM, -DIM, DIM*2, DIM*2); - - public VoltageTransformerSymbol(Color c1, Color c2) { - super(BOUNDS, null, null, c1, c2); - } - @Override - public void paint(Graphics2D g) { - g.setStroke(STROKE); - Graphics2D sg = (Graphics2D) g.create(); - - sg.clipRect(-DIM*2, -DIM*2, DIM*4, DIM*2); - sg.setColor(c1); - sg.translate(0, -0.7*DIM); - sg.draw(CIRCLE); - sg.setColor(c2); - sg.translate(0, 1.4*DIM); - sg.draw(CIRCLE); - sg.dispose(); - - g.clipRect(-DIM*2, 0, DIM*4, DIM*2); - g.setColor(c2); - g.translate(0, 0.7*DIM); - g.draw(CIRCLE); - g.setColor(c1); - g.translate(0, -1.4*DIM); - g.draw(CIRCLE); - } - } - - public static class TankSymbol extends PS implements Image { - - /** - * Create tank shape. - * @param r rectangle - * @param cap cap size - * @return tank shape - */ - public static Path2D createTankShape(Rectangle2D r, double cap) - { - double x1 = r.getMinX(); - double y1 = r.getMinY(); - double x2 = r.getMaxX(); - double y2 = r.getMaxY(); - Path2D p = new Path2D.Double(); - p.moveTo(x1+cap, y1); - p.lineTo(x2-cap, y1); - p.quadTo(x2,y1, x2,y1+cap); - p.lineTo(x2, y2-cap); - p.quadTo(x2, y2, x2-cap, y2); - p.lineTo(x1+cap, y2); - p.quadTo(x1, y2, x1, y2-cap); - p.lineTo(x1, y1+cap); - p.quadTo(x1, y1, x1+cap, y1); - return p; - } - - Path2D shape; - - public TankSymbol(Rectangle2D rect, double cap, Color bc, Color fc) { - super(rect, bc, fc, null, null); - shape = createTankShape(rect, cap); - } - - @Override - public void paint(Graphics2D g) { - g.setColor(fc); - g.setStroke(GeometryUtils.BASIC_STROKE); - g.fill(shape); - if (bc!=null) { - g.setColor(bc); - g.draw(shape); - } - } - - } - - public static class Tank2Symbol extends PS implements Image { - - public static Path2D createWShape() - { - Path2D p = new Path2D.Double(); - double k = Math.sqrt(2); - p.moveTo(-1, -1); - p.quadTo(0, -k, 1, -1); - p.quadTo(k, 0, 1, 1); - p.quadTo(0, k, -1, 1); - p.quadTo(-k, 0, -1, -1); - p.lineTo(-1, 1); - p.lineTo(0,0); - p.lineTo(1, 1); - p.lineTo(1, -1); - return p; - } - - Path2D shape; - Path2D shape2; - - public Tank2Symbol(Rectangle2D rect, double cap, Color bc, Color fc, Color a1) { - super(rect, bc, fc, a1, null); - shape = TankSymbol.createTankShape(rect, cap); - shape2 = createWShape(); - } - - @Override - public void paint(Graphics2D g) { - g.setColor(fc); - g.setStroke(GeometryUtils.BASIC_STROKE); - g.setStroke(new BasicStroke(0.3f)); - g.fill(shape); - if (bc!=null) { - g.setColor(bc); - g.draw(shape); - } - g.setColor(c1); - double d = Math.min(bounds.getHeight(), bounds.getWidth()); - g.translate(bounds.getMinX()+d/2, bounds.getMinY()+d/2); - g.scale(d/3, d/3); - g.draw(shape2); - } - - } - - private abstract static class PS implements Image { - - - public final Rectangle2D bounds; - public final Color c1; - public final Color c2; - public final Color bc; - public final Color fc; - private final int hash; - public PS(Rectangle2D bounds, Color bc, Color fc, Color c1, Color c2) { - this.bounds = bounds; - this.bc = bc; - this.fc = fc; - this.c1 = c1; - this.c2 = c2; - int hash = this.getClass().hashCode(); - if (bc!=null) hash += 3 * bc.hashCode(); - if (fc!=null) hash += 5 * fc.hashCode(); - if (c1!=null) hash += 7 * c1.hashCode(); - if (c2!=null) hash += 11 * c2.hashCode(); - this.hash = hash; - } - - @Override - public int hashCode() { - return hash; - } - - @Override - public boolean equals(Object obj) { - if (!obj.getClass().equals(getClass()))return false; - PS other = (PS) obj; - return - ObjectUtils.objectEquals(other.c1, c1) && - ObjectUtils.objectEquals(other.c2, c2) && - ObjectUtils.objectEquals(other.fc, fc) && - ObjectUtils.objectEquals(other.bc, bc); - } - @Override - public Rectangle2D getBounds() { - return bounds; - } - - @Override - public Shape getOutline() { - return bounds; - } - - @Override - public Node init(G2DParentNode g) { -// return init(g); - return null; - } - - public void paint(Graphics2D g2d) { // FIXME: get rid of this - - } - - @Override - public void addImageListener(ImageListener listener) { - } - - static EnumSet caps = EnumSet.of(Feature.Vector); - @Override - public EnumSet getFeatures() { - return caps; - } - - @Override - public void removeImageListener(ImageListener listener) { - } - - } - -} +/******************************************************************************* + * Copyright (c) 2007, 2010 Association for Decentralized Information Management + * in Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.g2d.elementclass.operationsymbols; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Polygon; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Path2D; +import java.awt.geom.Rectangle2D; +import java.util.EnumSet; + +import org.simantics.g2d.image.Image; +import org.simantics.g2d.image.impl.Shadow; +import org.simantics.g2d.image.impl.Shadow.ShadowParameters; +import org.simantics.g2d.utils.GeometryUtils; +import org.simantics.g2d.utils.PathUtils; +import org.simantics.scenegraph.Node; +import org.simantics.scenegraph.g2d.G2DParentNode; +import org.simantics.utils.ObjectUtils; + +/** + * @author Toni Kalajainen + */ +public class OperationSymbols { + + static final double SHADOW_SIZE = 100; + static final Color BORDER_COLOR = Color.GRAY; + static final Color FILL_COLOR = Color.GRAY; + static final Color SECONDARY_COLOR = Color.GREEN; + static final ShadowParameters SHADOW = new ShadowParameters(0.5, Color.BLACK, 2); + + static final Color HOT = new Color(50, 50, 200); + static final Color COLD = new Color(190, 190, 255); + + public static final BoilerHeatExchanger[] BOILER_HES = new BoilerHeatExchanger[] { + new BoilerHeatExchanger(null, HOT, HOT, null, HOT), + new BoilerHeatExchanger(HOT, null, COLD, COLD, HOT), + new BoilerHeatExchanger(HOT, null, COLD, null, HOT), + new BoilerHeatExchanger(HOT, null, null, HOT, HOT) + }; + + public static final Image BOILER_SYMBOL = new BoilerSymbol2(FILL_COLOR, FILL_COLOR, BOILER_HES); + public static final Image BOILER_SHADOW = new Shadow(BOILER_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); + + public static final Image COMPRESSOR_SYMBOL = new CompressorSymbol(FILL_COLOR, BORDER_COLOR); + public static final Image COMPRESSOR_SHADOW = new Shadow(COMPRESSOR_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); + + public static final Image NO_SPIN_SYMBOL = new NoSpinSymbol(FILL_COLOR, BORDER_COLOR); + public static final Image NO_SPIN_SHADOW = new Shadow(NO_SPIN_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); + + public static final Image CONTROLVALVE_SYMBOL = new ControlValveSymbol(BORDER_COLOR, FILL_COLOR); + public static final Image CONTROLVALVE_SHADOW = new Shadow(CONTROLVALVE_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); + + public static final Image THREEWAYVALVE_SYMBOL = new ThreeWayValveSymbol(BORDER_COLOR, FILL_COLOR); + public static final Image THREEWAYVALVE_SHADOW = new Shadow(THREEWAYVALVE_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); + + public static final Image HEATEXCHANGER_SYMBOL = new HeatExchangerSymbol(BORDER_COLOR, FILL_COLOR); + public static final Image HEATEXCHANGER_SHADOW = new Shadow(HEATEXCHANGER_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); + + public static final Image PUMP_SYMBOL = new PumpSymbol(BORDER_COLOR, FILL_COLOR); + public static final Image PUMP_SHADOW = new Shadow(PUMP_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); + + public static final Image SHUTOFFVALVE_SYMBOL = new ShutoffValveSymbol(BORDER_COLOR, FILL_COLOR); + public static final Image SHUTOFFVALVE_SHADOW = new Shadow(SHUTOFFVALVE_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); + + public static final Image SWITCH_SYMBOL = new SwitchSymbol(BORDER_COLOR); + public static final Image SWITCH_SHADOW = new Shadow(SWITCH_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); + + public static final Image TURBINE_SYMBOL = new TurbineSymbol(BORDER_COLOR, FILL_COLOR); + public static final Image TURBINE_SHADOW = new Shadow(TURBINE_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); + + public static final Image VOLTAGETRANSFORMER_SYMBOL = new VoltageTransformerSymbol(SECONDARY_COLOR, BORDER_COLOR); + public static final Image VOLTAGETRANSFORMER_SHADOW = new Shadow(VOLTAGETRANSFORMER_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); + + public static final Image CHIMNEY_SYMBOL = new ChimneySymbol(SECONDARY_COLOR, BORDER_COLOR); + public static final Image CHIMNEY_SHADOW = new Shadow(CHIMNEY_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); + + public static final Image GENERATOR_SYMBOL = new GeneratorSymbol(SECONDARY_COLOR, BORDER_COLOR); + public static final Image GENERATOR_SHADOW = new Shadow(GENERATOR_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); + + public static final Image TANK_SYMBOL = new TankSymbol(new Rectangle2D.Double(0,0, 20.0, 8.0), 2.0, SECONDARY_COLOR, BORDER_COLOR); + public static final Image TANK_SHADOW = new Shadow(TANK_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); + + public static final Image TANK2_SYMBOL = new Tank2Symbol(new Rectangle2D.Double(0,0, 20.0, 8.0), 2.0, SECONDARY_COLOR, BORDER_COLOR, HOT); + public static final Image TANK2_SHADOW = new Shadow(TANK2_SYMBOL, SHADOW, SHADOW_SIZE, SHADOW_SIZE); + + public static class BoilerSymbol extends PS implements Image { + public static final int DIM = 10; + public static final Polygon SHAPE = + new Polygon(new int[] {-DIM, -DIM, 0, DIM, DIM, 0}, new int[] {2*DIM, -2*DIM, -3*DIM, -2*DIM, 2*DIM, 3*DIM}, 6); + public static final Rectangle2D BOUNDS = SHAPE.getBounds2D(); + + public BoilerSymbol(Color bc, Color fc) { + super(SHAPE.getBounds2D(), bc, fc, null, null); + } + @Override + public void paint(Graphics2D g) { +// if (fc!=null) { +// g.setColor(fc); +// g.fill(SHAPE); +// } +// if (bc!=null) { +// g.setColor(bc); +// g.draw(SHAPE); +// } + } + } + + public static class BoilerHeatExchanger { + public final Color color; + public final Color topLeft, topRight, bottomLeft, bottomRight; + public BoilerHeatExchanger(Color topLeft, Color topRight, + Color bottomLeft, Color bottomRight, + Color color) { + this.bottomLeft = bottomLeft; + this.bottomRight = bottomRight; + this.color = color; + this.topLeft = topLeft; + this.topRight = topRight; + } + } + + public static class BoilerSymbol2 extends PS implements Image { + + public static Path2D HE = PathUtils.path(10,0, 0,0, 0,5, 5,10, -5,10, 0,15, 0,20); + + public static Rectangle2D getBoilerRect(BoilerHeatExchanger ... hes) + { + return new Rectangle2D.Double(-10, 0, 20, hes.length*25+25); + } + + public static Shape getBoilerShape(BoilerHeatExchanger ... hes) + { + Path2D p = new Path2D.Double(); + p.moveTo(-10, 10); + p.lineTo( 0, 0); + p.lineTo( 10, 10); + double h = hes.length * 25; + p.lineTo( 10, h+15); + p.lineTo( 0, h+25); + p.lineTo(-10, h+15); + p.closePath(); + return p; + } + + private final Shape shape; + private final BoilerHeatExchanger [] hes; + + public BoilerSymbol2(Color fc, Color bc, BoilerHeatExchanger ... hes) { + super( getBoilerRect(hes), bc, fc, null, null); + shape = getBoilerShape(hes); + this.hes = hes; + } + + @Override + public void paint(Graphics2D g) { + if (fc!=null) { + g.setColor(fc); + g.fill(shape); + } + if (bc!=null) { + g.setColor(bc); + g.draw(shape); + } + // paint heat exchangers + g.translate(0, 15); + for (BoilerHeatExchanger he : hes) + { + g.setColor(he.color); + g.draw(HE); + if (he.topLeft!=null) { + g.setColor(he.topLeft); + g.drawLine(-10, 0, 0, 0); + } + if (he.topRight!=null) { + g.setColor(he.topRight); + g.drawLine(0, 0, 10, 0); + } + if (he.bottomLeft!=null) { + g.setColor(he.bottomLeft); + g.drawLine(-10, 20, 0, 20); + } + if (he.bottomRight!=null) { + g.setColor(he.bottomRight); + g.drawLine(0, 20, 10, 20); + } + g.translate(0, 25); + } + } + } + + public static class ChimneySymbol extends PS implements Image { + + public static final int DIM = 10; + public static final int D2 = 8; + public static final Shape C1 = new Ellipse2D.Double(-DIM, -DIM, DIM*2, DIM*2); + public static final Shape C2 = new Ellipse2D.Double(-D2, -D2, D2*2, D2*2); + + public static final Rectangle2D BOUNDS = new Rectangle2D.Double(-10, -10, 20, 40); + + public ChimneySymbol(Color fc, Color bc) { + super( BOUNDS, bc, fc, null, null); + assert(bc!=null); + } + + @Override + public void paint(Graphics2D g) { + g.setColor(fc); + g.fill(C2); + g.fillRect(-5, 14, 10, 8); + g.setColor(bc); + g.draw(C1); + g.draw(C2); + + g.drawLine(0, DIM, 0, 15); + g.drawRect(-5, 14, 10, 8); + g.drawLine(-5, 14, 5, 22); + } + } + + + public static class GeneratorSymbol extends PS implements Image { + + public static final int DIM = 10; + public static final Shape C1 = new Ellipse2D.Double(-DIM, -DIM, DIM*2, DIM*2); + + public static final Rectangle2D BOUNDS = C1.getBounds2D(); + private final static Font FONT = Font.decode("Serif-12"); + + public GeneratorSymbol(Color fc, Color bc) { + super( BOUNDS, bc, fc, null, null); + assert(bc!=null && fc!=null); + } + + @Override + public void paint(Graphics2D g) { + g.setColor(fc); + g.fill(C1); + g.setColor(bc); + g.draw(C1); + g.setFont(FONT); + g.drawString("G", -4, 4); + } + } + + public static class NoSpinSymbol extends PS { + public NoSpinSymbol(Color fc, Color bc) { + super(BOUNDS, bc, fc, null, null); + } + public static final Stroke STROKE = new BasicStroke(1.5f); + public static final int DIM = 10; + public static final int D2 = 8; + public static final Rectangle2D BOUNDS = new Rectangle2D.Double(-DIM, -DIM, 2*DIM, 2*DIM); + public static final Shape CIRCLE = new Ellipse2D.Double(-D2, -D2, D2*2, D2*2); + + @Override + public void paint(Graphics2D g) { + g.setStroke(STROKE); + g.setColor(fc); + g.fillRect(-DIM, -DIM, DIM*2, DIM*2); + g.setColor(bc); + g.drawRect(-DIM, -DIM, DIM*2, DIM*2); + g.draw(CIRCLE); + g.drawLine(-DIM/3, -DIM/3, DIM/3, DIM/3); + g.drawLine(DIM/3, -DIM/3, -DIM/3, DIM/3); + } + } + + public static class ControlValveSymbol extends PS implements Image { + + public static final Stroke STROKE = new BasicStroke(1.5f); + public static final int D = 10; + public static final Path2D SHAPE = + PathUtils.closedPath(-D,-D, -D,D, D,-D, D,D ); + public static final Rectangle2D BOUNDS = SHAPE.getBounds2D(); + + public ControlValveSymbol(Color bc, Color fc) { + super(BOUNDS, bc, fc, null, null); + } + @Override + public void paint(Graphics2D g) { + if (fc!=null) { + g.setColor(fc); + g.fill(SHAPE); + } + if (bc!=null) { + g.setColor(bc); + g.draw(SHAPE); + } + } + } + + public static class ThreeWayValveSymbol extends PS implements Image { + + public static final Stroke STROKE = new BasicStroke(1.5f); + public static final int D = 10; + public static final Path2D SHAPE = + PathUtils.closedPath(-D,D*2/3, -D,-D*2/3, 0,0, -D*2/3,-D, D*2/3,-D, 0,0, D,D*2/3, D,-D*2/3, 0,0 ); + public static final Rectangle2D BOUNDS = SHAPE.getBounds2D(); + + public ThreeWayValveSymbol(Color bc, Color fc) { + super(BOUNDS, bc, fc, null, null); + } + @Override + public void paint(Graphics2D g) { + if (fc!=null) { + g.setColor(fc); + g.fill(SHAPE); + } + if (bc!=null) { + g.setColor(bc); + g.draw(SHAPE); + } + } + } + + + public static class HeatExchangerSymbol extends PS { + + public HeatExchangerSymbol(Color bc, Color fc) { + super(BOUNDS, bc, fc, null, null); + } + public static final Stroke STROKE = new BasicStroke(1.5f); + public static final int D = 10; + public static final Rectangle2D BOUNDS = new Rectangle2D.Double(-D, -D, 2*D, 2*D); + public static final int xPoints[] = new int [] {-D, 0, 0, -D/2, D/2, 0, 0, D}; + public static final int yPoints[] = new int [] {D/2, D/2, D/8, 0, 0, -D/8, -D/2, -D/2}; + + @Override + public void paint(Graphics2D g) { + g.setColor(fc); + g.fill(BOUNDS); + g.setColor(bc); + g.draw(BOUNDS); + g.setStroke(STROKE); + g.drawPolyline(xPoints, yPoints, xPoints.length); + } + } + + public static class PumpSymbol extends PS { + + public PumpSymbol(Color bc, Color fc) { + super(BOUNDS, bc, fc, null, null); + } + public static final Stroke STROKE = new BasicStroke(1.5f); + public static final int DIM = 10; + public static final int D2 = 12; + public static final Rectangle2D BOUNDS = new Rectangle2D.Double(-D2, -D2, 2*D2, 2*D2); + public static final Shape CIRCLE = new Ellipse2D.Double(-D2, -D2, D2*2, D2*2); + public static final Polygon SHAPE = + new Polygon(new int[] {-DIM, -DIM, 0, 0, DIM, 0, 0}, new int[] {DIM/4, -DIM/4, -DIM/4, -DIM, 0, DIM, DIM/4}, 7); + + @Override + public void paint(Graphics2D g) { + g.setColor(fc); + g.fill(CIRCLE); + g.setColor(bc); + g.setStroke( STROKE ); + g.draw(CIRCLE); + g.draw(SHAPE); + } + } + + + public static class ShutoffValveSymbol extends PS implements Image { + + public static final Stroke STROKE = new BasicStroke(1.5f); + public static final int DIM = 10; + public static final Path2D SHAPE = + PathUtils.closedPath(-DIM,-DIM, -DIM,DIM, DIM,-DIM, DIM,DIM ); + public static final Rectangle2D BOUNDS = SHAPE.getBounds2D(); + + public ShutoffValveSymbol(Color bc, Color fc) { + super(BOUNDS, bc, fc, null, null); + } + @Override + public void paint(Graphics2D g) { + if (fc!=null) { + g.setColor(fc); + g.fill(SHAPE); + } + if (bc!=null) { + g.setColor(bc); + g.draw(SHAPE); + g.drawLine(0, -DIM, 0, DIM); + } + } + } + + + public static class SwitchSymbol extends PS { + + public SwitchSymbol(Color bc) { + super(BOUNDS, bc, null, null, null); + } + public static final Stroke STROKE = new BasicStroke(1.5f); + public static final int D = 10; + public static final Rectangle2D BOUNDS = new Rectangle2D.Double(-D, -D, 2*D, 2*D); + + @Override + public void paint(Graphics2D g) { + g.setColor(bc); + g.setStroke(STROKE); + + g.drawLine(0, -D, 0, D); + g.drawLine(-D/4, -D/2, D/4, -D/2); + g.drawLine(-D, 0, D, 0); + g.drawLine(-D/4, D/2, D/4, D/2); + } + } + + + public static class TurbineSymbol extends PS implements Image { + + public static final int DIM = 10; + public static final Polygon SHAPE = + new Polygon(new int[] {-DIM, -DIM, DIM, DIM}, new int[] {-DIM, DIM, DIM*2, -DIM*2}, 4); + public static final Rectangle2D BOUNDS = SHAPE.getBounds2D(); + + public TurbineSymbol(Color bc, Color fc) { + super(BOUNDS, bc, fc, null, null); + } + @Override + public void paint(Graphics2D g) { + if (fc!=null) { + g.setColor(fc); + g.fill(SHAPE); + } + if (bc!=null) { + g.setColor(bc); + g.draw(SHAPE); + } + } + } + + public static class CompressorSymbol extends PS implements Image { + + public static final int DIM = 10; + public static final Polygon SHAPE = + new Polygon(new int[] {DIM, DIM, -DIM, -DIM}, new int[] {-DIM, DIM, DIM*2, -DIM*2}, 4); + public static final Rectangle2D BOUNDS = SHAPE.getBounds2D(); + + public CompressorSymbol(Color bc, Color fc) { + super(BOUNDS, bc, fc, null, null); + } + @Override + public void paint(Graphics2D g) { + if (fc!=null) { + g.setColor(fc); + g.fill(SHAPE); + } + if (bc!=null) { + g.setColor(bc); + g.draw(SHAPE); + } + } + } + + public static class VoltageTransformerSymbol extends PS implements Image { + + public static final Stroke STROKE = new BasicStroke(1.5f); + public static final int DIM = 10; + public static final Rectangle2D BOUNDS = new Rectangle2D.Double(-DIM, -DIM*1.7, 2*DIM, 2*DIM*1.7); + public static final Shape CIRCLE = new Ellipse2D.Double(-DIM, -DIM, DIM*2, DIM*2); + + public VoltageTransformerSymbol(Color c1, Color c2) { + super(BOUNDS, null, null, c1, c2); + } + @Override + public void paint(Graphics2D g) { + g.setStroke(STROKE); + Graphics2D sg = (Graphics2D) g.create(); + + sg.clipRect(-DIM*2, -DIM*2, DIM*4, DIM*2); + sg.setColor(c1); + sg.translate(0, -0.7*DIM); + sg.draw(CIRCLE); + sg.setColor(c2); + sg.translate(0, 1.4*DIM); + sg.draw(CIRCLE); + sg.dispose(); + + g.clipRect(-DIM*2, 0, DIM*4, DIM*2); + g.setColor(c2); + g.translate(0, 0.7*DIM); + g.draw(CIRCLE); + g.setColor(c1); + g.translate(0, -1.4*DIM); + g.draw(CIRCLE); + } + } + + public static class TankSymbol extends PS implements Image { + + /** + * Create tank shape. + * @param r rectangle + * @param cap cap size + * @return tank shape + */ + public static Path2D createTankShape(Rectangle2D r, double cap) + { + double x1 = r.getMinX(); + double y1 = r.getMinY(); + double x2 = r.getMaxX(); + double y2 = r.getMaxY(); + Path2D p = new Path2D.Double(); + p.moveTo(x1+cap, y1); + p.lineTo(x2-cap, y1); + p.quadTo(x2,y1, x2,y1+cap); + p.lineTo(x2, y2-cap); + p.quadTo(x2, y2, x2-cap, y2); + p.lineTo(x1+cap, y2); + p.quadTo(x1, y2, x1, y2-cap); + p.lineTo(x1, y1+cap); + p.quadTo(x1, y1, x1+cap, y1); + return p; + } + + Path2D shape; + + public TankSymbol(Rectangle2D rect, double cap, Color bc, Color fc) { + super(rect, bc, fc, null, null); + shape = createTankShape(rect, cap); + } + + @Override + public void paint(Graphics2D g) { + g.setColor(fc); + g.setStroke(GeometryUtils.BASIC_STROKE); + g.fill(shape); + if (bc!=null) { + g.setColor(bc); + g.draw(shape); + } + } + + } + + public static class Tank2Symbol extends PS implements Image { + + public static Path2D createWShape() + { + Path2D p = new Path2D.Double(); + double k = Math.sqrt(2); + p.moveTo(-1, -1); + p.quadTo(0, -k, 1, -1); + p.quadTo(k, 0, 1, 1); + p.quadTo(0, k, -1, 1); + p.quadTo(-k, 0, -1, -1); + p.lineTo(-1, 1); + p.lineTo(0,0); + p.lineTo(1, 1); + p.lineTo(1, -1); + return p; + } + + Path2D shape; + Path2D shape2; + + public Tank2Symbol(Rectangle2D rect, double cap, Color bc, Color fc, Color a1) { + super(rect, bc, fc, a1, null); + shape = TankSymbol.createTankShape(rect, cap); + shape2 = createWShape(); + } + + @Override + public void paint(Graphics2D g) { + g.setColor(fc); + g.setStroke(GeometryUtils.BASIC_STROKE); + g.setStroke(new BasicStroke(0.3f)); + g.fill(shape); + if (bc!=null) { + g.setColor(bc); + g.draw(shape); + } + g.setColor(c1); + double d = Math.min(bounds.getHeight(), bounds.getWidth()); + g.translate(bounds.getMinX()+d/2, bounds.getMinY()+d/2); + g.scale(d/3, d/3); + g.draw(shape2); + } + + } + + private abstract static class PS implements Image { + + + public final Rectangle2D bounds; + public final Color c1; + public final Color c2; + public final Color bc; + public final Color fc; + private final int hash; + public PS(Rectangle2D bounds, Color bc, Color fc, Color c1, Color c2) { + this.bounds = bounds; + this.bc = bc; + this.fc = fc; + this.c1 = c1; + this.c2 = c2; + int hash = this.getClass().hashCode(); + if (bc!=null) hash += 3 * bc.hashCode(); + if (fc!=null) hash += 5 * fc.hashCode(); + if (c1!=null) hash += 7 * c1.hashCode(); + if (c2!=null) hash += 11 * c2.hashCode(); + this.hash = hash; + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public boolean equals(Object obj) { + if (!obj.getClass().equals(getClass()))return false; + PS other = (PS) obj; + return + ObjectUtils.objectEquals(other.c1, c1) && + ObjectUtils.objectEquals(other.c2, c2) && + ObjectUtils.objectEquals(other.fc, fc) && + ObjectUtils.objectEquals(other.bc, bc); + } + @Override + public Rectangle2D getBounds() { + return bounds; + } + + @Override + public Shape getOutline() { + return bounds; + } + + @Override + public Node init(G2DParentNode g) { +// return init(g); + return null; + } + + public void paint(Graphics2D g2d) { // FIXME: get rid of this + + } + + @Override + public void addImageListener(ImageListener listener) { + } + + static EnumSet caps = EnumSet.of(Feature.Vector); + @Override + public EnumSet getFeatures() { + return caps; + } + + @Override + public void removeImageListener(ImageListener listener) { + } + + } + +}