001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.mappaint.styleelement;
003
004import java.util.Objects;
005
006import org.openstreetmap.josm.data.osm.OsmPrimitive;
007import org.openstreetmap.josm.data.osm.Way;
008import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
009import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
010import org.openstreetmap.josm.gui.mappaint.Cascade;
011import org.openstreetmap.josm.gui.mappaint.Environment;
012import org.openstreetmap.josm.gui.mappaint.Keyword;
013import org.openstreetmap.josm.tools.CheckParameterUtil;
014
015public class RepeatImageElement extends StyleElement {
016
017    /**
018     * The side on which the image should be aligned to the line.
019     */
020    public enum LineImageAlignment {
021        TOP(.5),
022        CENTER(0),
023        BOTTOM(-.5);
024
025        private final double alignmentOffset;
026
027        LineImageAlignment(double alignmentOffset) {
028            this.alignmentOffset = alignmentOffset;
029        }
030
031        /**
032         * Gets the alignment offset.
033         * @return The offset relative to the image height compared to placing the image in the middle of the line.
034         */
035        public double getAlignmentOffset() {
036            return alignmentOffset;
037        }
038    }
039
040    public MapImage pattern;
041    public float offset;
042    public float spacing;
043    public float phase;
044    public LineImageAlignment align;
045
046    private static final String[] REPEAT_IMAGE_KEYS = {REPEAT_IMAGE, REPEAT_IMAGE_WIDTH, REPEAT_IMAGE_HEIGHT, REPEAT_IMAGE_OPACITY,
047            null, null};
048
049    public RepeatImageElement(Cascade c, MapImage pattern, float offset, float spacing, float phase, LineImageAlignment align) {
050        super(c, 2.9f);
051        CheckParameterUtil.ensureParameterNotNull(pattern);
052        CheckParameterUtil.ensureParameterNotNull(align);
053        this.pattern = pattern;
054        this.offset = offset;
055        this.spacing = spacing;
056        this.phase = phase;
057        this.align = align;
058    }
059
060    public static RepeatImageElement create(Environment env) {
061        MapImage pattern = NodeElement.createIcon(env, REPEAT_IMAGE_KEYS);
062        if (pattern == null)
063            return null;
064        Cascade c = env.mc.getCascade(env.layer);
065        float offset = c.get(REPEAT_IMAGE_OFFSET, 0f, Float.class);
066        float spacing = c.get(REPEAT_IMAGE_SPACING, 0f, Float.class);
067        float phase = -c.get(REPEAT_IMAGE_PHASE, 0f, Float.class);
068
069        LineImageAlignment align = LineImageAlignment.CENTER;
070        Keyword alignKW = c.get(REPEAT_IMAGE_ALIGN, Keyword.CENTER, Keyword.class);
071        if ("top".equals(alignKW.val)) {
072            align = LineImageAlignment.TOP;
073        } else if ("bottom".equals(alignKW.val)) {
074            align = LineImageAlignment.BOTTOM;
075        }
076
077        return new RepeatImageElement(c, pattern, offset, spacing, phase, align);
078    }
079
080    @Override
081    public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, StyledMapRenderer painter,
082            boolean selected, boolean outermember, boolean member) {
083        Way w = (Way) primitive;
084        painter.drawRepeatImage(w, pattern, painter.isInactiveMode() || w.isDisabled(), offset, spacing, phase, align);
085    }
086
087    @Override
088    public boolean isProperLineStyle() {
089        return true;
090    }
091
092    @Override
093    public boolean equals(Object obj) {
094        if (this == obj) return true;
095        if (obj == null || getClass() != obj.getClass()) return false;
096        if (!super.equals(obj)) return false;
097        RepeatImageElement that = (RepeatImageElement) obj;
098        return Float.compare(that.offset, offset) == 0 &&
099                Float.compare(that.spacing, spacing) == 0 &&
100                Float.compare(that.phase, phase) == 0 &&
101                Objects.equals(pattern, that.pattern) &&
102                align == that.align;
103    }
104
105    @Override
106    public int hashCode() {
107        return Objects.hash(super.hashCode(), pattern, offset, spacing, phase, align);
108    }
109
110    @Override
111    public String toString() {
112        return "RepeatImageStyle{" + super.toString() + "pattern=[" + pattern +
113                "], offset=" + offset + ", spacing=" + spacing +
114                ", phase=" + (-phase) + ", align=" + align + '}';
115    }
116}