I want to write a recursive algorithm that highlights all possible nodes on base of dice value .
How can i do this ? Shouldn't move from null nodes .
In the Image you can see , my current node is blue and for example when the dice value is 4 I want to highlight red places. I wrote a code like this but doesn't work Thanks in advance
f(node n, dice d){
if(d == 0)
n.setDest();
if(Up node != null)
f(Up node , d-1);
if(Down node !=null)
f(Down node, d-1);
if(Right node != null)
f(Right node,d-1);
if(Left node != null)
f(Left node,d-1);
}
Here's a non-recursive solution
public class Move {
private List<Node> steps;
private int stepsRemaining;
private Node lastStep;
public Move(List<Node> steps, int stepsRemaining) {
this.steps = steps;
this.stepsRemaining = stepsRemaining;
this.lastStep = steps.get(steps.size() - 1);
}
// getters and setters
}
public List<Node> getOptions(Node node, int steps) {
LinkedList<Move> stack = new LinkedList<Move>();
stack.addFirst(new Move(Arrays.asList(node), steps);
List<Node> options = new ArrayList<Node>();
while (!stack.isEmpty()) {
Move currentMove = stack.removeFirst();
Node lastStep = currentMove.lastStep;
Node[] childNodes = new Node[] { lastStep.up, lastStep.down, lastStep.left, lastStep.right };
for (Node childNode : childNodes) {
// make sure we don't go back on ourselves
if (childNode != null && !currentMove.steps.contains(childNode)) {
if (currentMove.stepsRemaining == 1) {
options.add(childNode);
continue;
}
List<Node> childSteps = new ArrayList<Node>(currentNode.steps);
childSteps.add(childNode);
stack.addFirst(new Move(childSteps, currentMove.stepsRemaining - 1));
}
}
}
return options;
}
Add a visited boolean to your Node class. Then in each if test if the node has been visited yet.
if(Up node != null && node.visited != true)
f(Up node, d - 1)
don't forget to reset each time you launch your dice.
Maybe this die drawing code will help.
private void drawSpots(Graphics g, int w, int h, int count) {
g.setColor(Color.BLACK);
switch (count) {
case 1:
drawSpot(g, w / 2, h / 2);
break;
case 3:
drawSpot(g, w / 2, h / 2);
// Fall thru to next case
case 2:
drawSpot(g, w / 4, h / 4);
drawSpot(g, 3 * w / 4, 3 * h / 4);
break;
case 5:
drawSpot(g, w / 2, h / 2);
// Fall thru to next case
case 4:
drawSpot(g, w / 4, h / 4);
drawSpot(g, 3 * w / 4, 3 * h / 4);
drawSpot(g, 3 * w / 4, h / 4);
drawSpot(g, w / 4, 3 * h / 4);
break;
case 6:
drawSpot(g, w / 4, h / 4);
drawSpot(g, 3 * w / 4, 3 * h / 4);
drawSpot(g, 3 * w / 4, h / 4);
drawSpot(g, w / 4, 3 * h / 4);
drawSpot(g, w / 4, h / 2);
drawSpot(g, 3 * w / 4, h / 2);
break;
}
}
As you can see, drawing 1 and 6 are separate nodes. Drawing 3 is a special case of drawing 2. Drawing 5 is a special case of drawing 4.
It might be possible to arrange the die numbers differently. Drawing 1, 3, and 5 are similar. Drawing 2, 4, and 6 are similar as well.
I'm not going to write any code here, just explain the algorithm.
First of all, you don't need a recursive algorithm to solve this problem. You could, of course, implement it in a recursive way, but it would bring no advantages.
That being said, in order to solve the problem you just need to keep track of the intersection node (in case of just one intersection). After that you just need to calculate the distance between the current node - the blue one - and the intersection node, let's call it d. Whenever you roll the dice and have a number - let's call it c - you can calculate the position of the red nodes in O(1) time. In fact you have a distance of d + c for the branch where the current node is and c - d for the other branches. The only exception is when d > c in which case you have only 2 red nodes on the current node's branch and they are in positions d - c and d + c from the intersection node.
Pseudo Code
The following pseudo code considers only a situation as the one shown in the image (1 intersection, 2 directions from non intersection nodes, 2+ directions for intersection nodes). Keep in mind that the code is not optimized, it's just shown to illustrate the idea.
// currentNode - the node where you start from
// c - the number given by the die
GetNeighbouringNodes(Node currentNode, int c)
{
Node intersectionNode = null;
Direction intersectionNodeDirection = Direction.None;
if(currentNode.numberOfDirections > 2)
{
intersectionNode = currentNode;
}
else
{
intersectionNodeDirection = Direction.Up;
// the implementation of the getIntersectionNode is not included intentionally
intersectionNode = getIntersectionNode(currentNode, intersectionNodeDirection);
if(intersectionNode == null)
{
intersectionNodeDirection = Direction.Down;
intersectionNode = getIntersectionNode(currentNode, intersectionNodeDirection);
}
// check in other directions also
}
// at this point we have the intersection node
int d = getDistance(currentNode, intersectionNode);
List<Node> resultingNodes = new List<Node>();
if(d > c)
{
resultingNodes.add(getDistantNode(from: intersectionNode, distance: d - c, direction: inverseDirectionOf(intersectionNodeDirection)));
resultingNodes.add(getDistantNode(from: intersectionNode, distance: d + c, direction: inverseDirectionOf(intersectionNodeDirection)));
}
else
{
resultingNodes.add(getDistantNode(from: intersectionNode, distance: d + c, direction: inverseDirectionOf(intersectionNodeDirection)));
foreach(otherDirection)
{
resultingNodes.add(getDistantNode(from: intersectionNode, distance: c - d, direction: otherDirection)));
}
}
return resultingNodes;
}
Let me know if anything is unclear.
Related
I've been trying to create an algorithm for finding the order of points in a simple polygon.
The aim is when given points on a 2D plane (there is always possible to form a simple polygon btw) I output the order of points in a valid simple polygon. All points must be part of said polygon.
I've somewhat achieved this, but it fails for some test cases. The way I have done this is by finding the geometrical centre
int centerX = (lowX + highX) / 2;
int centerY = (lowY + highY) / 2;
Point center = new Point(centerX, centerY, -1);
and then sorting all points by their polar angle.
Collections.sort(points, (a, b) -> {
if(a == b || a.equals(b)) {
return 0;
}
double aTheta = Math.atan2((long)a.y - center.y, (long)a.x - center.x);
double bTheta = Math.atan2((long)b.y - center.y, (long)b.x - center.x);
if(aTheta < bTheta) {
return -1;
}
else if(aTheta > bTheta) {
return 1;
}
else {
double aDist = Math.sqrt((((long)center.x - a.x) * ((long)center.x - a.x)) +
(((long)center.y - a.y) * ((long)center.y - a.y)));
double bDist = Math.sqrt((((long)center.x - b.x) * ((long)center.x - b.x)) +
(((long)center.y - b.y) * ((long)center.y - b.y)));
if (aDist < bDist) {
return -1;
}
else {
return 1;
}
}
});
I'm struggling with finding out what makes this break for some of the test cases. Any help or pointers are greatly appreciated! Also wondering if there are any efficient, yet not overly complicated algorithms that can perform this.
UPDATE
I've found one of the failing test cases: When given the points (101, 101), (100, 100), (105, 100), (103, 100), (107, 100), (102, 100), (109, 100) Labeled 0 to 9 respectively
My program outputs 2 4 6 0 3 5 1 but it is not a valid simple polygon
It should be a permutation of 1 0 6 4 2 3 5
Here's a Java implementation of the nice answer already provided by Reblochon Masque.
Note that instead of using any trig functions to calculate angles we use a comparison of the relative orientation (or turn direction) from the min point to each of the two points being compared. Personally I find this more elegant than using angles, but others may disagree. However, as with any calculations based on double the orient2D method is susceptible to errors in rounding.
Also, when there's a tie based on orientation, because the min point and the two points are collinear, we break the tie by considering the relative ordering of the two points. This means that we'll visit points in order, with no "switchbacks", which I think is preferable.
static List<Point2D> simplePolygon(Collection<Point2D> points)
{
final Point2D min = minPoint2D(points);
List<Point2D> simple = new ArrayList<>(points);
Collections.sort(simple, (p1, p2) ->
{
int cmp = orient2D(min, p2, p1);
if(cmp == 0)
cmp = order2D(p1, p2);
return cmp;
});
return simple;
}
// return lowest, leftmost point
static Point2D minPoint2D(Collection<Point2D> points)
{
Point2D min = null;
for(Point2D p : points)
if(min == null || order2D(p, min) < 0) min = p;
return min;
}
// order points by increasing y, break ties by increasing x
static int order2D(Point2D p1, Point2D p2)
{
if(p1.getY() < p2.getY()) return -1;
else if(p1.getY() > p2.getY()) return 1;
else if(p1.getX() < p2.getX()) return -1;
else if(p1.getX() > p2.getX()) return 1;
else return 0;
}
// Does p involve a CCW(+1), CW(-1) or No(0) turn from the line p1-p2
static int orient2D(Point2D p1, Point2D p2, Point2D p)
{
double dx = p2.getX() - p1.getX();
double dy = p2.getY() - p1.getY();
double px = p.getX() - p1.getX();
double py = p.getY() - p1.getY();
double dot = py * dx - px * dy;
return dot < 0 ? -1 : dot > 0 ? 1 : 0;
}
Test:
int[] a = {101, 101, 100, 100, 105, 100, 103, 100, 107, 100, 102, 100, 109, 100};
List<Point2D> points = new ArrayList<>();
for(int i=0; i<a.length; i+=2)
points.add(new Point2D.Double(a[i], a[i+1]));
List<Point2D> simple = simplePolygon(points);
for(Point2D p : simple) System.out.println(p);
Output:
Point2D.Double[100.0, 100.0]
Point2D.Double[102.0, 100.0]
Point2D.Double[103.0, 100.0]
Point2D.Double[105.0, 100.0]
Point2D.Double[107.0, 100.0]
Point2D.Double[109.0, 100.0]
Point2D.Double[101.0, 101.0]
Which I believe is correct.
here is an easy to implement O(n logn) algorithm that is guaranteed to produce a simple polygon (no edge crossings)
1- find the point the most south, (and the most westwards if you have a tie with the y values).
2- Sort all points based on their angle between this most south point, and the horizontal line.
3- the ordered sequence is a simple polygon.
In some rare cases, some points may not form a vertex, but be included in an edge, if they are collinear at the same angle.
EDIT: Drawing a 4 pointed star does work now with this code but i don't really know WHY this works, AND if i divide by the same number for x & y it just gives me a diamond??? 3 & 7 seem to be the best values too and i have no idea why...
public AP4Star() { }
public AP4Star(int x1, int y1, int x2, int y2, Color c, bool solid, float penW) : base(x1, y1, x2, y2, c, solid, penW) { }
public override void Draw(Graphics g)
{
float xDisplacement = Math.Abs(getX1() - getX2());
float yDisplacement = Math.Abs(getY1() - getY2());
PointF top = new PointF((getX1() + getX2()) / 2, Math.Min(getY2(), getY1()));
PointF bottom = new PointF(top.X, Math.Max(getY2(), getY1()));
PointF left = new PointF(Math.Min(getX2(), getX1()), (top.Y + bottom.Y) / 2);
PointF right = new PointF(Math.Max(getX2(), getX1()), left.Y);
PointF mtr = new PointF(right.X - xDisplacement / 3, right.Y - yDisplacement / 7);
PointF mbr = new PointF(right.X - xDisplacement / 3, right.Y + yDisplacement / 7);
PointF mbl = new PointF(left.X + xDisplacement / 3, left.Y + yDisplacement / 7);
PointF mtl = new PointF(left.X + xDisplacement / 3, left.Y - yDisplacement / 7);
PointF[] fourStar = { top,mtr, right, mbr, bottom, mbl, left, mtl };
g.DrawPolygon(new Pen(getColor(), getPenWidth()), fourStar);
That code produce a pretty good pointy star but i feel like i am still doing this wrong... : result
I don't think this is really a coding question, it's more of a logic question. But here's how I'd solve it:
Start by zero-indexing all of your points. Assuming all of your points are equidistant from zero, that means n = 10 gives you four points like the following for your initial diamond:
p1: { x = 0, y = 10}
p2: { x = 10, y = 0}
p3: { x = 0, y = -10}
p4: { x = -10, y = 0}
Now just add each of those points with a new point that has n / 4 (if it was n / 2, it'd be a straight line. So n / 4 ... or anything greater than 2, should get you a pointy star). So if we use n/4, you get the following eight points:
p1: { x = 0, y = 10}
p2: { x = 2.5, y = 2.5}
p3: { x = 10, y = 0}
p4: { x = 2.5, y = -2.5}
p5: { x = 0, y = -10}
p6: { x = -2.5, y = -2.5
p7: { x = -10, y = 0}
p8: { x = -2.5, y = 2.5}
Now just draw a line between each of those points and you should have your pointy star. I hope that's helpful!
I apologize for the somewhat vague title, I'm unsure what you would call this puzzle.
I'm making a path finding method to find the route with the least moves, not the distance traveled.
The rules of the game are simple, you must traverse from the orange square to the green square, but you can only move in a straight line, and cannot stop moving in that direction until you hit a boundary (either the wall of the arena or an obstacle), as if they were sliding across ice.
Example map, and unless I'm mistaken, the desired path (8 moves)
Arena.java: https://gist.github.com/CalebWhiting/3a6680d40610829b1b6d
ArenaTest.java: https://gist.github.com/CalebWhiting/9a4767508831ea5dc0da
I'm assuming this would be best handled with a Dijkstras or A* path finding algorithm, however I'm not only not very experienced with these algorithms, but also don't know how I would go about defining the path rules.
Thank you for any help in advance.
Here's my solution (Java) in case someone is still interested. As #tobias_k suggested in his comment above, indeed BFS is the way to go:
import java.util.LinkedList;
public class PokemonIceCave {
public static void main(String[] args) {
int[][] iceCave1 = {
{0, 0, 0, 1, 0},
{0, 0, 0, 0, 1},
{0, 1, 1, 0, 0},
{0, 1, 0, 0, 1},
{0, 0, 0, 1, 0}
};
System.out.println(solve(iceCave1, 0, 0, 2, 4));
System.out.println();
int[][] iceCave2 = {
{0, 0, 0, 1, 0},
{0, 0, 0, 0, 1},
{0, 1, 1, 0, 0},
{0, 1, 0, 0, 1},
{0, 0, 0, 1, 0},
{0, 0, 0, 0, 0}
};
System.out.println(solve(iceCave2, 0, 0, 2, 5));
}
public static int solve(int[][] iceCave, int startX, int startY, int endX, int endY) {
Point startPoint = new Point(startX, startY);
LinkedList<Point> queue = new LinkedList<>();
Point[][] iceCaveColors = new Point[iceCave.length][iceCave[0].length];
queue.addLast(new Point(0, 0));
iceCaveColors[startY][startX] = startPoint;
while (queue.size() != 0) {
Point currPos = queue.pollFirst();
System.out.println(currPos);
// traverse adjacent nodes while sliding on the ice
for (Direction dir : Direction.values()) {
Point nextPos = move(iceCave, iceCaveColors, currPos, dir);
System.out.println("\t" + nextPos);
if (nextPos != null) {
queue.addLast(nextPos);
iceCaveColors[nextPos.getY()][nextPos.getX()] = new Point(currPos.getX(), currPos.getY());
if (nextPos.getY() == endY && nextPos.getX() == endX) {
// we found the end point
Point tmp = currPos; // if we start from nextPos we will count one too many edges
int count = 0;
while (tmp != startPoint) {
count++;
tmp = iceCaveColors[tmp.getY()][tmp.getX()];
}
return count;
}
}
}
System.out.println();
}
return -1;
}
public static Point move(int[][] iceCave, Point[][] iceCaveColors, Point currPos, Direction dir) {
int x = currPos.getX();
int y = currPos.getY();
int diffX = (dir == Direction.LEFT ? -1 : (dir == Direction.RIGHT ? 1 : 0));
int diffY = (dir == Direction.UP ? -1 : (dir == Direction.DOWN ? 1 : 0));
int i = 1;
while (x + i * diffX >= 0
&& x + i * diffX < iceCave[0].length
&& y + i * diffY >= 0
&& y + i * diffY < iceCave.length
&& iceCave[y + i * diffY][x + i * diffX] != 1) {
i++;
}
i--; // reverse the last step
if (iceCaveColors[y + i * diffY][x + i * diffX] != null) {
// we've already seen this point
return null;
}
return new Point(x + i * diffX, y + i * diffY);
}
public static class Point {
int x;
int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
#Override
public String toString() {
return "Point{" +
"x=" + x +
", y=" + y +
'}';
}
}
public enum Direction {
LEFT,
RIGHT,
UP,
DOWN
}
}
I think the best solution would probably be the BFS, where you represent the state of the board with a "State" object with the following parameters: number of moves made so far, and coordinates. It should also have a method to find the next states attainable (which should be fairly easy to code, just go N, S, E, W and return an array of the first blocking spots).
Create initial state (0 moves with initial coordinates)
Put in a priority queue (sorting by number moves)
while(priority queue has more states):
Remove node
if it is a goal state:
return the state
Find all neighbors of current state
Add them to priority queue (remembering to increment number of moves by 1)
This uses an implicit graph representation. Optimality is guaranteed because of the priority queue; when the goal state is found, it will have been reached with the fewest moves. If the whole priority queue is exhausted and no state is returned, then no solution exists. This solution takes O(V^2logV) time because of the priority queue, but I think this is the simplest to code. A straight up O(V) BFS solution is possible but you'll have to keep track of what states you have or have not visited yet and the fewest number of moves to reach them, which would take O(V) memory.
I have the following code. It is used to compute texture positions for use inside OpenGL, but, that is of no concern. It works perfectly the first time, but only the first time.
public static float[] createTexturePositions(final int width, final int height)
{
float[] positions = new float[width * height * 12];
// Range: 0 - 1
float dx = 1f / width;
float dy = 1f / height;
float y = 0f;
int i = 0;
for (int indexY = 0; indexY < height; indexY++)
{
float x = 0f;
for (int indexX = 0; indexX < width; indexX++)
{
float top = y;
float left = x;
float right = x + dx;
float bottom = y + dy;
// p1 p2
// p3
positions[i ] = left;
positions[i + 1] = top;
positions[i + 2] = right;
positions[i + 3] = top;
positions[i + 4] = left;
positions[i + 5] = bottom;
// p4
// p5 p6
positions[i + 6] = right;
positions[i + 7] = top;
positions[i + 8] = left;
positions[i + 9] = bottom;
positions[i + 10] = right;
positions[i + 11] = bottom;
x += dx;
i += 12;
}
y += dy;
}
return positions;
}
Now, what is happening makes no sense.
- this function returns different values even when the input is exactly the same.
- Error occurs randomly
- Error doesn't occur the first time, but usually by around the 4th call, the data doesn't match the previous call.
- Values differ by a lot, here I get: 0.85714287 vs 1.1417698E35 at index i = 565. My input values are 7, 7.
- The 2nd value is the nonsensical one, in the above case (e^35). But this is the value which was just generated from scratch. I.E. there should be no way it was modified.
- I only return copies of the array's thereby ensuring that the one being tested is the one I think it is!.
- The rounding errors shouldn't matter, since they should be the same every time, and even if they are different, they should at least be similar in value.
It seems to me like a data corruption issue. Like someone or something is modifying the data on me! Is there some sort of stack heap collision issue or something weird going on here that I don't know about?
UPDATE 1:
I found that adding the following code:
testValue( top );
testValue( left );
testValue( right );
testValue( bottom );
private static void testValue(float value)
{
if (value < 0f || value > 1.01f) throw new RuntimeException("Error! Value is Invalid");
}
prevents the problem from occurring! The testValue function is of course placed after the original function, with the calls to testValue occurring directly after the values are determined.
UPDATE 2:
I can toggle the problem on / off, just by commenting / uncommenting the testValue code. Again, it makes no sense, I think I will just leave this code in there, as it is fixing my problem for me!
I have a problem that I can't seem to get a working algorithm for, I've been trying to days and get so close but yet so far.
I want to draw a triangle defined by 3 points (p0, p1, p2). This triangle can be any shape, size, and orientation. The triangle must also be filled inside.
Here's a few things I've tried and why they've failed:
1
Drawing lines along the triangle from side to side
Failed because the triangle would have holes and would not be flat due to the awkwardness of drawing lines across the angled surface with changing locations
2
Iterate for an area and test if the point falls past the plane parallel to the triangle and 3 other planes projected onto the XY, ZY, and XZ plane that cover the area of the triangle
Failed because for certain triangles (that have very close sides) there would be unpredictable results, e.g. voxels floating around not connected to anything
3
Iterate for an area along the sides of the triangle (line algorithm) and test to see if a point goes past a parallel plane
Failed because drawing a line from p0 to p1 is not the same as a line from p1 to p0 and any attempt to rearrange either doesn't help, or causes more problems. Asymmetry is the problem with this one.
This is all with the intent of making polygons and flat surfaces. 3 has given me the most success and makes accurate triangles, but when I try to connect these together everything falls apart and I get issues with things not connecting, asymmetry, etc. I believe 3 will work with some tweaking but I'm just worn out from trying to make this work for so long and need help.
There's a lot of small details in my algorithms that aren't really relevant so I left them out. For number 3 it might be a problem with my implementation and not the algorithm itself. If you want code I'll try and clean it up enough to be understandable, it will take me a few minutes though. But I'm looking for algorithms that are known to work. I can't seem to find any voxel shape making algorithms anywhere, I've been doing everything from scratch.
EDIT:
Here's the third attempt. It's a mess, but I tried to clean it up.
// Point3i is a class I made, however the Vector3fs you'll see are from lwjgl
public void drawTriangle (Point3i r0, Point3i r1, Point3i r2)
{
// Util is a class I made with some useful stuff inside
// Starting values for iteration
int sx = (int) Util.min(r0.x, r1.x, r2.x);
int sy = (int) Util.min(r0.y, r1.y, r2.y);
int sz = (int) Util.min(r0.z, r1.z, r2.z);
// Ending values for iteration
int ex = (int) Util.max(r0.x, r1.x, r2.x);
int ey = (int) Util.max(r0.y, r1.y, r2.y);
int ez = (int) Util.max(r0.z, r1.z, r2.z);
// Side lengths
float l0 = Util.distance(r0.x, r1.x, r0.y, r1.y, r0.z, r1.z);
float l1 = Util.distance(r2.x, r1.x, r2.y, r1.y, r2.z, r1.z);
float l2 = Util.distance(r0.x, r2.x, r0.y, r2.y, r0.z, r2.z);
// Calculate the normal vector
Vector3f nn = new Vector3f(r1.x - r0.x, r1.y - r0.y, r1.z - r0.z);
Vector3f n = new Vector3f(r2.x - r0.x, r2.y - r0.y, r2.z - r0.z);
Vector3f.cross(nn, n, n);
// Determines which direction we increment for
int iz = n.z >= 0 ? 1 : -1;
int iy = n.y >= 0 ? 1 : -1;
int ix = n.x >= 0 ? 1 : -1;
// Reorganize for the direction of iteration
if (iz < 0) {
int tmp = sz;
sz = ez;
ez = tmp;
}
if (iy < 0) {
int tmp = sy;
sy = ey;
ey = tmp;
}
if (ix < 0) {
int tmp = sx;
sx = ex;
ex = tmp;
}
// We're we want to iterate over the end vars so we change the value
// by their incrementors/decrementors
ex += ix;
ey += iy;
ez += iz;
// Maximum length
float lmax = Util.max(l0, l1, l2);
// This is a class I made which manually iterates over a line, I already
// know that this class is working
GeneratorLine3d g0, g1, g2;
// This is a vector for the longest side
Vector3f v = new Vector3f();
// make the generators
if (lmax == l0) {
v.x = r1.x - r0.x;
v.y = r1.y - r0.y;
v.z = r1.z - r0.z;
g0 = new GeneratorLine3d(r0, r1);
g1 = new GeneratorLine3d(r0, r2);
g2 = new GeneratorLine3d(r2, r1);
}
else if (lmax == l1) {
v.x = r1.x - r2.x;
v.y = r1.y - r2.y;
v.z = r1.z - r2.z;
g0 = new GeneratorLine3d(r2, r1);
g1 = new GeneratorLine3d(r2, r0);
g2 = new GeneratorLine3d(r0, r1);
}
else {
v.x = r2.x - r0.x;
v.y = r2.y - r0.y;
v.z = r2.z - r0.z;
g0 = new GeneratorLine3d(r0, r2);
g1 = new GeneratorLine3d(r0, r1);
g2 = new GeneratorLine3d(r1, r2);
}
// Absolute values for the normal
float anx = Math.abs(n.x);
float any = Math.abs(n.y);
float anz = Math.abs(n.z);
int i, o;
int si, so;
int ii, io;
int ei, eo;
boolean maxx, maxy, maxz,
midy, midz, midx,
minx, miny, minz;
maxx = maxy = maxz =
midy = midz = midx =
minx = miny = minz = false;
// Absolute values for the longest side vector
float rnx = Math.abs(v.x);
float rny = Math.abs(v.y);
float rnz = Math.abs(v.z);
int rmid = Util.max(rnx, rny, rnz);
if (rmid == rnz) midz = true;
else if (rmid == rny) midy = true;
midx = !midz && !midy;
// Determine the inner and outer loop directions
if (midz) {
if (any > anx)
{
maxy = true;
si = sy;
ii = iy;
ei = ey;
}
else {
maxx = true;
si = sx;
ii = ix;
ei = ex;
}
}
else {
if (anz > anx) {
maxz = true;
si = sz;
ii = iz;
ei = ez;
}
else {
maxx = true;
si = sx;
ii = ix;
ei = ex;
}
}
if (!midz && !maxz) {
minz = true;
so = sz;
eo = ez;
}
else if (!midy && !maxy) {
miny = true;
so = sy;
eo = ey;
}
else {
minx = true;
so = sx;
eo = ex;
}
// GeneratorLine3d is iterable
Point3i p1;
for (Point3i p0 : g0) {
// Make sure the two 'mid' coordinate correspond for the area inside the triangle
if (midz)
do p1 = g1.hasNext() ? g1.next() : g2.next();
while (p1.z != p0.z);
else if (midy)
do p1 = g1.hasNext() ? g1.next() : g2.next();
while (p1.y != p0.y);
else
do p1 = g1.hasNext() ? g1.next() : g2.next();
while (p1.x != p0.x);
eo = (minx ? p0.x : miny ? p0.y : p0.z);
so = (minx ? p1.x : miny ? p1.y : p1.z);
io = eo - so >= 0 ? 1 : -1;
for (o = so; o != eo; o += io) {
for (i = si; i != ei; i += ii) {
int x = maxx ? i : midx ? p0.x : o;
int y = maxy ? i : midy ? p0.y : o;
int z = maxz ? i : midz ? p0.z : o;
// isPassing tests to see if a point goes past a plane
// I know it's working, so no code
// voxels is a member that is an arraylist of Point3i
if (isPassing(x, y, z, r0, n.x, n.y, n.z)) {
voxels.add(new Point3i(x, y, z));
break;
}
}
}
}
}
You could use something like Besenham's line algorithm, but extended into three dimensions. The two main ideas we want to take from it are:
rotate the initial line so its slope isn't too steep.
for any given x value, find an integer value that is closest to the ideal y value.
Just as Bresenham's algorithm prevents gaps by performing an initial rotation, we'll avoid holes by performing two initial rotations.
Get the normal vector and point that represent the plane your triangle lies on. Hint: use the cross product of (line from p0 to p1) and (line from p0 to p2) for the vector, and use any of your corner points for the point.
You want the plane to be sufficiently not-steep, to avoid holes. You must satisfy these conditions:
-1 >= norm.x / norm.y >= 1
-1 >= norm.z / norm.y >= 1
Rotate your normal vector and initial points 90 degrees about the x axis and 90 degrees about the z axis until these conditions are satisfied. I'm not sure how to do this in the fewest number of rotations, but I'm fairly sure you can satisfy these conditions for any plane.
Create a function f(x,z) which represents the plane your rotated triangle now lies on. It should return the Y value of any pair of X and Z values.
Project your triangle onto the XZ plane (i.e., set all the y values to 0), and use your favorite 2d triangle drawing algorithm to get a collection of x-and-z coordinates.
For each pixel value from step 4, pass the x and z values into your function f(x,z) from step 3. Round the result to the nearest integer, and store the x, y, and z values as a voxel somewhere.
If you performed any rotations in step 2, perform the opposite of those rotations in reverse order on your voxel collection.
Start with a function that checks for triangle/voxel intersection. Now you can scan a volume and find the voxels that intersect the triangle - these are the ones you're interested in. This is a lousy algorithm but is also a regression test for anything else you try. This test is easy to implement using SAT (separating axis theorem) and considering the triangle a degenerate volume (1 face, 3 edges) and considering the voxels symmetry (only 3 face normals).
I use octtrees, so my preferred method is to test a triangle against a large voxel and figure out which of the 8 child octants it intersects. Then use recursion on the intersected children until the desired level of subdivision is attained. Hint: at most 6 of the children can be intersected by the triangle and often fewer than that. This is tricky but will produce the same results as the first method but much quicker.
Rasterization in 3d is probably fastest, but IMHO is even harder to guarantee no holes in all cases. Again, use the first method for comparison.