/******************************************************************************* * Copyright (c) 2011 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.diagram.connection.rendering.arrows; import java.awt.Color; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.geom.Path2D; import java.io.Serializable; import java.util.StringTokenizer; /** * @author Tuukka Lehtonen */ public class ArrowLineEndStyle implements ILineEndStyle, Serializable { private static final long serialVersionUID = -346653685430483245L; public static enum ArrowType { None, Stroke, Fill } public static final double DEFAULT_LENGTH = 8.0; public static final double DEFAULT_WIDTH = 4.0; public static final double DEFAULT_SPACE = 0.0; protected ArrowType type; protected double lineEndLength; protected double length; protected double width; protected double space; protected Color color; protected transient Path2D path; public ArrowLineEndStyle() { this(DEFAULT_LENGTH, DEFAULT_WIDTH, DEFAULT_SPACE, null); } public ArrowLineEndStyle(double length, double width, double space) { this(length, width, space, null); } public ArrowLineEndStyle(double length, double width, double space, Color color) { this.type = ArrowType.Fill; this.length = length; this.width = width; this.space = space; this.color = color; this.path = arrow(length, width, space); } public ArrowLineEndStyle(String desc) { this.type = ArrowType.None; this.lineEndLength = 0.0; double l = DEFAULT_LENGTH; double w = DEFAULT_WIDTH; double s = DEFAULT_SPACE; StringTokenizer tokenizer = new StringTokenizer(desc); if (tokenizer.hasMoreTokens()) { String type = tokenizer.nextToken(); this.type = parseType(type); if (tokenizer.hasMoreTokens()) { String ls = tokenizer.nextToken(); l = parseSize(ls, length); if (tokenizer.hasMoreTokens()) { String ws = tokenizer.nextToken(); w = parseSize(ws, width); if (tokenizer.hasMoreTokens()) { String ss = tokenizer.nextToken(); s = parseSize(ss, space); } } } if (this.type != ArrowType.None) { this.path = arrow(l, w, s); lineEndLength = l+s; } } this.width = w; this.length = l; this.space = s; } @Override public void render(Graphics2D g, double x, double y, int dir) { if (type == ArrowType.None || path == null) return; AffineTransform old = g.getTransform(); g.translate(x, y); g.rotate(dir*Math.PI*0.5); if(color != null) g.setColor(color); switch (type) { case Fill: g.fill(path); break; case Stroke: g.draw(path); break; } g.setTransform(old); } @Override public double getLineEndLength(int direction) { return lineEndLength; } private static Path2D arrow(double length, double width, double space) { Path2D.Double path = new Path2D.Double(); path.moveTo(-space, 0); path.lineTo(-length-space, -width); path.lineTo(-length-space, +width); path.closePath(); return path; } private static double parseSize(String size, double defaultValue) { try { return Double.parseDouble(size); } catch (NumberFormatException e) { return defaultValue; } } private static ArrowType parseType(String type) { String lower = type.toLowerCase(); if ("none".equals(lower)) return ArrowType.None; if ("stroke".equals(lower)) return ArrowType.Stroke; if ("fill".equals(lower)) return ArrowType.Fill; throw new IllegalArgumentException("unrecognized arrow type: " + type); } @Override public String toString() { return getClass().getSimpleName() + "@" + System.identityHashCode(this) + "[" + type + ", w=" + width + ", l=" + length + ", s=" + space + ", lel=" + lineEndLength + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; long temp; temp = Double.doubleToLongBits(length); result = prime * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(lineEndLength); result = prime * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(space); result = prime * result + (int) (temp ^ (temp >>> 32)); result = prime * result + ((type == null) ? 0 : type.hashCode()); temp = Double.doubleToLongBits(width); result = prime * result + (int) (temp ^ (temp >>> 32)); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; ArrowLineEndStyle other = (ArrowLineEndStyle) obj; if (Double.doubleToLongBits(length) != Double.doubleToLongBits(other.length)) return false; if (Double.doubleToLongBits(lineEndLength) != Double.doubleToLongBits(other.lineEndLength)) return false; if (Double.doubleToLongBits(space) != Double.doubleToLongBits(other.space)) return false; if (type != other.type) return false; if (Double.doubleToLongBits(width) != Double.doubleToLongBits(other.width)) return false; return true; } }