package com.supaham.commons.bukkit.raytracing; import com.google.common.base.Preconditions; import com.supaham.commons.bukkit.utils.ImmutableVector; import org.bukkit.util.Vector; /** * A class for iterating over a Ray-Trace. The necessary data is provided through {@link * #next(RayTraceIteratorEntry)} and the last entry can be retrieved through {@link #run()}. * <p /> * After instantiating this class, you will need to call {@link #run()} for it to actually * function. * <p /> */ public abstract class RayTraceIterator { protected final RayTraceData data; protected final double incrementBy; private double rayLength; private Vector lastVector; private Vector incrVector; private Vector direction; /** * Constructs a new {@link RayTraceIterator} with {@link RayTraceData} to control the ray-tracing * task. The {@code double} parameter, incrementBy, is how much to progress in the direction * every iteration. The progress starts at the {@link RayTraceData#getStart()} and ends at {@link * RayTraceData#getEnd()}. * <p /> * {@code incrementBy} as 1 is typically not a bad choice considering a block exists every one * meter (or block, basically). * * @param data data to ray-trace with * @param incrementBy increment constant for each iteration */ protected RayTraceIterator(RayTraceData data, double incrementBy) { Preconditions.checkNotNull(data, "data cannot be null."); Preconditions.checkArgument(incrementBy > 0, "increment value must be larger than 0."); this.data = data; this.incrementBy = incrementBy; defaults(); } protected abstract boolean next(RayTraceIteratorEntry next); private void defaults() { Vector distance = data.getEnd().subtract(data.getStart()).toVector(); this.rayLength = distance.length(); this.direction = distance.normalize().clone(); this.incrVector = this.direction.clone().multiply(incrementBy); this.lastVector = data.getStart().toVector(); } public RayTraceIteratorEntry run() { int step = 0; while (true) { Vector _lastVector = this.lastVector.clone(); this.lastVector.add(this.incrVector); // We're done if (data.getStart().distance(lastVector) >= rayLength) { break; } MovingObjectPosition mop = rayTrace(_lastVector, this.lastVector); Vector newVector = this.lastVector.clone(); if (mop != null) { newVector = mop.getPosition().toVector(); } RayTraceIteratorEntry next = new RayTraceIteratorEntry(new ImmutableVector(_lastVector), new ImmutableVector(newVector), mop, step++); if (next(next)) { defaults(); return next; } } defaults(); return null; } private MovingObjectPosition rayTrace(Vector start, Vector end) { return RayTracing.rayTraceBlocks(data.getWorld(), start, end, data.isStopOnLiquid(), data.isIgnoreBoundingBox(), data.isReturnLastCollidableBlock()); } public RayTraceData getData() { return data; } public double getIncrementBy() { return incrementBy; } public static final class RayTraceIteratorEntry { private final ImmutableVector lastVector; private final ImmutableVector newVector; private final MovingObjectPosition movingObjectPosition; private final int step; public RayTraceIteratorEntry(ImmutableVector lastVector, ImmutableVector newVector, MovingObjectPosition movingObjectPosition, int step) { this.lastVector = lastVector; this.newVector = newVector; this.movingObjectPosition = movingObjectPosition; this.step = step; } public ImmutableVector getLastVector() { return lastVector; } public ImmutableVector getNewVector() { return newVector; } public MovingObjectPosition getMovingObjectPosition() { return movingObjectPosition; } public int getStep() { return step; } } }