/*******************************************************************************
* Copyright 2010 Simon Mieth
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.kabeja.entities.util;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.kabeja.entities.Hatch;
import org.kabeja.math.Bounds;
import org.kabeja.math.MathUtils;
import org.kabeja.math.ParametricLine;
import org.kabeja.math.Point3D;
import org.kabeja.math.Vector;
/**
* @author <a href="mailto:simon.mieth@gmx.de>Simon Mieth</a>
*
*/
public class HatchLineIterator implements Iterator<HatchLineSegment>{
public static final double LIMIT = 0.00001;
protected double angle;
protected Bounds hatchBounds;
protected HatchLineFamily pattern;
protected double length;
protected Vector v;
protected Vector r;
protected List bounderyEdges;
protected ParametricLine patternLine;
protected double tmin = Double.POSITIVE_INFINITY;
protected double tmax = Double.NEGATIVE_INFINITY;
protected double walkingLength;
protected double currentWalkingStep = 0;
public HatchLineIterator(Hatch hatch, HatchLineFamily pattern) {
this.angle = Math.toRadians(pattern.getRotationAngle());
this.hatchBounds = hatch.getBounds();
this.length = pattern.getLength();
this.bounderyEdges = new ArrayList();
// edge 0
Point3D start = new Point3D(this.hatchBounds.getMinimumX(),
this.hatchBounds.getMaximumY(), 0);
Point3D end = new Point3D(this.hatchBounds.getMinimumX(),
this.hatchBounds.getMinimumY(), 0);
this.bounderyEdges.add(new ParametricLine(start,
MathUtils.getVector(start, end)));
// edge 1
start = new Point3D(this.hatchBounds.getMinimumX(),
this.hatchBounds.getMinimumY(), 0);
end = new Point3D(this.hatchBounds.getMaximumX(),
this.hatchBounds.getMinimumY(), 0);
this.bounderyEdges.add(new ParametricLine(start,
MathUtils.getVector(start, end)));
// edge 2
start = new Point3D(this.hatchBounds.getMaximumX(),
this.hatchBounds.getMinimumY(), 0);
end = new Point3D(this.hatchBounds.getMaximumX(),
this.hatchBounds.getMaximumY(), 0);
this.bounderyEdges.add(new ParametricLine(start,
MathUtils.getVector(start, end)));
// edge 3
start = new Point3D(this.hatchBounds.getMaximumX(),
this.hatchBounds.getMaximumY(), 0);
end = new Point3D(this.hatchBounds.getMinimumX(),
this.hatchBounds.getMaximumY(), 0);
this.bounderyEdges.add(new ParametricLine(start,
MathUtils.getVector(start, end)));
this.pattern = pattern;
this.initialize();
}
public boolean hasNext() {
return this.currentWalkingStep <= this.walkingLength;
}
protected void initialize() {
// setup a length
// this can happen on solid lines
if (this.length == 0) {
this.length = 1;
}
// first get the center point of the bound rectangle
Point3D center = new Point3D();
center.setX(this.hatchBounds.getMinimumX() +
(this.hatchBounds.getWidth() / 2));
center.setY(this.hatchBounds.getMinimumY() +
(this.hatchBounds.getHeight() / 2));
center.setZ(0);
this.r = new Vector();
if (Math.abs(this.pattern.getOffsetY()) < LIMIT) {
this.r.setY(0);
} else {
this.r.setY(this.pattern.getOffsetY());
}
if (Math.abs(this.pattern.getOffsetX()) < LIMIT) {
this.r.setX(0);
} else {
this.r.setX(this.pattern.getOffsetX());
}
// create the direction vector of the line family
this.v = new Vector();
this.v.setX(this.length * Math.cos(this.angle));
this.v.setY(this.length * Math.sin(this.angle));
if (Math.abs(this.v.getX()) < LIMIT) {
this.v.setX(0);
}
if (Math.abs(this.v.getY()) < LIMIT) {
this.v.setY(0);
}
// we will now find the next raster point near the center point
double[] para = this.getRasterValues(center.getX(), center.getY());
center = this.getPoint(Math.round(para[0]), Math.round(para[1]));
// we create now our walking line
this.patternLine = new ParametricLine(center, this.r);
this.calculateIntersection(this.hatchBounds.getMinimumX(),
this.hatchBounds.getMaximumY());
this.calculateIntersection(this.hatchBounds.getMinimumX(),
this.hatchBounds.getMinimumY());
this.calculateIntersection(this.hatchBounds.getMaximumX(),
this.hatchBounds.getMinimumY());
this.calculateIntersection(this.hatchBounds.getMaximumX(),
this.hatchBounds.getMaximumY());
// the minimum point is our starting point
this.tmin = Math.floor(this.tmin);
this.tmax = Math.ceil(this.tmax);
Point3D p = this.patternLine.getPointAt(this.tmin);
this.patternLine.setStartPoint(p);
this.walkingLength = Math.ceil(Math.abs(this.tmax - this.tmin));
}
protected void calculateIntersection(double x, double y) {
Point3D s = new Point3D(x, y, 0);
ParametricLine line = new ParametricLine(s, this.v);
double t = this.patternLine.getIntersectionParameter(line);
if (t < this.tmin) {
this.tmin = t;
}
if (t > this.tmax) {
this.tmax = t;
}
}
/**
* calculate the m and n raster values of a given point.
*
* @return the raster values, where v[0]=m and v[1]=n
*/
protected double[] getRasterValues(double x, double y) {
double[] v = new double[2];
if (this.r.getX() == 0.0) {
v[0] = (x - this.pattern.getBaseX()) / this.v.getX();
v[1] = (y - this.pattern.getBaseY() - (this.v.getY() * v[0])) / this.r.getY();
} else if (this.r.getY() == 0.0) {
v[0] = (y - this.pattern.getBaseY()) / this.v.getY();
v[1] = (x - this.pattern.getBaseX()) / this.r.getX();
} else if (this.v.getX() == 0) {
v[1] = (x - this.pattern.getBaseX()) / this.r.getX();
v[0] = (y - this.pattern.getBaseY() - (this.r.getY() * v[1])) / this.v.getY();
} else if (this.v.getY() == 0.0) {
v[1] = (y - this.pattern.getBaseY()) / this.r.getY();
v[0] = (x - this.pattern.getBaseX() - (this.r.getX() * v[1])) / this.v.getX();
} else {
// a helper variable
double a = this.r.getY() / this.r.getX();
v[0] = (y - this.pattern.getBaseY() - (x * a) +
(this.pattern.getBaseX() * a)) / (this.v.getY() -
(a * this.v.getX()));
v[1] = (x - this.pattern.getBaseX() - (this.v.getX() * v[0])) / this.r.getX();
}
return v;
}
public HatchLineSegment next() {
Point3D p = this.patternLine.getPointAt(this.currentWalkingStep);
ParametricLine line = new ParametricLine(p, this.v);
// get the next intersection of
Iterator i = this.bounderyEdges.iterator();
List points = new ArrayList();
while (i.hasNext()) {
ParametricLine edge = (ParametricLine) i.next();
double t = edge.getIntersectionParameter(line);
if ((t >= 0) && (t < 1)) {
points.add(edge.getPointAt(t));
}
}
double startL = 0;
double l = 0;
if (points.size() == 2) {
Point3D start = (Point3D) points.get(0);
double startT = line.getParameter(start);
Point3D end = (Point3D) points.get(1);
double endT = line.getParameter(end);
startL = 0;
if (startT > endT) {
line.setStartPoint(end);
startL = Math.abs(endT - Math.floor(endT)) * this.length;
} else {
line.setStartPoint(start);
startL = Math.abs(startT - Math.floor(startT)) * this.length;
}
l = Math.abs(endT - startT) * this.length;
}
line.setDirectionVector(MathUtils.normalize(this.v));
HatchLineSegment segment = new HatchLineSegment(line, l, startL,
this.pattern.getPattern());
this.currentWalkingStep++;
return segment;
}
public void remove() {
// we do nothing here
}
protected Point3D getPoint(double m, double n) {
Point3D p = new Point3D();
p.setX((n * this.r.getX()) + this.pattern.getBaseX() +
(this.v.getX() * m));
p.setY((n * this.r.getY()) + this.pattern.getBaseY() +
(this.v.getY() * m));
return p;
}
}