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}