Fixes, more tests and more to do for RayTracing.

* More tests for PassableRayTracing (room, rays from outside).
* Alter InteractRayTracing to account for the block interacted with.
* Added tests for InteractRayTracing.

Problems:
* RayTracing may end x-th digit off target, thus in the wrong block.
Suggested fix is to keep correcting t by the absolute coordinates of the
blocks, i.e. calculate the absolute position rather than adding up.
* InteractRayTracing with strict set to false (like in the
blockinteract.visible check) will be too lenient with 1-thick wall
setups and fail test cases.
This commit is contained in:
asofold 2015-03-11 00:03:51 +01:00
parent 8856f68e55
commit a5d6594591
7 changed files with 598 additions and 256 deletions

View File

@ -21,6 +21,9 @@ public class Visible extends Check {
private BlockCache blockCache;
/**
* Strict set to false, due to false positives.
*/
private final InteractRayTracing rayTracing = new InteractRayTracing(false);
public Visible() {
@ -113,6 +116,15 @@ public class Visible extends Check {
private boolean checkRayTracing(final double eyeX, final double eyeY, final double eyeZ, final int blockX, final int blockY, final int blockZ, final BlockFace face){
/*
* TODO: Always use the exact looking direction first (calculate where
* it hits the target block, and which faces are exposed then, estimate
* alternative positions based on that, get rid of the workaround).
*/
/*
* TODO: Consider using (slightly less than) full block bounds always ?
* (alt: 2,3 classes of size)
*/
// Estimated target-middle-position (does it for most cases).
@SuppressWarnings("deprecation")
final double[] bounds = BlockProperties.getCorrectedBounds(blockCache, blockX, blockY, blockZ);
@ -131,7 +143,7 @@ public class Visible extends Check {
final boolean skipPassable = blockX == bEstX && blockY == bEstY && blockZ == bEstZ;
// TODO: Might also use looking direction (test how accurate).
return checkCollision(eyeX, eyeY, eyeZ, estX, estY, estZ, estId, bounds, modX, modY, modZ, skipPassable);
return checkCollision(eyeX, eyeY, eyeZ, estX, estY, estZ, estId, bounds, modX, modY, modZ, skipPassable, blockX, blockY, blockZ);
}
@ -150,11 +162,11 @@ public class Visible extends Check {
* @param skipPassable
* @return
*/
private boolean checkCollision(final double eyeX, final double eyeY, final double eyeZ, final double estX, final double estY, final double estZ, final int estId, final double[] bounds, final int modX, final int modY, final int modZ, final boolean skipPassable) {
private boolean checkCollision(final double eyeX, final double eyeY, final double eyeZ, final double estX, final double estY, final double estZ, final int estId, final double[] bounds, final int modX, final int modY, final int modZ, final boolean skipPassable, final int clickedX, final int clickedY, final int clickedZ) {
// Check current position.
if (skipPassable || BlockProperties.isPassable(blockCache, estX, estY, estZ, estId)){
// Perform ray-tracing.
rayTracing.set(eyeX, eyeY, eyeZ, estX, estY, estZ);
rayTracing.set(eyeX, eyeY, eyeZ, estX, estY, estZ, clickedX, clickedY, clickedZ);
rayTracing.loop();
if (!rayTracing.collides() && rayTracing.getStepsDone() < rayTracing.getMaxSteps()){
return false;
@ -168,10 +180,10 @@ public class Visible extends Check {
final double d = bounds == null ? 0.5 : (bounds[3] - bounds[0]) / 2.0;
if (d >= 0.05){
// Recursion with adapted x position (if differs enough from bounds.
if (!checkCollision(eyeX, eyeY, eyeZ, estX - d, estY, estZ, estId, bounds, 1, modY, modZ, skipPassable)){
if (!checkCollision(eyeX, eyeY, eyeZ, estX - d, estY, estZ, estId, bounds, 1, modY, modZ, skipPassable, clickedX, clickedY, clickedZ)){
return false;
}
if (!checkCollision(eyeX, eyeY, eyeZ, estX + d, estY, estZ, estId, bounds, 1, modY, modZ, skipPassable)){
if (!checkCollision(eyeX, eyeY, eyeZ, estX + d, estY, estZ, estId, bounds, 1, modY, modZ, skipPassable, clickedX, clickedY, clickedZ)){
return false;
}
}
@ -181,10 +193,10 @@ public class Visible extends Check {
final double d = bounds == null ? 0.5 : (bounds[5] - bounds[2]) / 2.0;
if (d >= 0.05){
// Recursion with adapted x position (if differs enough from bounds.
if (!checkCollision(eyeX, eyeY, eyeZ, estX, estY, estZ - d, estId, bounds, 1, modY, 1, skipPassable)){
if (!checkCollision(eyeX, eyeY, eyeZ, estX, estY, estZ - d, estId, bounds, 1, modY, 1, skipPassable, clickedX, clickedY, clickedZ)){
return false;
}
if (!checkCollision(eyeX, eyeY, eyeZ, estX, estY, estZ + d, estId, bounds, 1, modY, 1, skipPassable)){
if (!checkCollision(eyeX, eyeY, eyeZ, estX, estY, estZ + d, estId, bounds, 1, modY, 1, skipPassable, clickedX, clickedY, clickedZ)){
return false;
}
}
@ -194,10 +206,10 @@ public class Visible extends Check {
final double d = bounds == null ? 0.5 : (bounds[4] - bounds[1]) / 2.0;
if (d >= 0.05){
// Recursion with adapted x position (if differs enough from bounds.
if (!checkCollision(eyeX, eyeY, eyeZ, estX, estY - d, estZ, estId, bounds, 1, 1, 1, skipPassable)){
if (!checkCollision(eyeX, eyeY, eyeZ, estX, estY - d, estZ, estId, bounds, 1, 1, 1, skipPassable, clickedX, clickedY, clickedZ)){
return false;
}
if (!checkCollision(eyeX, eyeY, eyeZ, estX, estY + d, estZ, estId, bounds, 1, 1, 1, skipPassable)){
if (!checkCollision(eyeX, eyeY, eyeZ, estX, estY + d, estZ, estId, bounds, 1, 1, 1, skipPassable, clickedX, clickedY, clickedZ)){
return false;
}
}

View File

@ -88,6 +88,52 @@ public class FakeBlockCache extends BlockCache {
}
}
public void fill(int x1, int y1, int z1, int x2, int y2, int z2, Material type) {
fill(x1, y1, z1, x2, y2, z2, BlockProperties.getId(type), 0, new double[]{0.0, 0.0, 0.0, 1.0, 1.0, 1.0});
}
public void fill(int x1, int y1, int z1, int x2, int y2, int z2, int typeId, int data, double[] bounds) {
for (int x = x1; x <= x2; x++) {
for (int y = y1; y <= y2; y ++) {
for (int z = z1; z <= z2; z++) {
set(x, y, z, typeId, data, bounds);
}
}
}
}
public void walls(int x1, int y1, int z1, int x2, int y2, int z2, Material type) {
walls(x1, y1, z1, x2, y2, z2, BlockProperties.getId(type), 0, new double[]{0.0, 0.0, 0.0, 1.0, 1.0, 1.0});
}
public void walls(int x1, int y1, int z1, int x2, int y2, int z2, int typeId, int data, double[] bounds) {
for (int x = x1; x <= x2; x++) {
for (int y = y1; y <= y2; y ++) {
for (int z = z1; z <= z2; z++) {
if (x == x1 || x == x2 || z == z1 || z == z2) {
set(x, y, z, typeId, data, bounds);
}
}
}
}
}
public void room(int x1, int y1, int z1, int x2, int y2, int z2, Material type) {
room(x1, y1, z1, x2, y2, z2, BlockProperties.getId(type), 0, new double[]{0.0, 0.0, 0.0, 1.0, 1.0, 1.0});
}
public void room(int x1, int y1, int z1, int x2, int y2, int z2, int typeId, int data, double[] bounds) {
for (int x = x1; x <= x2; x++) {
for (int y = y1; y <= y2; y ++) {
for (int z = z1; z <= z2; z++) {
if (x == x1 || x == x2 || z == z1 || z == z2 || y == y1 || y == y2) {
set(x, y, z, typeId, data, bounds);
}
}
}
}
}
@Override
public void setAccess(World world) {
// Ignore.
@ -123,7 +169,7 @@ public class FakeBlockCache extends BlockCache {
@Override
public boolean standsOnEntity(Entity entity, double minX, double minY,
double minZ, double maxX, double maxY, double maxZ) {
// TODO: Consider adding blocks where this might be the case.
// TODO: Consider adding cuboids which mean "ground" if the foot location is inside.
return false;
}

View File

@ -1,5 +1,8 @@
package fr.neatmonster.nocheatplus.utilities;
import org.bukkit.Location;
/**
* Rough ray-tracing for interaction with something. This does not do any smart end-point guessing.
@ -25,11 +28,13 @@ public class InteractRayTracing extends RayTracing {
protected int lastBx, lastBy, lastBz;
public InteractRayTracing(){
protected int targetX, targetY, targetZ;
public InteractRayTracing() {
super();
}
public InteractRayTracing(boolean strict){
public InteractRayTracing(boolean strict) {
super();
this.strict = strict;
}
@ -42,27 +47,43 @@ public class InteractRayTracing extends RayTracing {
this.blockCache = blockCache;
}
/* (non-Javadoc)
* @see fr.neatmonster.nocheatplus.utilities.RayTracing#set(double, double, double, double, double, double)
*/
@Override
public void set(double x0, double y0, double z0, double x1, double y1, double z1) {
set(x0, y0, z0, x1, y1, z1, Location.locToBlock(x1), Location.locToBlock(y1), Location.locToBlock(z1));
// Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
}
/**
*
* @param x0
* @param y0
* @param z0
* @param x1
* @param y1
* @param z1
* @param targetX The block clicked/interacted with (can be different to the end point of ray-tracing, or ignored with Integer.MAX_VALUE).
* @param targetY
* @param targetZ
*/
public void set(double x0, double y0, double z0, double x1, double y1, double z1, int targetX, int targetY, int targetZ) {
super.set(x0, y0, z0, x1, y1, z1);
collides = false;
lastBx = blockX;
lastBy = blockY;
lastBz = blockZ;
this.targetX = targetX;
this.targetY = targetY;
this.targetZ = targetZ;
}
public boolean collides(){
public boolean collides() {
return collides;
}
/**
* Remove reference to BlockCache.
*/
public void cleanup(){
if (blockCache != null){
public void cleanup() {
if (blockCache != null) {
blockCache = null;
}
}
@ -74,49 +95,75 @@ public class InteractRayTracing extends RayTracing {
* @param blockZ
* @return
*/
private final boolean doesCollide(int blockX, int blockY, int blockZ){
private final boolean doesCollide(final int blockX, final int blockY, final int blockZ) {
final int id = blockCache.getTypeId(blockX, blockY, blockZ);
final long flags = BlockProperties.getBlockFlags(id);
if ((flags & BlockProperties.F_SOLID) == 0){
if ((flags & BlockProperties.F_SOLID) == 0) {
// Ignore non solid blocks anyway.
return false;
}
if ((flags & (BlockProperties.F_LIQUID | BlockProperties.F_IGN_PASSABLE | BlockProperties.F_STAIRS | BlockProperties.F_VARIABLE)) != 0){
if ((flags & (BlockProperties.F_LIQUID | BlockProperties.F_IGN_PASSABLE | BlockProperties.F_STAIRS | BlockProperties.F_VARIABLE)) != 0) {
// Special cases.
// TODO: F_VARIABLE: Bounding boxes are roughly right ?
return false;
}
if (!blockCache.isFullBounds(blockX, blockY, blockZ)) return false;
if (!blockCache.isFullBounds(blockX, blockY, blockZ)) {
return false;
}
return true;
}
/**
* Test if the primary line is on the block interacted with (may be a
* different one that the end point of ray-tracing).
*
* @return
*/
public boolean isTargetBlock() {
return targetX != Integer.MAX_VALUE && blockX == targetX && blockY == targetY && blockZ == targetZ;
}
/**
* Check if the block may be interacted through by use of some workaround.
*
* @param blockX
* @param blockY
* @param blockZ
* @return
*/
private final boolean allowsWorkaround(final int blockX, final int blockY, final int blockZ) {
// TODO: This allows some bypasses for "strange" setups.
// TODO: Recode this/other.
// TODO: This could allow some bypasses for "strange" setups.
// TODO: Consider using distance to target as heuristic ? [should not get smaller !?]
// TODO: Consider (min/max) offset for distance.
final int dX = blockX - lastBx;
final int dY = blockY - lastBy;
final int dZ = blockZ - lastBz;
final double dSq = dX * dX + dY * dY + dZ * dZ;
for (int i = 0; i < 6; i++){
// TODO: Limit distance more here !?
for (int i = 0; i < 6; i++) {
final int[] dir = incr[i];
final int rX = blockX + dir[0];
if (Math.abs(lastBx - rX) > 1) continue;
if (Math.abs(lastBx - rX) > 1) {
continue;
}
final int rY = blockY + dir[1];
if (Math.abs(lastBy - rY) > 1) continue;
if (Math.abs(lastBy - rY) > 1) {
continue;
}
final int rZ = blockZ + dir[2];
if (Math.abs(lastBz - rZ) > 1) continue;
if (Math.abs(lastBz - rZ) > 1) {
continue;
}
final int dRx = rX - lastBx;
final int dRy = rY - lastBy;
final int dRz = rZ - lastBz;
if (dRx * dRx + dRy * dRy + dRz * dRz <= dSq) continue;
if (!doesCollide(rX, rY, rZ)){
if (dRx * dRx + dRy * dRy + dRz * dRz <= dSq) {
continue;
}
if (!doesCollide(rX, rY, rZ)) {
// NOTE: Don't check "rX == targetBx && rZ == targetBz && rY == targetBy".
return true;
}
@ -125,22 +172,24 @@ public class InteractRayTracing extends RayTracing {
}
@Override
protected boolean step(int blockX, int blockY, int blockZ, double oX, double oY, double oZ, double dT, final boolean isPrimary) {
protected boolean step(final int blockX, final int blockY, final int blockZ, final double oX, final double oY, final double oZ, final double dT, final boolean isPrimary) {
// TODO: Make an optional, more precise check (like passable) ?
// TODO: Account for primary line vs. secondary.
// TODO: isEndBlock -> blockInteractedWith, because the offset edge might be on the next block.
if (isEndBlock() || !doesCollide(blockX, blockY, blockZ)){
// TODO: isTargetBlock checks the primary line (!, might be ok.).
if (isTargetBlock() || !doesCollide(blockX, blockY, blockZ)) {
if (isPrimary) {
lastBx = blockX;
lastBy = blockY;
lastBz = blockZ;
}
return true;
}
if (strict || blockX == lastBx && blockZ == lastBz && blockY == lastBy){
if (strict || blockX == lastBx && blockZ == lastBz && blockY == lastBy) {
collides = true;
return false;
}
// Check workarounds...
if (allowsWorkaround(blockX, blockY, blockZ)){
if (isPrimary && allowsWorkaround(blockX, blockY, blockZ)) {
lastBx = blockX;
lastBy = blockY;
lastBz = blockZ;

View File

@ -0,0 +1,145 @@
package fr.neatmonster.nocheatplus.test;
import org.bukkit.Material;
import org.junit.Test;
import fr.neatmonster.nocheatplus.logging.StaticLog;
import fr.neatmonster.nocheatplus.utilities.FakeBlockCache;
import fr.neatmonster.nocheatplus.utilities.InteractRayTracing;
import fr.neatmonster.nocheatplus.utilities.build.BuildParameters;
public class TestInteractRayTracing {
public final class CenteredInteractRayTracing extends InteractRayTracing {
private int centerX, centerY, centerZ;
public CenteredInteractRayTracing(boolean strict, int centerX, int centerY, int centerZ) {
super(strict);
this.centerX = centerX;
this.centerY = centerY;
this.centerZ = centerZ;
}
@Override
public void set(double x0, double y0, double z0, double x1, double y1, double z1) {
super.set(x0, y0, z0, x1, y1, z1, centerX, centerY, centerZ);
}
}
// TODO: Blunt copy and paste from TestPassableRayTracing, add something that makes sense.
public TestInteractRayTracing() {
StaticLog.setUseLogManager(false);
BlockTests.initBlockProperties();
StaticLog.setUseLogManager(true);
}
@Test
public void testAir() {
FakeBlockCache bc = new FakeBlockCache();
InteractRayTracing rt = new InteractRayTracing();
rt.setBlockCache(bc);
double[] coords = new double[]{0.5, 0.5, -0.5, 0.5, 0.5, 1.5};
rt.set(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
rt.loop();
if (rt.collides()) {
TestRayTracing.doFail("Expect not to collide with air.", coords);
}
if (rt.getStepsDone() > 4) {
TestRayTracing.doFail("Expect less than 4 steps for moving straight through a block of air.", coords);
}
rt.cleanup();
bc.cleanup();
}
/**
* Moving diagonally through an "empty corner", seen from above:<br>
* ox<br>
* xo
*/
@Test
public void testEmptyCorner() {
FakeBlockCache bc = new FakeBlockCache();
// The "empty corner" setup.
for (int y = 70 ; y < 73; y ++) {
bc.set(10, y, 10, Material.STONE);
bc.set(11, y, 11, Material.STONE);
}
// Ground.
for (int x = 9; x < 13; x++) {
for (int z = 9; z < 13; z++) {
bc.set(x, 69, z, Material.STONE);
}
}
// TODO: Make work with strict set to false.
InteractRayTracing rt = new InteractRayTracing(true);
//InteractRayTracing rt = new InteractRayTracing(false);
rt.setBlockCache(bc);
// TODO: More Directions, also just behind the corner.
double[][] setups = new double[][] {
// Slightly off the middle (11, y, 11)
{11.4, 70.0, 10.4, 10.6, 70.0, 11.4},
// Going exactly through the middle (11, y, 11)
{11.4, 70.0, 10.6, 10.6, 70.0, 11.4},
{11.5, 70.0, 10.5, 10.5, 70.0, 11.5},
};
TestRayTracing.runCoordinates(rt, setups, true, false, 3.0, true);
rt.cleanup();
bc.cleanup();
}
@Test
public void testWall() {
FakeBlockCache bc = new FakeBlockCache();
// Wall using full blocks.
bc.walls(0, 65, 0, 16, 67, 0, Material.STONE);
// Ground using full blocks (roughly 16 margin to each side).
bc.fill(-16, 64, -16, 32, 64, 16, Material.STONE);
// TODO: Test chest like bounds for target blocks.
InteractRayTracing rt = new InteractRayTracing(false);
rt.setBlockCache(bc);
// TODO: More cases, head inside block itself, angles, ...
double[][] noCollision = new double[][] {
{8.5, 66.75, 1.2 , 8.5, 65.8, 1.0},
{8.5, 66.75, 1.2 , 8.5, 69.0, 0.0}, // "Above enough".
};
TestRayTracing.runCoordinates(rt, noCollision, false, true, 3.0, true);
double[][] shouldCollide = new double[][] {
{8.5, 66.75, 1.2 , 8.5, 65.8, 0.0},
{8.5, 66.75, 1.2 , 8.5, 65.8, -0.2},
};
TestRayTracing.runCoordinates(rt, shouldCollide, true, false, 3.0, true);
rt.cleanup();
bc.cleanup();
}
@Test
public void testRoom() {
// TODO: Test for differing middle points (negative to positive range, random, selected rays).
FakeBlockCache bc = new FakeBlockCache();
bc.room(-1, 64, -1, 1, 66, 1, Material.STONE);
// Note that reversed checks are slightly different with the centered version, but start + end blocks are air anyway.
double[] middle = new double[] {0.5, 65.5, 0.5}; // Free spot.
// TODO: Must work with strict set to false.
//CenteredInteractRayTracing rt = new CenteredInteractRayTracing(false, 0, 65, 0);
CenteredInteractRayTracing rt = new CenteredInteractRayTracing(true, 0, 65, 0);
rt.setBlockCache(bc);
double[][] pastFailures = new double[][] {
{2.1393379885667643, 67.18197661625649, 1.7065201483677281 , 0.0, 65.0, 0.0},
{2.7915547712543676, 66.65545738305906, 1.310222428430474 , 0.0, 65.0, 0.0},
{0.0, 65.0, 4.5 , 0.0, 65.0, 1.0}, // strict is false.
{-3.5, 61.5, -3.5 , 0.0, 65.0, 0.0} // strict is false.
};
TestRayTracing.runCoordinates(rt, pastFailures, true, false, 3, true);
boolean intense = BuildParameters.testLevel > 1;
for (double x = -0.5; x < 1.0; x += 0.5) {
for (double y = -0.5; y < 1.0; y += 0.5) {
for (double z = -0.5; z < 1.0; z += 0.5) {
double add = Math.abs(x) + Math.abs(y) + Math.abs(z);
TestRayTracing.runCenterRays(rt, middle[0] + x, middle[1] + y, middle[2] + z, 2.0 + add, intense ? 10000 : 1000, true, false, true);
}
}
}
rt.cleanup();
bc.cleanup();
}
}

View File

@ -6,6 +6,7 @@ import org.junit.Test;
import fr.neatmonster.nocheatplus.logging.StaticLog;
import fr.neatmonster.nocheatplus.utilities.FakeBlockCache;
import fr.neatmonster.nocheatplus.utilities.PassableRayTracing;
import fr.neatmonster.nocheatplus.utilities.build.BuildParameters;
public class TestPassableRayTracing {
@ -93,6 +94,7 @@ public class TestPassableRayTracing {
// Going exactly through the middle (11, y, 11)
{11.4, 70.0, 10.6, 10.6, 70.0, 11.4},
{11.5, 70.0, 10.5, 10.5, 70.0, 11.5},
//{11.5, 70.0, 10.5, 10.99999999999, 70.0, 11.00000000001}, // TODO: Craft something here
};
TestRayTracing.runCoordinates(rt, setups, true, false, 3.0, true);
rt.cleanup();
@ -157,4 +159,24 @@ public class TestPassableRayTracing {
bc.cleanup();
}
@Test
public void testRoom() {
FakeBlockCache bc = new FakeBlockCache();
bc.room(-1, 64, -1, 1, 66, 1, Material.STONE);
double[] middle = new double[] {0.5, 65.5, 0.5}; // Free spot.
PassableRayTracing rt = new PassableRayTracing();
rt.setBlockCache(bc);
boolean intense = BuildParameters.testLevel > 1;
for (double x = -0.5; x < 1.0; x += 0.5) {
for (double y = -0.5; y < 1.0; y += 0.5) {
for (double z = -0.5; z < 1.0; z += 0.5) {
double add = Math.abs(x) + Math.abs(y) + Math.abs(z);
TestRayTracing.runCenterRays(rt, middle[0] + x, middle[1] + y, middle[2] + z, 2.0 + add, intense ? 10000 : 1000, true, false, true);
}
}
}
rt.cleanup();
bc.cleanup();
}
}

View File

@ -5,6 +5,7 @@ import static org.junit.Assert.fail;
import java.util.Random;
import org.bukkit.Location;
import org.bukkit.util.Vector;
import org.junit.Test;
import fr.neatmonster.nocheatplus.utilities.RayTracing;
@ -259,6 +260,16 @@ public class TestRayTracing {
// TODO: Add tests for typical coordinates a with interact, passable.
}
/**
*
* @param rt
* @param setup
* @param expectCollide
* @param expectNotCollide
* @param stepsManhattan
* @param reverse If set to true, end points will be exchanged for this run (not in addition).
* @param tag
*/
public static void runCoordinates(RayTracing rt, double[] setup, boolean expectCollide, boolean expectNotCollide, double stepsManhattan, boolean reverse, String tag) {
if (reverse) {
rt.set(setup[3], setup [4], setup[5], setup[0], setup[1], setup[2]);
@ -291,7 +302,7 @@ public class TestRayTracing {
* @param expectCollide
* @param expectNotCollide
* @param stepsManhattan
* @param testReversed
* @param testReversed If to test the each ray with reversed end points in addition.
*/
public static void runCoordinates(RayTracing rt, double[][] setups, boolean expectCollide, boolean expectNotCollide, double stepsManhattan, boolean testReversed) {
for (int i = 0; i < setups.length; i++) {
@ -304,4 +315,61 @@ public class TestRayTracing {
}
}
/**
* Run (some) standard directions towards the center.
* @param rt
* @param cX
* @param cY
* @param cZ
* @param length Rough length of the rays (might be applied per-axis including an additum).
* @param nRandom Test a number of random rays as well.
* @param expectCollide
* @param expectNotCollide
* @param testReversed If to test the each ray with reversed end points in addition.
*/
public static void runCenterRays(RayTracing rt, double cX, double cY, double cZ, double length, int nRandom, boolean expectCollide, boolean expectNotCollide, boolean testReversed) {
double[] mult = new double[] {-1.0, 0.0, 1.0};
for (int ix = 0; ix < 3; ix ++) {
for (int iy = 0; iy < 3; iy++) {
for (int iz = 0; iz < 3; iz++) {
if (ix == 1 && iy == 1 && iz == 1) {
// Skip the center itself.
continue;
}
double[] coords = new double[] {
cX + length * mult[ix],
cY + length * mult[iy],
cZ + length * mult[iz],
cX,
cY,
cZ
};
// TODO: Generate differing target points on/near middle as well.
TestRayTracing.runCoordinates(rt, coords, true, false, 3.0, false, "");
if (testReversed) {
TestRayTracing.runCoordinates(rt, coords, true, false, 3.0, true, "");
}
}
}
}
// TODO: Consider running block coordinates with larger radius (potentially all within some radius?).
for (int n = 0; n < nRandom; n ++) {
// TODO: Check if normalize is necessary.
// One totally random vector.
Vector vec = Vector.getRandom().normalize().multiply(length);
double[] coords = new double[] {
cX + vec.getX(),
cY + vec.getY(),
cZ + vec.getZ(),
cX,
cY,
cZ
};
TestRayTracing.runCoordinates(rt, coords, true, false, 3.0, false, "random");
if (testReversed) {
TestRayTracing.runCoordinates(rt, coords, true, false, 3.0, true, "random");
}
}
}
}