/****************************************************************************
* Copyright (c) 2008, 2009 Jeremy Dowdall
* 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:
* Jeremy Dowdall <jeremyd@aspencloud.com> - initial API and implementation
*****************************************************************************/
package org.eclipse.nebula.cwt.svg;
import static java.lang.Math.abs;
import static java.lang.Math.max;
import static java.lang.Math.min;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Path;
import org.eclipse.swt.graphics.PathData;
import org.eclipse.swt.graphics.Transform;
/**
* An SvgShape is a graphical svg element which can be directly applied
* to a given graphics context.<br>
* Shapes consist of:
* <ul>
* <li>circle</li>
* <li>ellipse</li>
* <li>line</li>
* <li>polygon</li>
* <li>polyline</li>
* <li>rectangle</li>
* <li>path</li>
* </ul>
* <p>See also:
* <a href="http://www.w3.org/TR/SVG/shapes.html">http://www.w3.org/TR/SVG/shapes.html</a></p>
*/
public class SvgShape extends SvgGraphic {
private Path path;
PathData pathData;
SvgShape(SvgContainer container, String id) {
super(container, id);
}
private void doApply(GC gc) {
gc.setAntialias(SWT.ON);
SvgFill derivedFill = getFill();
SvgStroke derivedStroke = getStroke();
Transform derivedTransform = getTransform(gc);
Transform bak = null;
if(!derivedTransform.isIdentity()) {
bak = new Transform(gc.getDevice());
gc.getTransform(bak);
gc.setTransform(derivedTransform);
}
if(derivedFill.isPaintable()) {
derivedFill.create(gc);
derivedFill.apply();
doFill(gc);
derivedFill.dispose();
}
if(derivedStroke.isPaintable()) {
derivedStroke.create(gc);
derivedStroke.apply();
doStroke(gc);
derivedStroke.dispose();
}
if(bak != null) {
gc.setTransform(bak);
}
derivedTransform.dispose();
}
public void apply(GC gc) {
if(pathData.types != null) {
path = new Path(gc.getDevice(), pathData);
}
doApply(gc);
if(path != null) {
path.dispose();
path = null;
}
}
/**
* Returns whether or not the given point is contained by this shape.
* @param x
* @param y
* @param gc
* @param outline
* @return true if the given point is contained, false otherwise
* @see Path#contains(float, float, GC, boolean)
*/
public boolean contains(float x, float y, GC gc, boolean outline) {
Transform t = new Transform(gc.getDevice());
gc.getTransform(t);
t.invert();
float[] pts = new float[] { x, y };
t.transform(pts);
t.dispose();
return path.contains(pts[0], pts[1], gc, outline);
}
private void doFill(GC gc) {
if(path != null) {
gc.fillPath(path);
} else if(pathData.points.length == 4) {
int w = (int) (2 * pathData.points[2]);
int h = (int) (2 * pathData.points[3]);
if(w > 0 && h > 0) {
int x = (int) (pathData.points[0]-pathData.points[2]);
int y = (int) (pathData.points[1]-pathData.points[3]);
gc.fillOval(x, y, w, h);
}
} else if(pathData.points.length == 6) {
int w = (int) pathData.points[2];
int h = (int) pathData.points[3];
if(w > 0 && h > 0) {
int x = (int) pathData.points[0];
int y = (int) pathData.points[1];
int rx = (int) pathData.points[4];
int ry = (int) pathData.points[5];
if(rx > 0 || ry > 0) {
gc.fillRoundRectangle(x, y, w, h, getRadiusX(), getRadiusY());
} else {
gc.fillRectangle(x, y, w, h);
}
}
}
}
private void doStroke(GC gc) {
if(path != null) {
gc.drawPath(path);
} else if(pathData.points.length == 4) {
int w = (int) (2 * pathData.points[2]);
int h = (int) (2 * pathData.points[3]);
if(w > 0 && h > 0) {
int x = (int) (pathData.points[0]-pathData.points[2]);
int y = (int) (pathData.points[1]-pathData.points[3]);
gc.drawOval(x, y, w, h);
}
} else if(pathData.points.length == 6) {
int w = (int) pathData.points[2];
int h = (int) pathData.points[3];
if(w > 0 && h > 0) {
int x = (int) pathData.points[0];
int y = (int) pathData.points[1];
int rx = (int) pathData.points[4];
int ry = (int) pathData.points[5];
if(rx > 0 || ry > 0) {
gc.drawRoundRectangle(x, y, w, h, getRadiusX(), getRadiusY());
} else {
gc.drawRectangle(x, y, w, h);
}
}
}
}
float[] getBounds() {
if(path != null) {
// TODO Path#getBounds
float minx = Float.POSITIVE_INFINITY;
float miny = Float.POSITIVE_INFINITY;
float maxx = Float.NEGATIVE_INFINITY;
float maxy = Float.NEGATIVE_INFINITY;
for(int i = 0; i < pathData.points.length - 1; i++) {
minx = min(minx, pathData.points[i]);
maxx = max(maxx, pathData.points[i]);
i++;
miny = min(miny, pathData.points[i]);
maxy = max(maxy, pathData.points[i]);
}
return new float[] { minx, miny, abs(maxx - minx), abs(maxy - miny) };
} else if(pathData.points.length == 4) {
int x = (int) (pathData.points[0]-pathData.points[2]);
int y = (int) (pathData.points[1]-pathData.points[3]);
int w = (int) (2 * pathData.points[2]);
int h = (int) (2 * pathData.points[3]);
return new float[] { x, y, w, h };
} else if(pathData.points.length == 6) {
int x = (int) pathData.points[0];
int y = (int) pathData.points[1];
int w = (int) pathData.points[2];
int h = (int) pathData.points[3];
return new float[] { x, y, w, h };
}
throw new UnsupportedOperationException();
}
private int getRadiusX() {
if(pathData.points[4] > 0) {
return (int) (2 * pathData.points[4]);
}
if(pathData.points[5] > 0) {
return getRadiusY();
}
return 0;
}
private int getRadiusY() {
if(pathData.points[5] > 0) {
return (int) (2 * pathData.points[5]);
}
if(pathData.points[4] > 0) {
return getRadiusX();
}
return 0;
}
}