/* 
 * This code is part of Project Waterloo from King's College London
 * 
 *
 * Copyright King's College London 2011-
 * 
 * @author Malcolm Lidierth, King's College London  [Contact]
 * 
 * Project Waterloo is free software:  you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Project Waterloo is distributed in the hope that it will  be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 *
 */
package kcl.waterloo.marker;

import java.awt.Font;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.*;
import java.util.ArrayList;
import java.util.HashMap;
import kcl.waterloo.plotmodel2D.GJCyclicArrayList;

/**
 *
 * @author Malcolm Lidierth, King's College London  [Contact]
 */
public class GJMarker {

    private static final class maps {

        private static final HashMap circles = new HashMap();
        private static final HashMap squares = new HashMap();
        private static final HashMap triangles = new HashMap();
        private static final HashMap itriangles = new HashMap();
        private static final HashMap ltriangles = new HashMap();
        private static final HashMap rtriangles = new HashMap();
        private static final HashMap diamonds = new HashMap();
        private static final HashMap strings = new HashMap();
        private static final GJMarker dot = makeDot();
        private static final HashMap circleMarkers = new HashMap();
        private static final HashMap squareMarkers = new HashMap();
        private static final HashMap triangleMarkers = new HashMap();
        private static final HashMap itriangleMarkers = new HashMap();
        private static final HashMap ltriangleMarkers = new HashMap();
        private static final HashMap rtriangleMarkers = new HashMap();
        private static final HashMap diamondMarkers = new HashMap();
    }

    public static void reset() {
        maps.circleMarkers.clear();
        maps.squareMarkers.clear();
        maps.triangleMarkers.clear();
        maps.itriangleMarkers.clear();
        maps.ltriangleMarkers.clear();
        maps.rtriangleMarkers.clear();
        maps.diamondMarkers.clear();

        maps.circles.clear();
        maps.squares.clear();
        maps.triangles.clear();
        maps.itriangles.clear();
        maps.ltriangles.clear();
        maps.rtriangles.clear();
        maps.diamonds.clear();
        maps.strings.clear();
    }
    // Main code block
    private final Shape path;

    public GJMarker() {
        path = new Path2D.Double();

    }

    /**
     * GLMarker constructor
     *
     * @param p a Path2D object
     */
    public GJMarker(Path2D p) {
        path = p;
    }

    /**
     * GLMarker constructor
     *
     * @param p a Path2D.Double object
     */
    public GJMarker(Path2D.Double p) {
        path = p;
    }

    /**
     * GLMarker constructor
     *
     * @param s any object implementing the Shape interface
     */
    public GJMarker(Shape s) {
        this();
        ArrayList segments = new ArrayList();
        double[] d = new double[6];
        int type;
        for (PathIterator it = s.getPathIterator(null); !it.isDone(); it.next()) {
            type = it.currentSegment(d);
            segments.add(new GJPathSegmentInfo(type, d));
        }
        setSegments(segments);
    }

    /**
     * GLMarker constructor
     *
     * @param arr
     */
    public GJMarker(ArrayList arr) {
        this();
        setSegments(arr);
    }

    public static GJMarker circle(double width) {
        if (!maps.circles.containsKey(width)) {
            maps.circles.put(width, makeCircle(width));
            maps.circleMarkers.put(width, new GJMarker(maps.circles.get(width)));
        }
        return maps.circleMarkers.get(width);
    }

    public static GJMarker square(double width) {
        if (!maps.squares.containsKey(width)) {
            maps.squares.put(width, makeSquare(width));
            maps.squareMarkers.put(width, new GJMarker(maps.squares.get(width)));
        }
        return maps.squareMarkers.get(width);
    }

    public static GJMarker triangle(double width) {
        if (!maps.triangles.containsKey(width)) {
            maps.triangles.put(width, makeTriangle(width));
            maps.triangleMarkers.put(width, new GJMarker(maps.triangles.get(width)));
        }
        return maps.triangleMarkers.get(width);
    }

    public static GJMarker invertedTriangle(double width) {
        if (!maps.itriangles.containsKey(width)) {
            maps.itriangles.put(width, makeInvertedTriangle(width));
            maps.itriangleMarkers.put(width, new GJMarker(maps.itriangles.get(width)));
        }
        return maps.itriangleMarkers.get(width);
    }

    public static GJMarker leftTriangle(double width) {
        if (!maps.ltriangles.containsKey(width)) {
            maps.ltriangles.put(width, makeLeftTriangle(width));
            maps.ltriangleMarkers.put(width, new GJMarker(maps.ltriangles.get(width)));
        }
        return maps.ltriangleMarkers.get(width);
    }

    public static GJMarker rightTriangle(double width) {
        if (!maps.rtriangles.containsKey(width)) {
            maps.rtriangles.put(width, makeRightTriangle(width));
            maps.rtriangleMarkers.put(width, new GJMarker(maps.rtriangles.get(width)));
        }
        return maps.rtriangleMarkers.get(width);
    }

    public static GJMarker diamond(double width) {
        if (!maps.diamonds.containsKey(width)) {
            maps.diamonds.put(width, makeDiamond(width));
            maps.diamondMarkers.put(width, new GJMarker(maps.diamonds.get(width)));
        }
        return maps.diamondMarkers.get(width);
    }

    public static GJMarker dot(double width) {
        return maps.dot;
    }

    /**
     * Creates a GJMarler from the string on input (typically, but not
     * necessarily a single character) using 18 point size and the "Sans Serif"
     * virtual font.
     *
     * The marker will be offset by its centroid so that it "looks right" when
     * used in a scatter plot.
     *
     * @param s input fromString
     * @return a GJMarker
     */
    public static GJMarker fromString(String s) {
        if (!maps.strings.containsKey(s)) {
            String2D p = makeCharMarker(s, 18);
            Point2D tr = ShapeUtils.getCentroid(p);
            p.transform(AffineTransform.getTranslateInstance(-tr.getX(), -tr.getY()));
            maps.strings.put(s, p);
        }
        return new GJMarker(maps.strings.get(s));
    }

    /**
     * Creates a GJMarker from the string on input (typically, but not
     * necessarily a single character) using the specified point size and the
     * "Sans Serif" virtual font.
     *
     * The marker will be offset by its centroid so that it "looks right" when
     * used in a scatter plot.
     *
     * @param s input fromString
     * @param pointSize the point size for the font
     * @return a GJMarker
     */
    public static GJMarker fromString(String s, int pointSize) {
        return fromString(s, pointSize, "Sans Serif");
    }

    public static GJMarker fromString(String s, int pointSize, String font) {
        Path2D p = makeMarkerFromFont(s, pointSize, font);
        Point2D tr = ShapeUtils.getCentroid(p);
        p.transform(AffineTransform.getTranslateInstance(-tr.getX(), -tr.getY()));
        return new GJMarker(p);
    }

    public final Rectangle getBounds() {
        return path.getBounds();
    }

    public final Rectangle2D getBounds2D() {
        return path.getBounds2D();
    }

    public final double getWidth() {
        return path.getBounds2D().getWidth();
    }

    public final double getHeight() {
        return path.getBounds2D().getHeight();
    }

    public final Path2D getPath() {
        return new Path2D.Double(path);
    }

    /**
     * This method is retained for backwards compatability during
     * de-serialization of pre-1.1 Alpha3 xml files only.
     *
     * @deprecated
     */
    public final void setPath(Shape p) {
        setSegments(ShapeUtils.getSegments(p));
    }

    public final Shape getShape() {
        return path;
    }

    public final void setShape(Shape s) {
        setSegments(ShapeUtils.getSegments(s));
    }

    public final PathIterator getPathIterator() {
        return path.getPathIterator(null);
    }

    public final PathIterator getPathIterator(AffineTransform af) {
        return path.getPathIterator(af);
    }

    public final PathIterator getPathIterator(AffineTransform af, double flatness) {
        return path.getPathIterator(af, flatness);
    }

    public static GJMarker getMarker(String s, double width) {
        if (s.equals("Circle")) {
            return circle(width);
        } else if (s.equals("Square")) {
            return square(width);
        } else if (s.equals("Triangle")) {
            return triangle(width);
        } else if (s.equals("Diamond")) {
            return diamond(width);
        } else if (s.equals("Inverted Triangle")) {
            return invertedTriangle(width);
        } else if (s.equals("Left Triangle")) {
            return leftTriangle(width);
        } else if (s.equals("Right Triangle")) {
            return rightTriangle(width);
        } else {
            return dot(width);
        }
    }

    public static Path2D makeArrow(double x1, double y1, double sc) {
        Path2D thisArrow = new Path2D.Double();
        thisArrow.moveTo(x1 - x1 / sc, y1 - y1 / sc);
        thisArrow.lineTo(x1, y1);
        thisArrow.lineTo(x1 - x1 / sc, y1 + y1 / sc);
        return thisArrow;
    }

    public static Path2D makeArrow(double x1, double y1, double dx, double dy, boolean closeFlag) {
        Path2D thisArrow = new Path2D.Double();
        thisArrow.moveTo(x1 - dx, y1 - dy);
        thisArrow.lineTo(x1, y1);
        thisArrow.lineTo(x1 - dx, y1 + dy);
        if (closeFlag) {
            thisArrow.closePath();
        }
        return thisArrow;
    }

    private static GJMarker makeDot() {
        GJMarker dotMarker;
        Path2D instance = new Path2D.Double();
        Shape temp = new Rectangle2D.Double(-0.5, -0.5, 1, 1);
        instance.append(temp, false);
        dotMarker = new GJMarker(instance);
        return dotMarker;
    }

    private static Rectangle2D makeRectangle(double x, double y, double w, double h) {
        return new Rectangle2D.Double(x, y, w, h);
    }

    private static Ellipse2D makeCircle(double r) {
        return new Ellipse2D.Double(-r, -r, 2 * r, 2 * r);
    }

    private static Rectangle2D makeSquare(double r) {
        return makeRectangle(-r, -r, 2 * r, 2 * r);
    }

    private static Triangle2D makeTriangle(double r) {
        return makeTriangle(r, 0);
    }

    private static Triangle2D makeTriangle(double r, double theta) {
        double[] x = {-r, 0, r, -r};
        double centroid = r / 3;
        double[] y = {r - centroid, -r - centroid, r - centroid, r - centroid};
        return new Triangle2D(makeMarker(x, y), theta);
    }

    private static Triangle2D makeInvertedTriangle(double r) {
        return makeTriangle(r, Math.PI);
    }

    private static Triangle2D makeLeftTriangle(double r) {
        return makeTriangle(r, Math.PI * 1.5);
    }

    private static Triangle2D makeRightTriangle(double r) {
        return makeTriangle(r, Math.PI / 2);
    }

    private static Shape makeDiamond(double r) {
        Path2D tr = new Path2D.Double(makeSquare(r));
        tr.transform(AffineTransform.getRotateInstance(Math.PI / 4, 0, 0));
        return tr;
    }

    // Size in points
    public static String2D makeStop(int size) {
        return makeCharMarker(".", size);
    }

    public static String2D makePlus(int size) {
        return makeCharMarker("+", size);
    }

    public static String2D makeCross(int size) {
        return makeCharMarker("x", size);
    }

    /**
     * Creates a Path2D from the string on input (typically, but not necessarily
     * a single character) using the specified point size and the "Sans Serif"
     * vitual font.
     *
     *
     * @param str
     * @param size
     * @return
     */
    public static String2D makeCharMarker(String str, int size) {
        return makeMarkerFromFont(str, size, "Serif");
    }

    public static String2D makeMarkerFromFont(String str, int size, String fontname) {
        Font f = new Font(fontname, Font.PLAIN, size);
        FontRenderContext context = new java.awt.font.FontRenderContext(null, true, false);
        GlyphVector vec = f.createGlyphVector(context, str);
        Shape s = vec.getOutline(0, 0);
        return new String2D(s, 0, str);
    }

    public static Path2D makeMarker(double[] x, double[] y) {
        Path2D path = new Path2D.Double();
        path.moveTo(x[0], y[0]);
        int i;
        for (i = 1; i < x.length; i++) {
            path.lineTo(x[i], y[i]);
        }
        return path;
    }

    public static GJMarker getWidestMarker(GJCyclicArrayList p) {
        int index = -1;
        double maxWidth = 0;
        for (GJMarker m : p) {
            if (m.getBounds2D().getWidth() > maxWidth) {
                index = p.indexOf(m);
            }
        }
        return p.get(index);
    }

    public static GJMarker getTallestMarker(GJCyclicArrayList p) {
        int index = -1;
        double maxHeight = 0;
        for (GJMarker m : p) {
            if (m.getBounds2D().getHeight() > maxHeight) {
                index = p.indexOf(m);
            }
        }
        return p.get(index);
    }

    public final ArrayList getSegments() {
        ArrayList segments = new ArrayList();
        double[] d = new double[6];
        int type;
        for (PathIterator it = path.getPathIterator(null); !it.isDone(); it.next()) {
            type = it.currentSegment(d);
            segments.add(new GJPathSegmentInfo(type, d));
        }
        return segments;
    }

    public final void setSegments(ArrayList arr) {
        ((Path2D) path).reset();
        Path2D p = (Path2D) path;
        for (GJPathSegmentInfo segment : arr) {
            switch (segment.getType()) {
                case PathIterator.SEG_MOVETO:
                    p.moveTo(segment.getData()[0], segment.getData()[1]);
                    break;
                case PathIterator.SEG_LINETO:
                    p.lineTo(segment.getData()[0], segment.getData()[1]);
                    break;
                case PathIterator.SEG_QUADTO:
                    p.quadTo(segment.getData()[0], segment.getData()[1],
                            segment.getData()[2], segment.getData()[3]);
                    break;
                case PathIterator.SEG_CUBICTO:
                    p.curveTo(segment.getData()[0], segment.getData()[1],
                            segment.getData()[2], segment.getData()[3],
                            segment.getData()[4], segment.getData()[5]);
                    break;
                case PathIterator.SEG_CLOSE:
                    p.closePath();
            }
        }
    }

    public static Path2D makePath(ArrayList arr) {
        Path2D p = new Path2D.Double();
        for (GJPathSegmentInfo segment : arr) {
            switch (segment.getType()) {
                case PathIterator.SEG_MOVETO:
                    p.moveTo(segment.getData()[0], segment.getData()[1]);
                    break;
                case PathIterator.SEG_LINETO:
                    p.lineTo(segment.getData()[0], segment.getData()[1]);
                    break;
                case PathIterator.SEG_QUADTO:
                    p.quadTo(segment.getData()[0], segment.getData()[1],
                            segment.getData()[2], segment.getData()[3]);
                    break;
                case PathIterator.SEG_CUBICTO:
                    p.curveTo(segment.getData()[0], segment.getData()[1],
                            segment.getData()[2], segment.getData()[3],
                            segment.getData()[4], segment.getData()[5]);
                    break;
                case PathIterator.SEG_CLOSE:
                    p.closePath();
            }
        }
        return p;
    }
}

 

Ads help maintain this website.