/**
* Squidy Interaction 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 3 of the License,
* or (at your option) any later version.
*
* Squidy Interaction 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 Squidy Interaction Library. If not, see
* <http://www.gnu.org/licenses/>.
*
* 2009 Human-Computer Interaction Group, University of Konstanz.
* <http://hci.uni-konstanz.de>
*
* Please contact info@squidy-lib.de or visit our website
* <http://www.squidy-lib.de> for further information.
*/
package org.squidy.designer.paint;
import java.awt.BasicStroke;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.FlatteningPathIterator;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
/**
* <code>SketchyStroke</code>.
*
* <pre>
* Date: Jul 15, 2009
* Time: 9:37:37 PM
* </pre>
*
*
* @author Roman R�dle <a
* href="mailto:Roman.Raedle@uni-konstanz.de">Roman.Raedle
* @uni-konstanz.de</a> Human-Computer Interaction Group University of
* Konstanz
*
* @version $Id: SketchyStroke.java 772 2011-09-16 15:39:44Z raedle $
* @since 1.0.0
*/
public class SketchyStroke implements Stroke {
private float detail = 2;
private float amplitude = 2;
private static final float FLATNESS = 1;
private float thickness;
public SketchyStroke(float thickness, float detail, float amplitude) {
this.thickness = thickness;
this.detail = detail;
this.amplitude = amplitude;
}
/**
* @param shape
* @return
*/
public Shape createStrokedShape(Shape shape) {
GeneralPath result = new GeneralPath();
shape = new BasicStroke(thickness).createStrokedShape(shape);
PathIterator it = new FlatteningPathIterator(shape.getPathIterator(null), FLATNESS);
float points[] = new float[6];
float moveX = 0, moveY = 0;
float lastX = 0, lastY = 0;
float thisX = 0, thisY = 0;
int type = 0;
boolean first = false;
float next = 0;
while (!it.isDone()) {
type = it.currentSegment(points);
switch (type) {
case PathIterator.SEG_MOVETO:
moveX = lastX = randomize(points[0]);
moveY = lastY = randomize(points[1]);
result.moveTo(moveX, moveY);
first = true;
next = 0;
break;
case PathIterator.SEG_CLOSE:
points[0] = moveX;
points[1] = moveY;
// Fall into....
case PathIterator.SEG_LINETO:
thisX = randomize(points[0]);
thisY = randomize(points[1]);
float dx = thisX - lastX;
float dy = thisY - lastY;
float distance = (float) Math.sqrt(dx * dx + dy * dy);
if (distance >= next) {
float r = 1.0f / distance;
while (distance >= next) {
float x = lastX + next * dx * r;
float y = lastY + next * dy * r;
result.lineTo(randomize(x), randomize(y));
next += detail;
}
}
next -= distance;
first = false;
lastX = thisX;
lastY = thisY;
break;
}
it.next();
}
return result;
}
private float runner = 0.0f;
private boolean up = true;
/**
* @param x
* @return
*/
private float randomize(float x) {
// return x + (float) Math.random() * amplitude * 2 - 1;
if (up) {
runner += 0.1f;
}
else {
runner -= 0.1f;
}
if (runner > 1.0f) {
runner = 1.0f;
up = false;
}
else if (runner < 0.0f) {
runner = 0.0f;
up = true;
}
return x + (float) runner * amplitude * 2 - 1;
}
}