/*
* Scriptographer
*
* This file is part of Scriptographer, a Scripting Plugin for Adobe Illustrator
* http://scriptographer.org/
*
* Copyright (c) 2002-2010, Juerg Lehni
* http://scratchdisk.com/
*
* All rights reserved. See LICENSE file for details.
*
* File created on Mar 21, 2008.
*/
package com.scriptographer.ai;
import java.awt.BasicStroke;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.Area;
import java.awt.geom.GeneralPath;
import java.util.ArrayList;
import com.scratchdisk.list.List;
/**
* @author lehni
*/
public abstract class PathItem extends Item {
/**
* Wraps an AIArtHandle in a Path object
*/
protected PathItem(int handle, int docHandle, boolean created) {
super(handle, docHandle, created);
}
/**
* Creates a path object of the given type. Used by CompoundPath
*/
protected PathItem(short type) {
super(type);
}
/**
* Converts to a Java2D shape.
*
* @jshide
*/
public abstract GeneralPath toShape();
/*
* PostScript-like interface: moveTo, lineTo, curveTo, arcTo
*/
/**
* {@grouptitle PostScript-style drawing commands}
*/
public void moveTo(Point point) {
if (point == null) moveTo(0, 0);
else moveTo(point.x, point.y);
}
public abstract void moveTo(double x, double y);
public void lineTo(Point point) {
if (point == null) lineTo(0, 0);
else lineTo(point.x, point.y);
}
public abstract void lineTo(double x, double y);
public void cubicCurveTo(Point handle1, Point handle2, Point to) {
cubicCurveTo(handle1 != null ? handle1.x : 0,
handle1 != null ? handle1.y : 0,
handle2 != null ? handle2.x : 0,
handle2 != null ? handle2.y : 0,
to != null ? to.x : 0,
to != null ? to.y : 0);
}
public abstract void cubicCurveTo(double handle1X, double handle1Y,
double handle2X, double handle2Y, double toX, double toY);
/**
* @deprecated
*/
public void curveTo(Point handle1, Point handle2, Point to) {
cubicCurveTo(handle1, handle2, to);
}
/**
* @deprecated
*/
public void curveTo(double handle1X, double handle1Y,
double handle2X, double handle2Y, double toX, double toY) {
cubicCurveTo(handle1X, handle1Y, handle2X, handle2Y, toX, toY);
}
public void quadraticCurveTo(Point handle, Point to) {
quadraticCurveTo(handle != null ? handle.x : 0,
handle != null ? handle.y : 0,
to != null ? to.x : 0,
to != null ? to.y : 0);
}
public abstract void quadraticCurveTo(double handleX, double handleY,
double toX, double toY);
/**
* @deprecated
*/
public void quadTo(Point handle, Point to) {
quadraticCurveTo(handle, to);
}
/**
* @deprecated
*/
public void quadTo(double handleX, double handleY, double toX, double toY) {
quadraticCurveTo(handleX, handleY, toX, toY);
}
public void curveTo(Point through, Point to, double parameter) {
curveTo(through != null ? through.x : 0,
through != null ? through.y : 0,
to != null ? to.x : 0,
to != null ? to.y : 0,
parameter);
}
public void curveTo(Point through, Point to) {
curveTo(through, to, 0.5);
}
public abstract void curveTo(double throughX, double throughY,
double toX, double toY, double t);
public void curveTo(double throughX, double throughY,
double toX, double toY) {
curveTo(throughX, throughY, toX, toY, 0.5);
}
/**
* @deprecated
*/
public void curveThrough(Point through, Point to, double parameter) {
curveTo(through, to, parameter);
}
/**
* @deprecated
*/
public void curveThrough(double throughX, double throughY,
double toX, double toY, double parameter) {
curveTo(throughX, throughY, toX, toY, parameter);
}
/**
* @deprecated
*/
public void curveThrough(Point through, Point to) {
curveTo(through, to);
}
/**
* @deprecated
*/
public void curveThrough(double throughX, double throughY,
double toX, double toY) {
curveTo(throughX, throughY, toX, toY);
}
public void arcTo(Point point, boolean clockwise) {
arcTo(point != null ? point.x : 0,
point != null ? point.y : 0,
clockwise);
}
public void arcTo(Point point) {
arcTo(point, true);
}
public abstract void arcTo(double x, double y, boolean clockwise);
public void arcTo(double x, double y) {
arcTo(x, y, true);
}
public void arcTo(Point through, Point to) {
arcTo(through != null ? through.x : 0,
through != null ? through.y : 0,
to != null ? to.x : 0,
to != null ? to.y : 0);
}
public abstract void arcTo(double throughX, double throughY,
double toX, double toY);
/**
* @deprecated
*/
public void arcThrough(Point through, Point to) {
arcTo(through.x, through.y, to.x, to.y);
}
/**
* @deprecated
*/
public void arcThrough(double throughX, double throughY, double toX,
double toY) {
arcTo(throughX, throughY, toX, toY);
}
public void lineBy(Point vector) {
if (vector != null)
lineBy(vector.x, vector.y);
}
public abstract void lineBy(double x, double y);
public void curveBy(Point throughVector, Point toVector, double parameter) {
curveBy(throughVector != null ? throughVector.x : 0,
throughVector != null ? throughVector.y : 0,
toVector != null ? toVector.x : 0,
toVector != null ? toVector.y : 0,
parameter);
}
public void curveBy(Point throughVector, Point toVector) {
curveBy(throughVector, toVector, 0.5);
}
public abstract void curveBy(double throughX, double throughY,
double toX, double toY, double parameter);
public void curveBy(double throughX, double throughY,
double toX, double toY) {
curveBy(throughX, throughY, toX, toY, 0.5);
}
public void arcBy(Point vector, boolean clockwise) {
arcBy(vector != null ? vector.x : 0,
vector != null ? vector.y : 0,
clockwise);
}
public void arcBy(Point vector) {
arcBy(vector, true);
}
public abstract void arcBy(double x, double y, boolean clockwise);
public void arcBy(double x, double y) {
arcBy(x, y, true);
}
public void arcBy(Point throughVector, Point toVector) {
arcBy(throughVector != null ? throughVector.x : 0,
throughVector != null ? throughVector.y : 0,
toVector != null ? toVector.x : 0,
toVector != null ? toVector.y : 0);
}
public abstract void arcBy(double throughX, double throughY,
double toX, double toY);
/**
* Closes the path. If it is closed, Illustrator connects the first and last
* segments.
*/
public abstract void closePath();
/**
* {@grouptitle Geometric Tests}
*
* Checks if the interior of the path intersects with the interior of the
* specified path.
*
* @param item
* @return {@true if the paths intersect}
*/
public boolean intersects(PathItem item) {
Area area = new Area(this.toShape());
area.intersect(new Area(item.toShape()));
return !area.isEmpty();
}
/**
* Checks if the interior of the path contains the interior of the specified
* path.
*
* @param item
* @return {@true if the path contains the specified path}
*/
public boolean contains(PathItem item) {
Area area = new Area(item.toShape());
area.subtract(new Area(this.toShape()));
return area.isEmpty();
}
/**
* Checks if the specified point is contained within the interior of the path.
*
* @param point
* @return {@true if the point is contained within the path}
*/
public boolean contains(Point point) {
return this.toShape().contains(point.toPoint2D());
}
/**
* {@grouptitle Boolean Operations}
*
* Returns the intersection of the paths as a new path
*
* @param item
*/
public PathItem intersect(PathItem item) {
Area area = new Area(this.toShape());
area.intersect(new Area(item.toShape()));
PathItem res = document.createPathItem(area);
res.setStyle(this.getStyle());
return res;
}
/**
* Adds the shape of the specified path to the path and returns it as a new
* path.
*
* @param item
*/
public PathItem unite(PathItem item) {
Area area = new Area(this.toShape());
area.add(new Area(item.toShape()));
PathItem res = document.createPathItem(area);
res.setStyle(this.getStyle());
return res;
}
/**
* Subtracts the shape of the specified path from the path and returns it as
* a new path.
*
* @param item
*/
public PathItem exclude(PathItem item) {
Area area = new Area(this.toShape());
area.subtract(new Area(item.toShape()));
PathItem res = document.createPathItem(area);
res.setStyle(this.getStyle());
return res;
}
/**
* Returns all curves contained in the Item. For {@link Path} items this is
* the same as {@link Path#getCurves}, for {@link CompoundPath} items it
* returns the curves of all the {@link Path} items contained inside.
*/
protected abstract List<Curve> getAllCurves();
/**
* Returns all interesections between two {@link Path} items in an array of
* {@link CurveLocation} objects. {@link CompoundPath} items are support
* too.
*/
public CurveLocation[] getIntersections(PathItem path) {
// First check the bounds of the two paths. If they don't intersect,
// we don't need to iterate through the whole path.
if (!getBounds().intersects(path.getBounds()))
return new CurveLocation[0];
ArrayList<CurveLocation> locations = new ArrayList<CurveLocation>();
List<Curve> curves1 = getAllCurves(), curves2 = path.getAllCurves();
int length1 = curves1.size(), length2 = curves2.size();
// Convert curves2 to curve values, as we're looping through them for
// each curve in curves1.
double[][][] curvesValues2 = new double[length2][][];
for (int i = 0; i < length2; i++)
curvesValues2[i] = curves2.get(i).getCurveValues();
// Now loop through each curve in curves1 and get intersections with
// the curves in curves2.
for (int i = 0; i < length1; i++) {
Curve curve = curves1.get(i);
double[][] curveValues = curve.getCurveValues();
for (int j = 0; j < length2; j++)
Curve.getIntersections(curve, curveValues, curvesValues2[j],
locations);
}
return locations.toArray(new CurveLocation[locations.size()]);
}
/**
* Draws the path's content into a Graphics2D object. Useful for
* conversions.
*
* @jshide
*/
public void paint(Graphics2D graphics) {
Shape shape = toShape();
Color fillColor = getFillColor();
Color strokeColor = getStrokeColor();
if (fillColor != Color.NONE) {
graphics.setColor(fillColor.toAWTColor());
graphics.fill(shape);
}
if (strokeColor != Color.NONE) {
float[] dashArray = getDashArray();
if (dashArray.length == 0)
dashArray = null;
graphics.setColor(strokeColor.toAWTColor());
graphics.setStroke(new BasicStroke(getStrokeWidth(),
getStrokeCap().value, getStrokeJoin().value,
getMiterLimit(), dashArray,
getDashOffset()));
graphics.draw(shape);
}
}
}