Let RayTracing test all combinations of transitions for all axes.

Previously only "random" transitions were taken, for simplicity. For the
sake of better debugging and consistency we check all combinations of
transitions now, calling the iteration with all transitions done at once
the "primary line", while calling step with a subset of transitions done
would be the "secondary line".

Currently an iteration might still end x-th digit off the target, so it
does not necessarily end on the target block itself. This is not a
problem for passable, but might be one for interaction and other
applications, thus this should be fixed at some point.
This commit is contained in:
asofold 2015-03-09 02:15:02 +01:00
parent e0f7e53c57
commit e871d205f6
5 changed files with 496 additions and 309 deletions

View File

@ -2242,7 +2242,7 @@ public class BlockProperties {
* @param z * @param z
* @param id * @param id
* @param bounds Not null: bounds of the block at x, y, z. * @param bounds Not null: bounds of the block at x, y, z.
* @param flags Block flags for the block at x, y, z. * @param flags Block flags for the block at x, y, z. Mix in F_COLLIDE_EDGES to disallow the "high edges" of blocks.
* @return * @return
*/ */
public static final boolean collidesBlock(final BlockCache access, final double minX, double minY, final double minZ, final double maxX, final double maxY, final double maxZ, final int x, final int y, final int z, final int id, final double[] bounds, final long flags) { public static final boolean collidesBlock(final BlockCache access, final double minX, double minY, final double minZ, final double maxX, final double maxY, final double maxZ, final int x, final int y, final int z, final int id, final double[] bounds, final long flags) {

View File

@ -1,6 +1,5 @@
package fr.neatmonster.nocheatplus.utilities; 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. * Rough ray-tracing for interaction with something. This does not do any smart end-point guessing.
@ -8,151 +7,148 @@ import org.bukkit.Location;
* *
*/ */
public class InteractRayTracing extends RayTracing { public class InteractRayTracing extends RayTracing {
private static final int[][] incr = new int[][]{
{1, 0, 0},
{0, 1, 0},
{0, 0, 1},
{-1, 0, 0},
{0, -1, 0},
{0, 0, -1},
};
protected BlockCache blockCache = null;
protected boolean collides = false;
protected boolean strict = false;
protected int lastBx, lastBy, lastBz;
protected int targetBx, targetBy, targetBz;
public InteractRayTracing(){
super();
}
public InteractRayTracing(boolean strict){
super();
this.strict = strict;
}
public BlockCache getBlockCache() {
return blockCache;
}
public void setBlockCache(BlockCache blockCache) { private static final int[][] incr = new int[][]{
this.blockCache = blockCache; {1, 0, 0},
} {0, 1, 0},
{0, 0, 1},
/* (non-Javadoc) {-1, 0, 0},
* @see fr.neatmonster.nocheatplus.utilities.RayTracing#set(double, double, double, double, double, double) {0, -1, 0},
*/ {0, 0, -1},
@Override };
public void set(double x0, double y0, double z0, double x1, double y1, double z1) {
super.set(x0, y0, z0, x1, y1, z1);
collides = false;
lastBx = blockX;
lastBy = blockY;
lastBz = blockZ;
targetBx = Location.locToBlock(x1);
targetBy = Location.locToBlock(y1);
targetBz = Location.locToBlock(z1);
}
public boolean collides(){
return collides;
}
/** protected BlockCache blockCache = null;
* Remove reference to BlockCache.
*/ protected boolean collides = false;
public void cleanup(){
if (blockCache != null){ protected boolean strict = false;
blockCache = null;
} protected int lastBx, lastBy, lastBz;
}
public InteractRayTracing(){
/** super();
* Simplistic collision check (can interact through this block). }
* @param blockX
* @param blockY public InteractRayTracing(boolean strict){
* @param blockZ super();
* @return this.strict = strict;
*/ }
private final boolean doesCollide(int blockX, int blockY, int blockZ){
final int id = blockCache.getTypeId(blockX, blockY, blockZ); public BlockCache getBlockCache() {
final long flags = BlockProperties.getBlockFlags(id); return blockCache;
if ((flags & BlockProperties.F_SOLID) == 0){ }
// Ignore non solid blocks anyway.
return false; public void setBlockCache(BlockCache blockCache) {
} this.blockCache = blockCache;
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 ? /* (non-Javadoc)
return false; * @see fr.neatmonster.nocheatplus.utilities.RayTracing#set(double, double, double, double, double, double)
} */
if (!blockCache.isFullBounds(blockX, blockY, blockZ)) return false; @Override
return true; public void set(double x0, double y0, double z0, double x1, double y1, double z1) {
} super.set(x0, y0, z0, x1, y1, z1);
collides = false;
/** lastBx = blockX;
* Check if the block may be interacted through by use of some workaround. lastBy = blockY;
* @param blockX lastBz = blockZ;
* @param blockY }
* @param blockZ
* @return public boolean collides(){
*/ return collides;
private final boolean allowsWorkaround(final int blockX, final int blockY, final int blockZ) { }
// TODO: This allows some bypasses for "strange" setups.
// TODO: Consider using distance to target as heuristic ? [should not get smaller !?] /**
final int dX = blockX - lastBx; * Remove reference to BlockCache.
final int dY = blockY - lastBy; */
final int dZ = blockZ - lastBz; public void cleanup(){
final double dSq = dX * dX + dY * dY + dZ * dZ; if (blockCache != null){
for (int i = 0; i < 6; i++){ blockCache = null;
final int[] dir = incr[i]; }
final int rX = blockX + dir[0]; }
if (Math.abs(lastBx - rX) > 1) continue;
final int rY = blockY + dir[1]; /**
if (Math.abs(lastBy - rY) > 1) continue; * Simplistic collision check (can interact through this block).
final int rZ = blockZ + dir[2]; * @param blockX
if (Math.abs(lastBz - rZ) > 1) continue; * @param blockY
final int dRx = rX - lastBx; * @param blockZ
final int dRy = rY - lastBy; * @return
final int dRz = rZ - lastBz; */
if (dRx * dRx + dRy * dRy + dRz * dRz <= dSq) continue; private final boolean doesCollide(int blockX, int blockY, int blockZ){
if (!doesCollide(rX, rY, rZ)){ final int id = blockCache.getTypeId(blockX, blockY, blockZ);
// NOTE: Don't check "rX == targetBx && rZ == targetBz && rY == targetBy". final long flags = BlockProperties.getBlockFlags(id);
return true; if ((flags & BlockProperties.F_SOLID) == 0){
} // Ignore non solid blocks anyway.
} return false;
return false; }
} if ((flags & (BlockProperties.F_LIQUID | BlockProperties.F_IGN_PASSABLE | BlockProperties.F_STAIRS | BlockProperties.F_VARIABLE)) != 0){
// Special cases.
@Override // TODO: F_VARIABLE: Bounding boxes are roughly right ?
protected boolean step(int blockX, int blockY, int blockZ, double oX, double oY, double oZ, double dT) { return false;
// TODO: Make an optional, more precise check (like passable) ? }
if (blockX == targetBx && blockZ == targetBz && blockY == targetBy || !doesCollide(blockX, blockY, blockZ)){ if (!blockCache.isFullBounds(blockX, blockY, blockZ)) return false;
lastBx = blockX; return true;
lastBy = blockY; }
lastBz = blockZ;
return true; /**
} * Check if the block may be interacted through by use of some workaround.
if (strict || blockX == lastBx && blockZ == lastBz && blockY == lastBy){ * @param blockX
collides = true; * @param blockY
return false; * @param blockZ
} * @return
// Check workarounds... */
if (allowsWorkaround(blockX, blockY, blockZ)){ private final boolean allowsWorkaround(final int blockX, final int blockY, final int blockZ) {
lastBx = blockX; // TODO: This allows some bypasses for "strange" setups.
lastBy = blockY; // TODO: Consider using distance to target as heuristic ? [should not get smaller !?]
lastBz = blockZ; final int dX = blockX - lastBx;
return true; final int dY = blockY - lastBy;
} final int dZ = blockZ - lastBz;
// No workaround found. final double dSq = dX * dX + dY * dY + dZ * dZ;
collides = true; for (int i = 0; i < 6; i++){
return false; final int[] dir = incr[i];
} final int rX = blockX + dir[0];
if (Math.abs(lastBx - rX) > 1) continue;
final int rY = blockY + dir[1];
if (Math.abs(lastBy - rY) > 1) continue;
final int rZ = blockZ + dir[2];
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)){
// NOTE: Don't check "rX == targetBx && rZ == targetBz && rY == targetBy".
return true;
}
}
return false;
}
@Override
protected boolean step(int blockX, int blockY, int blockZ, double oX, double oY, double oZ, 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)){
lastBx = blockX;
lastBy = blockY;
lastBz = blockZ;
return true;
}
if (strict || blockX == lastBx && blockZ == lastBz && blockY == lastBy){
collides = true;
return false;
}
// Check workarounds...
if (allowsWorkaround(blockX, blockY, blockZ)){
lastBx = blockX;
lastBy = blockY;
lastBz = blockZ;
return true;
}
// No workaround found.
collides = true;
return false;
}
} }

View File

@ -2,81 +2,81 @@ package fr.neatmonster.nocheatplus.utilities;
public class PassableRayTracing extends RayTracing{ public class PassableRayTracing extends RayTracing{
protected BlockCache blockCache = null; protected BlockCache blockCache = null;
protected boolean collides = false;
protected boolean ignorefirst = false;
public BlockCache getBlockCache() { protected boolean collides = false;
return blockCache;
}
public void setBlockCache(BlockCache blockCache) { protected boolean ignorefirst = false;
this.blockCache = blockCache;
}
/**
* Set from PlayerLocation instances. Currently takes BlockCache from the from-location.
* @param from
* @param to
*/
public void set(final PlayerLocation from, final PlayerLocation to){
set(from.getX(), from.getY(), from.getZ(), to.getX(), to.getY(), to.getZ());
setBlockCache(from.getBlockCache()); // TODO: This might better be done extra.
}
/* (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) {
super.set(x0, y0, z0, x1, y1, z1);
collides = false;
ignorefirst = false;
}
public boolean collides(){
return collides;
}
/**
* Ignore the first block. Must be called after set, because set will override this with false.
*/
public void setIgnorefirst(){
this.ignorefirst = true;
}
/** public BlockCache getBlockCache() {
* Test if the first block is set to be ignored (resets to false with set). return blockCache;
* @return }
*/
public boolean getIgnoreFirst(){
return ignorefirst;
}
/** public void setBlockCache(BlockCache blockCache) {
* Remove reference to BlockCache. this.blockCache = blockCache;
*/ }
public void cleanup(){
if (blockCache != null){ /**
blockCache = null; * Set from PlayerLocation instances. Currently takes BlockCache from the from-location.
} * @param from
} * @param to
*/
@Override public void set(final PlayerLocation from, final PlayerLocation to){
protected boolean step(final int blockX, final int blockY, final int blockZ, final double oX, final double oY, final double oZ, final double dT) { set(from.getX(), from.getY(), from.getZ(), to.getX(), to.getY(), to.getZ());
// Just delegate. setBlockCache(from.getBlockCache()); // TODO: This might better be done extra.
if (step == 1 && ignorefirst){ }
return true;
} /* (non-Javadoc)
if (BlockProperties.isPassableRay(blockCache, blockX, blockY, blockZ, oX, oY, oZ, dX, dY, dZ, dT)){ * @see fr.neatmonster.nocheatplus.utilities.RayTracing#set(double, double, double, double, double, double)
return true; */
} @Override
else{ public void set(double x0, double y0, double z0, double x1, double y1, double z1) {
collides = true; super.set(x0, y0, z0, x1, y1, z1);
return false; collides = false;
} ignorefirst = false;
} }
public boolean collides(){
return collides;
}
/**
* Ignore the first block. Must be called after set, because set will override this with false.
*/
public void setIgnorefirst(){
this.ignorefirst = true;
}
/**
* Test if the first block is set to be ignored (resets to false with set).
* @return
*/
public boolean getIgnoreFirst(){
return ignorefirst;
}
/**
* Remove reference to BlockCache.
*/
public void cleanup(){
if (blockCache != null){
blockCache = null;
}
}
@Override
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) {
// Just delegate.
if (step == 1 && ignorefirst){
return true;
}
if (BlockProperties.isPassableRay(blockCache, blockX, blockY, blockZ, oX, oY, oZ, dX, dY, dZ, dT)){
return true;
}
else{
collides = true;
return false;
}
}
} }

View File

@ -33,9 +33,20 @@ public abstract class RayTracing {
/** Tolerance for time, for checking the abort condition: 1.0 - t <= tol . */ /** Tolerance for time, for checking the abort condition: 1.0 - t <= tol . */
protected double tol = 0.0; protected double tol = 0.0;
/** Counting the number of steps. Step is incremented before calling step(), and is 0 after set(...). Checking this from within step means to get the current step number, checking after loop gets the number of steps done. */ /**
* Counting the number of steps along the primary line. Step is incremented
* before calling step(), and is 0 after set(...). Checking this from within
* step means to get the current step number, checking after loop gets the
* number of steps done.
*/
protected int step = 0; protected int step = 0;
/** If to call stepSecondary at all (secondary transitions).*/
protected boolean secondaryStep = true;
/** If to pass secondary transitions if ANY sub-call succeeds. */
protected boolean secondaryPassSingular = false;
/** Maximum steps that will be done. */ /** Maximum steps that will be done. */
private int maxSteps = Integer.MAX_VALUE; private int maxSteps = Integer.MAX_VALUE;
@ -80,11 +91,19 @@ public abstract class RayTracing {
step = 0; step = 0;
} }
private static final double tDiff(final double dTotal, final double offset, final int block, final int endBlock) { /**
*
* @param dTotal
* @param offset
* @param isEndBlock If the end block coordinate is reached for this axis.
* @return
*/
private static final double tDiff(final double dTotal, final double offset, final boolean isEndBlock) {
// TODO: endBlock check only for == not </> ?
if (dTotal > 0.0) { if (dTotal > 0.0) {
if (offset >= 1.0) { if (offset >= 1.0) {
// Static block change (e.g. diagonal move). // Static block change (e.g. diagonal move).
return 0.0; // block == endBlock ? Double.MAX_VALUE : 0.0; return isEndBlock ? Double.MAX_VALUE : 0.0;
} else { } else {
return (1.0 - offset) / dTotal; return (1.0 - offset) / dTotal;
} }
@ -92,7 +111,7 @@ public abstract class RayTracing {
else if (dTotal < 0.0) { else if (dTotal < 0.0) {
if (offset <= 0.0) { if (offset <= 0.0) {
// Static block change (e.g. diagonal move). // Static block change (e.g. diagonal move).
return 0.0; //block == endBlock ? Double.MAX_VALUE : 0.0; return isEndBlock ? Double.MAX_VALUE : 0.0;
} else { } else {
return offset / -dTotal; return offset / -dTotal;
} }
@ -106,23 +125,31 @@ public abstract class RayTracing {
* Loop through blocks. * Loop through blocks.
*/ */
public void loop() { public void loop() {
// TODO: Might intercept 0 dist ?
// Time to block edge. // Time to block edge.
double tX, tY, tZ, tMin; double tX, tY, tZ, tMin;
boolean changed; // Number of axes to make a transition for.
int transitions;
// Transition direction per axis.
boolean transX, transY, transZ;
// Actual loop.
// TODO: Fix last transition not taken sometimes (with "off by x-th digit" or "t=0 transition").
while (1.0 - t > tol) { while (1.0 - t > tol) {
// Determine smallest time to block edge. // Determine smallest time to block edge, per axis.
tX = tDiff(dX, oX, blockX, endBlockX); // TODO: if all coords are in the end block: ensure the full distance is taken towards the end coordinate.
tY = tDiff(dY, oY, blockY, endBlockY); tX = tDiff(dX, oX, blockX == endBlockX);
tZ = tDiff(dZ, oZ, blockZ, endBlockZ); tY = tDiff(dY, oY, blockY == endBlockY);
tMin = Math.min(tX, Math.min(tY, tZ)); tZ = tDiff(dZ, oZ, blockZ == endBlockZ);
// Adjust time.
tMin = Math.max(0.0, Math.min(tX, Math.min(tY, tZ)));
if (tMin == Double.MAX_VALUE) { if (tMin == Double.MAX_VALUE) {
// All differences are 0 (no progress). // All differences are 0 (no progress).
if (step < 1) { if (step < 1) {
// Allow one step. // Allow one step always.
tMin = 0.0; tMin = 0.0;
} else { }
else {
break; break;
} }
} }
@ -130,69 +157,202 @@ public abstract class RayTracing {
// Set to the remaining distance (does trigger). // Set to the remaining distance (does trigger).
tMin = 1.0 - t; tMin = 1.0 - t;
} }
// Call step with appropriate arguments.
// Step for the primary line.
step ++; step ++;
if (!step(blockX, blockY, blockZ, oX, oY, oZ, tMin)) { if (!step(blockX, blockY, blockZ, oX, oY, oZ, tMin, true)) {
break; break;
} }
if (t + tMin >= 1.0 - tol) { // && isEndBlock()) {
// Abort if arrived.
if (t + tMin >= 1.0 - tol && isEndBlock()) {
break; break;
} }
// Advance (add to t etc.).
changed = false; // Determine transitions, per axis.
transitions = 0;
transX = transY = transZ = false;
if (tX == tMin && blockX != endBlockX && dX != 0.0) {
transX = true;
transitions ++;
}
if (tY == tMin && blockY != endBlockY && dY != 0.0) {
transY = true;
transitions ++;
}
if (tZ == tMin && blockZ != endBlockZ && dZ != 0.0) {
transZ = true;
transitions ++;
}
// Advance on-block origin based on this move.
// TODO: Calculate "directly" based on this/next block or and/t?
oX = Math.min(1.0, Math.max(0.0, oX + tMin * dX)); oX = Math.min(1.0, Math.max(0.0, oX + tMin * dX));
oY = Math.min(1.0, Math.max(0.0, oY + tMin * dY)); oY = Math.min(1.0, Math.max(0.0, oY + tMin * dY));
oZ = Math.min(1.0, Math.max(0.0, oZ + tMin * dZ)); oZ = Math.min(1.0, Math.max(0.0, oZ + tMin * dZ));
// TODO: Consider Heuristic change of the checking order for dy > 0 vs. dy < 0.
// x // Advance time.
if (tX == tMin && blockX != endBlockX) { t = Math.min(1.0, t + tMin);
if (dX < 0) {
oX = 1.0; // Handle block transitions.
blockX --; if (transitions > 0) {
changed = true; if (!handleTransitions(transitions, transX, transY, transZ, tMin)) {
} break;
else if (dX > 0) {
oX = 0.0;
blockX ++;
changed = true;
} }
} }
if (!changed) {
// y // Abort if done or exceeded maxSteps.
if (tY == tMin && blockY != endBlockY) { if (transitions == 0 || step >= maxSteps) {
if (dY < 0) {
oY = 1.0;
blockY --;
changed = true;
}
else if (dY > 0) {
oY = 0.0;
blockY ++;
changed = true;
}
}
if (!changed) {
// z
if (tZ == tMin && blockZ != endBlockZ) {
if (dZ < 0) {
oZ = 1.0;
blockZ --;
changed = true;
}
else if (dZ > 0) {
oZ = 0.0;
blockZ ++;
changed = true;
}
}
}
}
t += tMin;
if (!changed || step >= maxSteps) {
break; break;
} }
} }
// TODO: Catch special case with going beyond coordinates. }
/**
*
* @param transitions
* @param transX
* @param transY
* @param transZ
* @param tMin
* @return If to continue at all.
*/
protected boolean handleTransitions(final int transitions, final boolean transX, final boolean transY, final boolean transZ, final double tMin) {
// Secondary transitions.
if (transitions > 1 && secondaryStep) {
if (!handleSecondaryTransitions(transitions, transX, transY, transZ, tMin)) {
return false;
}
}
// Apply all transitions to the primary line.
if (transX) {
if (dX > 0.0) {
blockX ++;
oX = 0.0;
}
else {
blockX --;
oX = 1.0;
}
}
if (transY) {
if (dY > 0.0) {
blockY ++;
oY = 0.0;
}
else {
blockY --;
oY = 1.0;
}
}
if (transZ) {
if (dZ > 0.0) {
blockZ ++;
oZ = 0.0;
}
else {
blockZ --;
oZ = 1.0;
}
}
return true; // Continue loop.
}
/**
*
* @param transitions
* @param transX
* @param transY
* @param transZ
* @param tMin
* @return If to continue at all.
*/
protected boolean handleSecondaryTransitions(final int transitions, final boolean transX, final boolean transY, final boolean transZ, final double tMin) {
// Handle one transition.
if (transX) {
if (step(blockX + (dX > 0 ? 1 : -1), blockY, blockZ, dX > 0 ? 0.0 : 1.0, oY, oZ, 0.0, false)) {
if (secondaryPassSingular) {
return true;
}
}
else if (!secondaryPassSingular) {
return false;
}
}
if (transY) {
if (step(blockX, blockY + (dY > 0 ? 1 : -1), blockZ, oX, dY > 0 ? 0.0 : 1.0, oZ, 0.0, false)) {
if (secondaryPassSingular) {
return true;
}
}
else if (!secondaryPassSingular) {
return false;
}
}
if (transZ) {
if (step(blockX, blockY, blockZ + (dZ > 0 ? 1 : -1), oX, oY, dZ > 0 ? 0.0 : 1.0, 0.0, false)) {
if (secondaryPassSingular) {
return true;
}
}
else if (!secondaryPassSingular) {
return false;
}
}
// Handle two transitions.
if (transitions == 3) {
if (!handleSecondaryDoubleTransitions(transitions, transX, transY, transZ, tMin)) {
return false;
}
}
// If secondaryPassSingular is true, all returned false, otherwise none returned false.
return !secondaryPassSingular;
}
/**
*
* @param transitions
* @param transX
* @param transY
* @param transZ
* @param tMin
* @return
*/
protected boolean handleSecondaryDoubleTransitions(final int transitions, final boolean transX, final boolean transY, final boolean transZ, final double tMin) {
// Two transitions at once, thus step directly.
// X and Y.
if (step(blockX + (dX > 0 ? 1 : -1), blockY + (dY > 0 ? 1 : -1), blockZ, dX > 0 ? 0.0 : 1.0, dY > 0 ? 0.0 : 1.0, oZ, 0.0, false)) {
if (secondaryPassSingular) {
return true;
}
}
else if (!secondaryPassSingular) {
return false;
}
// X and Z.
if (step(blockX + (dX > 0 ? 1 : -1), blockY, blockZ + (dZ > 0 ? 1 : -1), dX > 0 ? 0.0 : 1.0, oY, dZ > 0 ? 0.0 : 1.0, 0.0, false)) {
if (secondaryPassSingular) {
return true;
}
}
else if (!secondaryPassSingular) {
return false;
}
// Y and Z.
if (step(blockX, blockY + (dY > 0 ? 1 : -1), blockZ + (dZ > 0 ? 1 : -1), oX, dY > 0 ? 0.0 : 1.0, dZ > 0 ? 0.0 : 1.0, 0.0, false)) {
if (secondaryPassSingular) {
return true;
}
}
else if (!secondaryPassSingular) {
return false;
}
// If secondaryPassSingular is true, all returned false, otherwise none returned false.
return !secondaryPassSingular;
} }
/** /**
@ -203,6 +363,10 @@ public abstract class RayTracing {
return false; return false;
} }
/**
* (Might later get changed to protected visibility.)
* @return
*/
public boolean isEndBlock() { public boolean isEndBlock() {
return blockX == endBlockX && blockY == endBlockY && blockZ == endBlockZ; return blockX == endBlockX && blockY == endBlockY && blockZ == endBlockZ;
} }
@ -236,8 +400,24 @@ public abstract class RayTracing {
/** /**
* One step in the loop. * One step in the loop.
* @return If to continue loop. *
* @param blockX
* The block coordinates regarded in this step.
* @param blockY
* @param blockZ
* @param oX
* Origin relative to the block coordinates.
* @param oY
* @param oZ
* @param dT
* Amount of time regarded in this step (note that 0.0 is
* possible for transitions).
* @param isPrimary
* If this is along the primary line, for which all transitions
* are done at once. The secondary line would cover all
* combinations of transitions off the primary line.
* @return If to continue processing at all.
*/ */
protected abstract boolean step(int blockX, int blockY, int blockZ, double oX, double oY, double oZ, double dT); protected abstract boolean step(int blockX, int blockY, int blockZ, double oX, double oY, double oZ, double dT, boolean isPrimary);
} }

View File

@ -21,6 +21,7 @@ public class TestRayTracing {
protected static double maxFactor = 9.0; protected static double maxFactor = 9.0;
protected static int maxSteps(double dX, double dY, double dZ) { protected static int maxSteps(double dX, double dY, double dZ) {
// TODO: Better calculation.
return (int) (maxFactor * (1 + Math.abs(dX) + Math.abs(dY) + Math.abs(dZ))); return (int) (maxFactor * (1 + Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)));
} }
@ -32,7 +33,7 @@ public class TestRayTracing {
@Override @Override
protected boolean step(int blockX, int blockY, int blockZ, double oX, protected boolean step(int blockX, int blockY, int blockZ, double oX,
double oY, double oZ, double dT) { double oY, double oZ, double dT, boolean isPrimary) {
if (step > maxSteps(dX, dY, dZ)) { if (step > maxSteps(dX, dY, dZ)) {
System.out.println("[WARNING] Max steps exceeded: " + maxSteps(dX, dY, dZ)); System.out.println("[WARNING] Max steps exceeded: " + maxSteps(dX, dY, dZ));
return false; return false;
@ -80,7 +81,7 @@ public class TestRayTracing {
protected double ldt = 0; protected double ldt = 0;
protected int step = 0; protected double lox, loy, loz;
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.neatmonster.nocheatplus.utilities.RayTracing#set(double, double, double, double, double, double) * @see fr.neatmonster.nocheatplus.utilities.RayTracing#set(double, double, double, double, double, double)
@ -91,8 +92,10 @@ public class TestRayTracing {
lbx = blockX - 1; lbx = blockX - 1;
lby = blockY - 1; lby = blockY - 1;
lbz = blockZ - 1; lbz = blockZ - 1;
lox = oX;
loy = oY;
loz = oZ;
ldt = 0; ldt = 0;
step = 0;
} }
// private boolean ignEdge(double offset, double dTotal) { // private boolean ignEdge(double offset, double dTotal) {
@ -100,9 +103,8 @@ public class TestRayTracing {
// } // }
@Override @Override
protected boolean step(int blockX, int blockY, int blockZ, double oX, double oY, double oZ, double dT) { protected boolean step(int blockX, int blockY, int blockZ, double oX, double oY, double oZ, double dT, boolean isPrimary) {
// TODO: This does not check last step for some occasions where it should. // TODO: This does not check last step for some occasions where it should.
step ++;
if (dT < 0.0) { if (dT < 0.0) {
doFail("dT < 0 at t = " + StringUtil.fdec3.format(t), coords); doFail("dT < 0 at t = " + StringUtil.fdec3.format(t), coords);
@ -130,21 +132,28 @@ public class TestRayTracing {
lbx = blockX; lbx = blockX;
lby = blockY; lby = blockY;
lbz = blockZ; lbz = blockZ;
lox = oX;
loy = oY;
loz = oZ;
ldt = dT; ldt = dT;
if (step > maxSteps(dX, dY, dZ)) doFail("max steps exceeded: " + maxSteps(dX, dY, dZ), coords); if (step > maxSteps(dX, dY, dZ)) {
doFail("max steps exceeded: " + maxSteps(dX, dY, dZ), coords);
}
return true; return true;
} }
private void checkOffset(double offset, String name) { private void checkOffset(double offset, String name) {
if (offset < 0.0 || offset > 1.0) doFail("Bad " + name + "-offset: " + offset, coords); if (offset < 0.0 || offset > 1.0) {
doFail("Bad " + name + "-offset: " + offset, coords);
}
} }
@Override @Override
public void loop() { public void loop() {
super.loop(); super.loop();
// checkBlockTarget(coords[3], blockX, oX, dX, ldt, "x"); checkBlockTarget(coords[3], lbx, lox, dX, ldt, "x");
// checkBlockTarget(coords[4], blockY, oY, dY, ldt, "y"); checkBlockTarget(coords[4], lby, loy, dY, ldt, "y");
// checkBlockTarget(coords[5], blockZ, oZ, dZ, ldt, "z"); checkBlockTarget(coords[5], lbz, loz, dZ, ldt, "z");
} }
private void checkBlockTarget(double target, int current, double offset, double dTotal, double dT, String name) { private void checkBlockTarget(double target, int current, double offset, double dTotal, double dT, String name) {
@ -153,19 +162,22 @@ public class TestRayTracing {
// TODO: Might do with or without these ? // TODO: Might do with or without these ?
// if (current == b + 1 && dTotal > 0 && offset == 0) return; // if (current == b + 1 && dTotal > 0 && offset == 0) return;
// if (current == b - 1 && dTotal < 0 && offset == 1) return; // if (current == b - 1 && dTotal < 0 && offset == 1) return;
if (Math.abs(dT * dTotal + offset + (double) current - target) <= 0.001) { double diff = Math.abs(dT * dTotal + offset + (double) current - target);
if (diff <= 0.001) {
// TODO: Test how far off this usually is...
// TODO: Narrow down by edge coordinates or so. // TODO: Narrow down by edge coordinates or so.
return; return;
} }
System.out.println(target + "|" + current + "|" + offset + "|" + dT * dTotal); System.out.println(target + "|" + current + "|" + offset + "|" + dT * dTotal);
// Failure. // Failure.
doFail("Bad target " + name + "-coordinate: " + current + " instead of " + b, coords); doFail("Bad target " + name + "-coordinate: " + current + " instead of " + b + " (" + diff + " off)", coords);
} }
} }
}; };
rt.loop(); rt.loop();
if (!rt.isEndBlock()) { if (!rt.isEndBlock()) {
//doFail("Incorrect end block.", coords); // TODO: Fix last transition not taken sometimes (with "off by x-th digit" or "t=0 transition").
// doFail("Incorrect end block.", coords);
} }
return rt; return rt;
} }
@ -177,9 +189,6 @@ public class TestRayTracing {
if (done != steps) { if (done != steps) {
doFail("Wrong number of steps: " + done + " instead of " + steps, coords); doFail("Wrong number of steps: " + done + " instead of " + steps, coords);
} }
if (!crt.isEndBlock()) {
//doFail("Incorrect end block.", coords);
}
return crt; return crt;
} }
@ -192,7 +201,7 @@ public class TestRayTracing {
public static RayTracing dumpRawRayTracing(final double[] coords) { public static RayTracing dumpRawRayTracing(final double[] coords) {
RayTracing rt = new RayTracing(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]) { RayTracing rt = new RayTracing(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]) {
@Override @Override
protected boolean step(int blockX, int blockY, int blockZ, double oX, double oY, double oZ, double dT) { protected boolean step(int blockX, int blockY, int blockZ, double oX, double oY, double oZ, double dT, boolean isPrimary) {
dump(blockX, blockY, blockZ, oX, oY, oZ, t, dT); dump(blockX, blockY, blockZ, oX, oY, oZ, t, dT);
if (step > maxSteps(dX, dY, dZ)) { if (step > maxSteps(dX, dY, dZ)) {
System.out.println("[WARNING] Max steps exceeded: " + maxSteps(dX, dY, dZ)); System.out.println("[WARNING] Max steps exceeded: " + maxSteps(dX, dY, dZ));
@ -211,7 +220,7 @@ public class TestRayTracing {
@Test @Test
public void testNumberOfSteps() { public void testNumberOfSteps() {
// Hand picked stuff. // Hand picked stuff.
checkNumberOfSteps(new double[]{0.5, 0.5, 0.5, 1.5, -0.5, 1.5}, 4); checkNumberOfSteps(new double[]{0.5, 0.5, 0.5, 1.5, -0.5, 1.5}, 2);
} }
@Test @Test
@ -219,13 +228,16 @@ public class TestRayTracing {
// Past failures / making a difference. // Past failures / making a difference.
for (double[] coords : new double[][] { for (double[] coords : new double[][] {
// Sort by x0. // Sort by x0.
new double[]{-9.873, -4.773, -3.387, -0.161, -1.879, -7.079}, {-9.873, -4.773, -3.387, -0.161, -1.879, -7.079},
new double[]{-3.0066423238842366, 0.8056808285866079, 5.359238045631369 , 2.0000000356757375, -2.3002237817433757, -5.889349195033338}, {-3.0066423238842366, 0.8056808285866079, 5.359238045631369 , 2.0000000356757375, -2.3002237817433757, -5.889349195033338},
new double[]{2.5619753859456917, -5.010424935746547, -7.39326637860553 , -4.678643570182639, -2.0000000105642313, -4.634727842675916}, {2.5619753859456917, -5.010424935746547, -7.39326637860553 , -4.678643570182639, -2.0000000105642313, -4.634727842675916},
new double[]{7.388348424961977, -8.000000029346532, -2.5365675909347507 , 2.17126848312847, 3.236994108042559, -8.423292642985071}, {7.388348424961977, -8.000000029346532, -2.5365675909347507 , 2.17126848312847, 3.236994108042559, -8.423292642985071},
new double[]{7.525633617461991, 2.654408573114717, 3.5119744782127893 , 9.99999995904821, 9.599753890871172, 6.721727939686946}, {7.525633617461991, 2.654408573114717, 3.5119744782127893 , 9.99999995904821, 9.599753890871172, 6.721727939686946},
new double[] {-6.0, -4.0, -3.0 , -4.0, -3.0, -2.0}, {1.1, 1.1, 1.1 , 1.3, 1.3, 1.3},
new double[]{-3.0, 3.0, -6.0 , 2.0, -3.0, 4.0}, {1.3, 1.3, 1.3 , 1.1, 1.1, 1.1},
{1.1, 1.1, 1.1 , 1.1, 1.1, 1.1},
{-6.0, -4.0, -3.0 , -4.0, -3.0, -2.0},
{-3.0, 3.0, -6.0 , 2.0, -3.0, 4.0},
}) { }) {
checkConsistency(coords); checkConsistency(coords);
} }
@ -237,7 +249,6 @@ public class TestRayTracing {
checkConsistency(randomCoords(10.0)); checkConsistency(randomCoords(10.0));
} }
// TODO: make these work.
for (int i = 0; i < (e ? 10000000 : 1000); i++) { for (int i = 0; i < (e ? 10000000 : 1000); i++) {
checkConsistency(randomBlockCoords(6)); checkConsistency(randomBlockCoords(6));
} }