Updates to the shape package

This commit is contained in:
Sollace 2022-10-08 10:57:16 +02:00
parent 645d6c6b7e
commit b8df7d0e67
19 changed files with 180 additions and 221 deletions

View file

@ -66,7 +66,7 @@ public class RainboomAbilitySpell extends AbstractSpell {
source.findAllEntitiesInRange(RADIUS).forEach(e -> {
e.damage(MagicalDamageSource.create("rainboom", source).setBreakSunglasses(), 6);
});
EFFECT_RANGE.offset(source.getOrigin()).getBlockPositions().forEach(pos -> {
EFFECT_RANGE.translate(source.getOrigin()).getBlockPositions().forEach(pos -> {
BlockState state = source.getReferenceWorld().getBlockState(pos);
if (state.isIn(UTags.FRAGILE) && canBreak(pos, owner)) {
owner.world.breakBlock(pos, true);

View file

@ -155,7 +155,7 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileSpell
if (radius > 2) {
Vec3d origin = getOrigin(source);
new Sphere(false, radius).offset(origin).getBlockPositions().forEach(i -> {
new Sphere(false, radius).translate(origin).getBlockPositions().forEach(i -> {
if (!canAffect(source, i)) {
return;
}

View file

@ -66,7 +66,7 @@ public class FireSpell extends AbstractAreaEffectSpell implements ProjectileSpel
generateParticles(source);
}
return new Sphere(false, Math.max(0, 4 + getTraits().get(Trait.POWER))).offset(source.getOrigin()).getBlockPositions().reduce(false,
return new Sphere(false, Math.max(0, 4 + getTraits().get(Trait.POWER))).translate(source.getOrigin()).getBlockPositions().reduce(false,
(r, i) -> source.canModifyAt(i) && applyBlocks(source.getReferenceWorld(), i),
(a, b) -> a || b)
|| applyEntities(null, source.getReferenceWorld(), source.getOriginVector());

View file

@ -51,7 +51,7 @@ public class HydrophobicSpell extends AbstractSpell {
if (!source.isClient()) {
World world = source.getReferenceWorld();
Shape area = new Sphere(false, getRange(source)).offset(source.getOriginVector());
Shape area = new Sphere(false, getRange(source)).translate(source.getOriginVector());
storedFluidPositions.removeIf(entry -> {
if (!area.isPointInside(Vec3d.ofCenter(entry.pos()))) {

View file

@ -42,7 +42,7 @@ public class IceSpell extends AbstractSpell {
public boolean tick(Caster<?> source, Situation situation) {
boolean submerged = source.getEntity().isSubmergedInWater() || source.getEntity().isSubmergedIn(FluidTags.LAVA);
long blocksAffected = OUTER_RANGE.offset(source.getOrigin()).getBlockPositions().filter(i -> {
long blocksAffected = OUTER_RANGE.translate(source.getOrigin()).getBlockPositions().filter(i -> {
if (source.canModifyAt(i) && applyBlockSingle(source.getEntity(), source.getReferenceWorld(), i, situation)) {
if (submerged & source.getOrigin().isWithinDistance(i, RADIUS - 1)) {

View file

@ -43,7 +43,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
private float pitch;
private float yaw;
private PointGenerator particleArea = PARTICLE_AREA;
private Shape particleArea = PARTICLE_AREA;
protected PortalSpell(CustomisedSpellType<?> type) {
super(type);

View file

@ -31,7 +31,7 @@ public class CloudsEscapingParticle extends GroundPoundParticle {
double columnHeight = 1 + age / 30;
new Sphere(true, columnHeight, 1, 1, 1)
.offset(center)
.translate(center)
.randomPoints(random)
.forEach(point -> {
ParticleUtils.spawnParticle(world, ParticleTypes.CLOUD, point, vel);

View file

@ -45,7 +45,7 @@ public class GroundPoundParticle extends Particle {
protected void spawnChildParticles() {
Vec3d vel = new Vec3d(0, (0.5 + (Math.sin(age) * 2.5)) * 5, 0);
new Sphere(true, age, 1, 0, 1).offset(getPos()).randomPoints(random).forEach(point -> {
new Sphere(true, age, 1, 0, 1).translate(getPos()).randomPoints(random).forEach(point -> {
BlockPos pos = new BlockPos(point).down();
BlockState state = world.getBlockState(pos);

View file

@ -34,7 +34,7 @@ public interface ParticleSource extends ParticleSpawner {
}
default void spawnParticles(Vec3d pos, PointGenerator area, int count, Consumer<Vec3d> particleSpawner) {
area.offset(pos).randomPoints(count, getReferenceWorld().random).forEach(particleSpawner);
area.translate(pos).randomPoints(count, getReferenceWorld().random).forEach(particleSpawner);
}
@Override

View file

@ -16,7 +16,7 @@ public interface ParticleUtils {
static PointGenerator getShapeFor(Entity entity) {
final double halfDist = Math.abs(entity.getStandingEyeHeight() / 1.5);
final double middle = entity.getBoundingBox().minY + halfDist;
return new Sphere(false, Math.abs((float)halfDist + entity.getWidth())).offset(new Vec3d(entity.getX(), middle, entity.getZ()));
return new Sphere(false, Math.abs((float)halfDist + entity.getWidth())).translate(new Vec3d(entity.getX(), middle, entity.getZ()));
}
static void spawnParticles(ParticleEffect effect, Entity entity, int count) {
@ -24,7 +24,7 @@ public interface ParticleUtils {
}
static void spawnParticles(ParticleEffect effect, World world, Vec3d origin, int count) {
spawnParticles(world, Sphere.UNIT_SPHERE.offset(origin), effect, count);
spawnParticles(world, Sphere.UNIT_SPHERE.translate(origin), effect, count);
}
static void spawnParticles(World world, PointGenerator points, ParticleEffect effect, int count) {

View file

@ -12,6 +12,10 @@ import net.minecraft.world.EntityView;
public interface VecHelper {
static Vec3d divide(Vec3d one, Vec3d two) {
return new Vec3d(one.x / two.x, one.y / two.y, one.z / two.z);
}
static Vec3d supply(DoubleSupplier rng) {
return new Vec3d(rng.getAsDouble(), rng.getAsDouble(), rng.getAsDouble());
}

View file

@ -49,20 +49,21 @@ public class Cylinder implements Shape {
}
@Override
public double getVolumeOfSpawnableSpace() {
public double getVolume() {
return volume;
}
private double computeSpawnableSpace() {
if (hollow) {
if (stretchX != stretchZ) {
double result = 3 * (stretchX + stretchZ);
result -= Math.sqrt((10 * stretchX * stretchZ) + 3 * ((stretchX * stretchX) + (stretchZ * stretchZ)));
return Math.PI * result;
}
return 2 * Math.PI * rad * stretchX * height;
if (!hollow) {
return Math.PI * (stretchX * rad * stretchZ * rad) * height;
}
return Math.PI * (stretchX * rad * stretchZ * rad) * height;
if (stretchX != stretchZ) {
double result = 3 * (stretchX + stretchZ);
result -= Math.sqrt((10 * stretchX * stretchZ) + 3 * ((stretchX * stretchX) + (stretchZ * stretchZ)));
return Math.PI * result;
}
return 2 * Math.PI * rad * stretchX * height;
}
@Override

View file

@ -1,84 +1,54 @@
package com.minelittlepony.unicopia.util.shape;
import com.minelittlepony.unicopia.util.VecHelper;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.random.Random;
/**
* A lonely Line. The simplest form of shape.
*
*/
public class Line implements Shape {
private final double len;
private final double length;
private final double dX;
private final double dY;
private final double dZ;
private final double sX;
private final double sY;
private final double sZ;
/**
* Creates a line with a given length, starting point, and gradient represented
* by another point.
*
* @param length Length of this line
* @param startX Offset X from origin
* @param startY Offset Y from origin
* @param startZ Offset Z from origin
* @param deltaX Change in X
* @param deltaY Change in Y
* @param deltaZ Change in Z
*/
public Line(double length, double startX, double startY, double startZ, double deltaX, double deltaY, double deltaZ) {
len = length;
dX = deltaX;
dY = deltaY;
dZ = deltaZ;
sX = startX;
sY = startY;
sZ = startZ;
}
private final Vec3d gradient;
public Line(Vec3d start, Vec3d end) {
Vec3d lenV = end.subtract(start);
length = lenV.length();
this.gradient = lenV.multiply(1/length);
}
len = lenV.length();
sX = start.x;
sY = start.y;
sZ = start.z;
dX = lenV.x / len;
dY = lenV.y / len;
dZ = lenV.z / len;
public Line(double length, Vec3d gradient) {
this.length = length;
this.gradient = gradient.normalize();
}
@Override
public double getVolumeOfSpawnableSpace() {
return len;
public double getVolume() {
return length;
}
@Override
public Vec3d computePoint(Random rand) {
double distance = MathHelper.nextDouble(rand, 0, len);
return new Vec3d(dX, dY, dZ).multiply(distance).add(sX, sY, sZ);
return gradient.multiply(MathHelper.nextDouble(rand, 0, length));
}
@Override
public boolean isPointInside(Vec3d point) {
return point.x/dX == point.y/dY && point.x/dX == point.z/dZ;
Vec3d divided = VecHelper.divide(point, gradient);
return divided.x == divided.y && divided.x == divided.z;
}
@Override
public Vec3d getLowerBound() {
return new Vec3d(sX, sY, sZ);
return Vec3d.ZERO;
}
@Override
public Vec3d getUpperBound() {
return new Vec3d(sX + dX, sY + dY, sZ + dZ).multiply(len);
return gradient.multiply(length);
}
}

View file

@ -2,7 +2,6 @@ package com.minelittlepony.unicopia.util.shape;
import java.util.Spliterator;
import java.util.Spliterators.AbstractSpliterator;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
@ -15,35 +14,21 @@ import net.minecraft.util.math.random.Random;
*Interface for a 3d shape, used for spawning particles in a designated area (or anything else you need shapes for).
*/
public interface PointGenerator {
/**
* Get the volume of space filled by this shape, or the surface area if hollow.
*
* @return double volume
*/
double getVolumeOfSpawnableSpace();
/**
* Computes a random coordinate that falls within this shape's designated area.
*/
Vec3d computePoint(Random rand);
/**
* Returns a sequence of random points dealed out to uniformly fill this shape's area.
*/
default Stream<Vec3d> randomPoints(Random rand) {
return randomPoints((int)getVolumeOfSpawnableSpace(), rand);
}
/**
* Returns a sequence of N random points.
*/
default Stream<Vec3d> randomPoints(int n, Random rand) {
AtomicInteger atom = new AtomicInteger(n);
return StreamSupport.stream(new AbstractSpliterator<Vec3d>(n, Spliterator.SIZED) {
private int index = n;
@Override
public boolean tryAdvance(Consumer<? super Vec3d> consumer) {
if (atom.decrementAndGet() >= 0) {
if (--index >= 0) {
consumer.accept(computePoint(rand));
return true;
}
@ -54,37 +39,17 @@ public interface PointGenerator {
}
/**
* Returns a stream of block positions.
* Returns a new shape with after applying additional rotation.
*/
Stream<BlockPos> getBlockPositions();
/**
* Returns a new point generator where all of its points are offset by the specified amount.
*/
default PointGenerator offset(Vec3i offset) {
return offset(Vec3d.of(offset));
default PointGenerator rotate(float pitch, float yaw) {
return pitch == 0 && yaw == 0 ? this : new RotatedPointGenerator(this, pitch, yaw);
}
/**
* Returns a new point generator where all of its points are offset by the specified amount.
*/
default PointGenerator offset(Vec3d offset) {
final PointGenerator source = this;
return new PointGenerator() {
@Override
public double getVolumeOfSpawnableSpace() {
return source.getVolumeOfSpawnableSpace();
}
default PointGenerator translate(Vec3i offset) {
return offset.equals(Vec3i.ZERO) ? this : translate(Vec3d.of(offset));
}
@Override
public Vec3d computePoint(Random rand) {
return source.computePoint(rand).add(offset);
}
@Override
public Stream<BlockPos> getBlockPositions() {
return source.getBlockPositions().map(pos -> pos.add(offset.x, offset.y, offset.z));
}
};
default PointGenerator translate(Vec3d offset) {
return offset.equals(Vec3d.ZERO) ? this : new TranslatedPointGenerator(this, offset);
}
}

View file

@ -0,0 +1,48 @@
package com.minelittlepony.unicopia.util.shape;
import net.minecraft.util.math.*;
import net.minecraft.util.math.random.Random;
record RotatedPointGenerator (
PointGenerator original,
float pitch,
float yaw
) implements Shape {
@Override
public double getVolume() {
return ((Shape)original).getVolume();
}
@Override
public Vec3d computePoint(Random rand) {
return rotate(original.computePoint(rand));
}
@Override
public Vec3d getLowerBound() {
return rotate(((Shape)original).getLowerBound());
}
@Override
public Vec3d getUpperBound() {
return rotate(((Shape)original).getUpperBound());
}
@Override
public boolean isPointInside(Vec3d point) {
return ((Shape)original).isPointInside(point.rotateY(-yaw).rotateX(-pitch));
}
private Vec3d rotate(Vec3d vec) {
return vec.rotateX(pitch).rotateY(yaw);
}
@Override
public Shape rotate(float pitch, float yaw) {
if (pitch == 0 && yaw == 0) {
return this;
}
return new RotatedPointGenerator(this, this.pitch + pitch, this.yaw + yaw);
}
}

View file

@ -1,51 +0,0 @@
package com.minelittlepony.unicopia.util.shape;
import net.minecraft.util.math.*;
import net.minecraft.util.math.random.Random;
final class RotatedShape implements Shape {
private final float pitch;
private final float yaw;
private final Shape original;
RotatedShape(Shape original, float pitch, float yaw) {
this.original = original;
this.pitch = pitch;
this.yaw = yaw;
}
@Override
public double getVolumeOfSpawnableSpace() {
return original.getVolumeOfSpawnableSpace();
}
@Override
public Vec3d computePoint(Random rand) {
return original.computePoint(rand).rotateX(pitch).rotateY(yaw);
}
@Override
public Vec3d getLowerBound() {
return original.getLowerBound().rotateX(pitch).rotateY(yaw);
}
@Override
public Vec3d getUpperBound() {
return original.getUpperBound().rotateX(pitch).rotateY(yaw);
}
@Override
public boolean isPointInside(Vec3d point) {
return original.isPointInside(point.rotateY(-yaw).rotateX(-pitch));
}
@Override
public Shape rotate(float pitch, float yaw) {
if (pitch == 0 && yaw == 0) {
return this;
}
return new RotatedShape(this, this.pitch + pitch, this.yaw + yaw);
}
}

View file

@ -10,6 +10,13 @@ import net.minecraft.util.math.random.Random;
*Interface for a 3d shape, used for spawning particles in a designated area (or anything else you need shapes for).
*/
public interface Shape extends PointGenerator {
/**
* Get the volume of space filled by this shape, or the surface area if hollow.
*
* @return double volume
*/
double getVolume();
/**
* Gets the lower bounds of the region occupied by this shape.
*/
@ -28,7 +35,6 @@ public interface Shape extends PointGenerator {
/**
* Returns a stream of all block positions that fit inside this shape.
*/
@Override
default Stream<BlockPos> getBlockPositions() {
return BlockPos.stream(
new BlockPos(getLowerBound()),
@ -36,60 +42,28 @@ public interface Shape extends PointGenerator {
).filter(pos -> isPointInside(Vec3d.ofCenter(pos)));
}
/**
* Returns a sequence of random points dealed out to uniformly fill this shape's area.
*/
default Stream<Vec3d> randomPoints(Random rand) {
return randomPoints((int)getVolume(), rand);
}
/**
* Returns a new shape with after applying additional rotation.
*/
@Override
default Shape rotate(float pitch, float yaw) {
if (pitch == 0 && yaw == 0) {
return this;
}
return new RotatedShape(this, pitch, yaw);
return pitch == 0 && yaw == 0 ? this : new RotatedPointGenerator(this, pitch, yaw);
}
/**
* Returns a new point generator where all of its points are offset by the specified amount.
*/
@Override
default Shape offset(Vec3i offset) {
return offset(Vec3d.of(offset));
default Shape translate(Vec3i offset) {
return offset.equals(Vec3i.ZERO) ? this : translate(Vec3d.of(offset));
}
/**
* Returns a new point generator where all of its points are offset by the specified amount.
*/
@Override
default Shape offset(Vec3d offset) {
final Shape source = this;
return new Shape() {
@Override
public double getVolumeOfSpawnableSpace() {
return source.getVolumeOfSpawnableSpace();
}
@Override
public Vec3d computePoint(Random rand) {
return source.computePoint(rand).add(offset);
}
@Override
public Stream<BlockPos> getBlockPositions() {
return source.getBlockPositions().map(pos -> pos.add(offset.x, offset.y, offset.z));
}
@Override
public Vec3d getLowerBound() {
return source.getLowerBound().add(offset);
}
@Override
public Vec3d getUpperBound() {
return source.getLowerBound().add(offset);
}
@Override
public boolean isPointInside(Vec3d point) {
return source.isPointInside(point.subtract(offset));
}
};
default Shape translate(Vec3d offset) {
return offset.equals(Vec3d.ZERO) ? this : new TranslatedPointGenerator(this, offset);
}
}

View file

@ -48,19 +48,20 @@ public class Sphere implements Shape {
}
@Override
public double getVolumeOfSpawnableSpace() {
public double getVolume() {
return volume;
}
private double computeSpawnableSpace() {
if (hollow) {
if (stretch.x == stretch.y && stretch.y == stretch.z) {
double radius = rad * stretch.x;
return 4 * Math.PI * radius * radius;
}
return computeEllipsoidArea(rad, stretch);
if (!hollow) {
return computeEllipsoidVolume(rad, stretch);
}
return computeEllipsoidVolume(rad, stretch);
if (stretch.x == stretch.y && stretch.y == stretch.z) {
double radius = rad * stretch.x;
return 4 * Math.PI * radius * radius;
}
return computeEllipsoidArea(rad, stretch);
}
@Override
@ -75,7 +76,11 @@ public class Sphere implements Shape {
double phi = MathHelper.nextDouble(rand, 0, Math.PI * 2);
double theta = Math.asin(z / rad);
return new Vec3d(rad * Math.cos(theta) * Math.cos(phi) * stretch.x, rad * Math.cos(theta) * Math.sin(phi) * stretch.y, z * stretch.z);
return new Vec3d(
rad * Math.cos(theta) * Math.cos(phi) * stretch.x,
rad * Math.cos(theta) * Math.sin(phi) * stretch.y,
z * stretch.z
);
}
@Override
@ -89,12 +94,12 @@ public class Sphere implements Shape {
@Override
public Vec3d getLowerBound() {
return new Vec3d(-rad * stretch.x, -rad * stretch.y, -rad * stretch.z);
return stretch.multiply(-rad);
}
@Override
public Vec3d getUpperBound() {
return new Vec3d(rad * stretch.x, rad * stretch.y, rad * stretch.z);
return stretch.multiply(rad);
}
public static double computeEllipsoidArea(double rad, Vec3d stretch) {

View file

@ -0,0 +1,43 @@
package com.minelittlepony.unicopia.util.shape;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.random.Random;
record TranslatedPointGenerator (
PointGenerator source,
Vec3d offset
) implements Shape {
@Override
public double getVolume() {
return ((Shape)source).getVolume();
}
@Override
public Vec3d computePoint(Random rand) {
return source.computePoint(rand).add(offset);
}
@Override
public Vec3d getLowerBound() {
return ((Shape)source).getLowerBound().add(offset);
}
@Override
public Vec3d getUpperBound() {
return ((Shape)source).getUpperBound().add(offset);
}
@Override
public boolean isPointInside(Vec3d point) {
return ((Shape)source).isPointInside(point.subtract(offset));
}
@Override
public Shape translate(Vec3d offset) {
if (offset.equals(Vec3d.ZERO)) {
return this;
}
return new TranslatedPointGenerator(source, this.offset.add(offset));
}
}