/*
* Freeplane - mind map editor
* Copyright (C) 2008 Joerg Mueller, Daniel Polansky, Christian Foltin, Dimitry Polivaev
*
* This file is modified by Dimitry Polivaev in 2008.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.freeplane.view.swing.map.link;
import java.awt.Shape;
import java.awt.geom.PathIterator;
import java.awt.geom.QuadCurve2D;
import java.awt.geom.Rectangle2D;
class PathBBox {
private static void accum(final double[] bounds, final double x, final double y) {
bounds[0] = Math.min(bounds[0], x);
bounds[1] = Math.min(bounds[1], y);
bounds[2] = Math.max(bounds[2], x);
bounds[3] = Math.max(bounds[3], y);
}
private static void accumCubic(final double bounds[], final double t, final double curx, final double cury,
final double cpx0, final double cpy0, final double cpx1, final double cpy1,
final double endx, final double endy) {
final double u = (1 - t);
final double x = curx * u * u * u + 3.0 * cpx0 * t * u * u + 3.0 * cpx1 * t * t * u + endx * t * t * t;
final double y = cury * u * u * u + 3.0 * cpy0 * t * u * u + 3.0 * cpy1 * t * t * u + endy * t * t * t;
PathBBox.accum(bounds, x, y);
}
private static void accumQuad(final double bounds[], final double t, final double curx, final double cury,
final double cpx0, final double cpy0, final double endx, final double endy) {
final double u = (1 - t);
final double x = curx * u * u + 2.0 * cpx0 * t * u + endx * t * t;
final double y = cury * u * u + 2.0 * cpy0 * t * u + endy * t * t;
PathBBox.accum(bounds, x, y);
}
private static int findCubicZeros(final double zeros[], final double cur, final double cp0, final double cp1,
final double end) {
zeros[0] = (cp0 - cur) * 3.0;
zeros[1] = (cp1 - cp0 - cp0 + cur) * 6.0;
zeros[2] = (end + (cp0 - cp1) * 3.0 - cur) * 3.0;
final int num = QuadCurve2D.solveQuadratic(zeros);
int ret = 0;
for (int i = 0; i < num; i++) {
final double t = zeros[i];
if (t > 0 && t < 1) {
zeros[ret] = t;
ret++;
}
}
return ret;
}
private static double findQuadZero(final double cur, final double cp, final double end) {
return -(cp + cp - cur - cur) / (2.0 * (cur - cp - cp + end));
}
public static Rectangle2D getBBox(final Shape s) {
boolean first = true;
final double bounds[] = new double[4];
final double coords[] = new double[6];
double curx = 0;
double cury = 0;
double movx = 0;
double movy = 0;
double cpx0, cpy0, cpx1, cpy1, endx, endy;
for (final PathIterator pi = s.getPathIterator(null); !pi.isDone(); pi.next()) {
pi.currentSegment(coords);
switch (pi.currentSegment(coords)) {
case PathIterator.SEG_MOVETO:
movx = curx = coords[0];
movy = cury = coords[1];
if (first) {
bounds[0] = bounds[2] = curx;
bounds[1] = bounds[3] = cury;
first = false;
}
else {
PathBBox.accum(bounds, curx, cury);
}
break;
case PathIterator.SEG_LINETO:
curx = coords[0];
cury = coords[1];
PathBBox.accum(bounds, curx, cury);
break;
case PathIterator.SEG_QUADTO:
cpx0 = coords[0];
cpy0 = coords[1];
endx = coords[2];
endy = coords[3];
double t = PathBBox.findQuadZero(curx, cpx0, endx);
if (t > 0 && t < 1) {
PathBBox.accumQuad(bounds, t, curx, cury, cpx0, cpy0, endx, endy);
}
t = PathBBox.findQuadZero(cury, cpy0, endy);
if (t > 0 && t < 1) {
PathBBox.accumQuad(bounds, t, curx, cury, cpx0, cpy0, endx, endy);
}
curx = endx;
cury = endy;
PathBBox.accum(bounds, curx, cury);
break;
case PathIterator.SEG_CUBICTO:
cpx0 = coords[0];
cpy0 = coords[1];
cpx1 = coords[2];
cpy1 = coords[3];
endx = coords[4];
endy = coords[5];
int num = PathBBox.findCubicZeros(coords, curx, cpx0, cpx1, endx);
for (int i = 0; i < num; i++) {
PathBBox.accumCubic(bounds, coords[i], curx, cury, cpx0, cpy0, cpx1, cpy1, endx, endy);
}
num = PathBBox.findCubicZeros(coords, cury, cpy0, cpy1, endy);
for (int i = 0; i < num; i++) {
PathBBox.accumCubic(bounds, coords[i], curx, cury, cpx0, cpy0, cpx1, cpy1, endx, endy);
}
curx = endx;
cury = endy;
PathBBox.accum(bounds, curx, cury);
break;
case PathIterator.SEG_CLOSE:
curx = movx;
cury = movy;
break;
}
}
return new Rectangle2D.Double(bounds[0], bounds[1], bounds[2] - bounds[0], bounds[3] - bounds[1]);
}
}