org.cobraparser.html.domimpl.CanvasPath2D Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Cobra Show documentation
Show all versions of Cobra Show documentation
Cobra is the rendering engine designed for LoboBrowser
/*
GNU LESSER GENERAL PUBLIC LICENSE
Copyright (C) 2015 Uproot Labs India Pvt Ltd
This library 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 2.1 of the License, or (at your option) any later version.
This library 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 library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.cobraparser.html.domimpl;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Line2D;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Path2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
public class CanvasPath2D {
private boolean needNewSubpath = true;
Path2D path2D = new Path2D.Double();
public void moveTo(final double x, final double y) {
moveToWithTransform(x, y, null);
}
void moveToWithTransform(final double x, final double y, final AffineTransform aft) {
if (aft == null) {
path2D.moveTo(x, y);
} else {
final Point2D p1 = aft.transform(new Point2D.Double(x, y), null);
path2D.moveTo(p1.getX(), p1.getY());
}
currPoint = new Point2D.Double(x, y);
needNewSubpath = false;
}
public void closePath() {
if (!needNewSubpath) {
path2D.closePath();
}
needNewSubpath = currPoint == null;
}
public void lineTo(final double x, final double y) {
lineToWithTransform(x, y, null);
}
void lineToWithTransform(final double x, final double y, final AffineTransform aft) {
if (needNewSubpath) {
ensureSubpathWithTransform(x, y, aft);
} else {
if (aft == null) {
path2D.lineTo(x, y);
} else {
final Point2D p1 = aft.transform(new Point2D.Double(x, y), null);
path2D.lineTo(p1.getX(), p1.getY());
}
}
currPoint = new Point2D.Double(x, y);
needNewSubpath = false;
}
public void quadraticCurveTo(final double x1, final double y1, final double x2, final double y2) {
quadraticCurveToWithTransform(x1, y1, x2, y2, null);
}
void quadraticCurveToWithTransform(final double x1, final double y1, final double x2, final double y2, final AffineTransform aft) {
if (aft == null) {
path2D.quadTo(x1, y1, x2, y2);
} else {
final Point2D p1 = aft.transform(new Point2D.Double(x1, y1), null);
final Point2D p2 = aft.transform(new Point2D.Double(x2, y2), null);
path2D.quadTo(p1.getX(), p1.getY(), p2.getX(), p2.getY());
}
currPoint = new Point2D.Double(x2, y2);
needNewSubpath = false;
}
public void bezierCurveTo(final double x1, final double y1, final double x2, final double y2, final double x3, final double y3) {
bezierCurveToWithTransform(x1, y1, x2, y2, x3, y3, null);
}
void bezierCurveToWithTransform(final double x1, final double y1, final double x2, final double y2, final double x3, final double y3,
final AffineTransform aft) {
if (aft == null) {
path2D.quadTo(x1, y1, x2, y2);
} else {
final Point2D p1 = aft.transform(new Point2D.Double(x1, y1), null);
final Point2D p2 = aft.transform(new Point2D.Double(x2, y2), null);
final Point2D p3 = aft.transform(new Point2D.Double(x3, y3), null);
path2D.curveTo(p1.getX(), p1.getY(), p2.getX(), p2.getY(), p3.getX(), p3.getY());
}
currPoint = new Point2D.Double(x3, y3);
needNewSubpath = false;
}
private static double tweakStart(final double start, double value, final double end) {
while (value < start) {
value += (TWO_PI);
}
while (value > end) {
value -= (TWO_PI);
}
return value;
}
private static double tweakEnd(final double start, double value, final double end) {
while (value <= start) {
value += (TWO_PI);
}
while (value > end) {
value -= (TWO_PI);
}
return value;
}
private final static double TWO_PI = 2 * Math.PI;
public void arc(final double x, final double y, final double radius, final double startAngle, final double endAngle) {
arc(x, y, radius, startAngle, endAngle, false);
}
public void arc(final double x, final double y, final double radius, final double startAngle, final double endAngle,
final boolean antiClockwise) {
arcWithTransform(x, y, radius, startAngle, endAngle, antiClockwise, null);
}
void arcWithTransform(final double x, final double y, final double radius, final double startAngle, final double endAngle,
final boolean antiClockwise, final AffineTransform aft) {
ellipseWithTransform(x, y, radius, radius, 0, startAngle, endAngle, antiClockwise, aft);
}
private void appendWithTransform(final Shape shape, final AffineTransform aft, final boolean connect) {
if (aft != null) {
final PathIterator pi = shape.getPathIterator(aft);
path2D.append(pi, connect);
} else {
path2D.append(shape, connect);
}
}
private void setCurrPoint(final AffineTransform aft) {
if (aft != null) {
try {
currPoint = aft.createInverse().transform(path2D.getCurrentPoint(), null);
} catch (final NoninvertibleTransformException e) {
throw new IllegalArgumentException(e);
}
} else {
currPoint = path2D.getCurrentPoint();
}
}
public void arcTo(final double x1, final double y1, final double x2, final double y2, final double radius) {
arcToWithTransform(x1, y1, x2, y2, radius, null);
}
void arcToWithTransform(final double x1, final double y1, final double x2, final double y2, final double radius, final AffineTransform aft) {
final Point2D p0 = ensureSubpathWithTransform(x1, y1, aft);
final Point2D p1 = new Point2D.Double(x1, y1);
final Point2D p2 = new Point2D.Double(x2, y2);
final Line2D l1 = new Line2D.Double(p0, p2);
if (p0.equals(p1) || p1.equals(p2)) {
lineToWithTransform(x1, y1, aft);
} else if (l1.contains(p1)) {
lineToWithTransform(x1, y1, aft);
} else {
final Arc2D.Double arcTo = new Arc2D.Double();
arcTo.setArcByTangent(p0, p1, p2, radius);
appendWithTransform(arcTo, aft, true);
}
}
public void ellipse(final double x, final double y, final double radiusX, final double radiusY, final double rotation,
final double startAngle, final double endAngle) {
ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, false);
}
public void ellipse(final double x, final double y, final double radiusX, final double radiusY, final double rotation,
final double startAngle, final double endAngle, final boolean antiClockwise) {
ellipseWithTransform(x, y, radiusX, radiusY, rotation, startAngle, endAngle, antiClockwise, null);
}
void ellipseWithTransform(final double x, final double y, final double radiusX, final double radiusY, final double rotation,
final double startAngle, final double endAngle, final boolean antiClockwise, final AffineTransform aft) {
final double start;
final double end;
final double extent;
final double diffAngle = antiClockwise ? (startAngle - endAngle) : (endAngle - startAngle);
if (diffAngle >= TWO_PI) {
start = 0;
end = TWO_PI;
extent = TWO_PI;
} else {
start = tweakStart(0, -startAngle % TWO_PI, TWO_PI);
end = tweakEnd(start, -endAngle % TWO_PI, TWO_PI + start);
extent = antiClockwise ? (end - start) : -(TWO_PI + (start - end));
}
final Arc2D.Double ellipse = new Arc2D.Double(x - radiusX, y - radiusY, 2 * radiusX, 2 * radiusY, Math.toDegrees(start),
Math.toDegrees(extent), Arc2D.OPEN);
AffineTransform rotatedT;
if (aft != null) {
rotatedT = new AffineTransform(aft);
rotatedT.rotate(rotation, x, y);
} else {
rotatedT = AffineTransform.getRotateInstance(rotation, x, y);
}
appendWithTransform(ellipse, rotatedT, true);
setCurrPoint(aft);
needNewSubpath = false;
}
public void rect(final double x, final double y, final double width, final double height) {
rectWithTransform(x, y, width, height, null);
}
void rectWithTransform(final double x, final double y, final double width, final double height, final AffineTransform aft) {
// Note: We can't use Rectangle2D because it doesn't support negative width, height, nor can we adjust x, y for negative
// widths / heights because the clockwise / anti-clockwise nature of the path isn't preserved
moveToWithTransform(x,y,aft);
lineToWithTransform(x+width, y, aft);
lineToWithTransform(x+width, y+height, aft);
lineToWithTransform(x, y+height, aft);
closePath();
moveToWithTransform(x, y, aft);
}
private Point2D currPoint = null;
private Point2D ensureSubpathWithTransform(final double x, final double y, final AffineTransform aft) {
if (needNewSubpath) {
moveToWithTransform(x, y, aft);
return new Point2D.Double(x, y);
} else {
return currPoint;
}
}
}