/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.pepsoft.worldpainter.util;
import org.pepsoft.util.MathUtils;
/**
*
* @author pepijn
*/
public final class GeometryUtil {
private GeometryUtil() {
// Prevent instantiation
}
/**
* Visit all the points along the outline of a circle in an integer
* coordinate space with the centre at 0,0. The order in which the points
* are visited is not defined. The visitor may abort the process at any
* point by returning <code>false</code>.
*
* @param radius The radius of the circle to visit.
* @param visitor The visitor to invoke for each point.
* @return <code>true</code> if the visitor returned true for each point
* (and therefore every point was visited). <code>false</code> if the
* visitor returned <code>false</code> for some point and the process
* was aborted.
*/
public static boolean visitCircle(int radius, PlaneVisitor visitor) {
int dx = radius, dy = 0;
int radiusError = 1 - dx;
while (dx >= dy) {
if (! visitor.visit( dx, dy, radius)) {return false;}
if (! visitor.visit(-dx, dy, radius)) {return false;}
if (! visitor.visit(-dx, -dy, radius)) {return false;}
if (! visitor.visit( dx, -dy, radius)) {return false;}
if (dx != dy) {
if (! visitor.visit( dy, dx, radius)) {return false;}
if (! visitor.visit(-dy, dx, radius)) {return false;}
if (! visitor.visit(-dy, -dx, radius)) {return false;}
if (! visitor.visit( dy, -dx, radius)) {return false;}
}
dy++;
if (radiusError < 0) {
radiusError += 2 * dy + 1;
} else {
dx--;
radiusError += 2 * (dy - dx + 1);
}
}
return true;
}
/**
* Visit all the points on the face of a filled circular disk in an integer
* coordinate space with the centre at 0,0. The order in which the points
* are visited is not defined. The visitor may abort the process at any
* point by returning <code>false</code>.
*
* @param radius The radius of the circle to visit.
* @param visitor The visitor to invoke for each point.
* @return <code>true</code> if the visitor returned true for each point
* (and therefore every point was visited). <code>false</code> if the
* visitor returned <code>false</code> for some point and the process
* was aborted.
*/
public static boolean visitFilledCircle(int radius, PlaneVisitor visitor) {
int dx = radius, dy = 0;
int radiusError = 1 - dx;
while (dx >= dy) {
if (! visitor.visit(0, -dy, dy)) {return false;}
if ((dy > 0) && (! visitor.visit(0, dy, dy))) {return false;}
for (int i = 1; i <= dx; i++) {
final float d = MathUtils.getDistance(i, dy);
if (! visitor.visit(-i, -dy, d)) {return false;}
if (! visitor.visit(-i, dy, d)) {return false;}
if (! visitor.visit(i, -dy, d)) {return false;}
if (! visitor.visit(i, dy, d)) {return false;}
}
if (dx > 0) {
if (! visitor.visit(0, -dx, dx)) {return false;}
if (! visitor.visit(0, dx, dx)) {return false;}
}
for (int i = 1; i <= dy; i++) {
final float d = MathUtils.getDistance(i, dx);
if (! visitor.visit(-i, -dx, d)) {return false;}
if (! visitor.visit(-i, dx, d)) {return false;}
if (! visitor.visit(i, -dx, d)) {return false;}
if (! visitor.visit(i, dx, d)) {return false;}
}
dy++;
if (radiusError < 0) {
radiusError += 2 * dy + 1;
} else {
dx--;
radiusError += 2 * (dy - dx + 1);
}
}
return true;
}
/**
* Visit all the points inside a spherical volume in an integer coordinate
* space with the centre at 0,0,0. The order in which the points are visited
* is not defined. The visitor may abort the process at any point by
* returning <code>false</code>.
*
* @param radius The radius of the sphere to visit.
* @param visitor The visitor to invoke for each point.
* @return <code>true</code> if the visitor returned true for each point
* (and therefore every point was visited). <code>false</code> if the
* visitor returned <code>false</code> for some point and the process
* was aborted.
*/
public static boolean visitFilledSphere(int radius, VolumeVisitor visitor) {
for (int dz = 0; dz <= radius; dz++) {
int r = (int) (Math.sqrt(radius * radius - dz * dz) + 0.5);
final int finalDz = dz;
if (! visitFilledCircle(r, ((dx, dy, d) -> visitor.visit(dx, dy, finalDz, MathUtils.getDistance(dx, dy, finalDz))))) {
return false;
}
if ((dz > 0) && (! visitFilledCircle(r, ((dx, dy, d) -> visitor.visit(dx, dy, -finalDz, MathUtils.getDistance(dx, dy, -finalDz)))))) {
return false;
}
}
return true;
}
@FunctionalInterface
public interface PlaneVisitor {
/**
* Visit the specified location relative to the origin of the plane.
*
* @param dx The x coordinate to visit relative to the origin of the
* plane.
* @param dy The y coordinate to visit relative to the origin of the
* plane.
* @param d The distance from the origin.
* @return <code>true</code> if the process should continue;
* <code>false</code> if no more points should be visited on the plane.
*/
boolean visit(int dx, int dy, float d);
}
@FunctionalInterface
public interface VolumeVisitor {
/**
* Visit the specified location relative to the origin of the volume.
*
* @param dx The x coordinate to visit relative to the origin of the
* volume.
* @param dy The y coordinate to visit relative to the origin of the
* volume.
* @param dz The z coordinate to visit relative to the origin of the
* volume.
* @param d The distance from the origin.
* @return <code>true</code> if the process should continue;
* <code>false</code> if no more points should be visited in the volume.
*/
boolean visit(int dx, int dy, int dz, float d);
}
}