Start implementations

This commit is contained in:
Paul Reilly
2023-06-01 23:02:01 -05:00
parent def84bd747
commit a632eb778c
11 changed files with 535 additions and 8 deletions

View File

@ -0,0 +1,7 @@
package me.totalfreedom.api;
@FunctionalInterface
public interface Interpolator
{
double[] interpolate(final double from, final double to, final int max);
}

View File

@ -1,31 +1,127 @@
package me.totalfreedom.particle;
import me.totalfreedom.api.Interpolator;
import me.totalfreedom.utils.InterpolationUtils;
import org.bukkit.Color;
import org.bukkit.entity.Player;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer;
import org.bukkit.Particle;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
import java.util.UUID;
/**
* Represents a Trail instance for a specific player.
*/
public interface Trail
{
/**
* Returns the UUID of the player associated with the trail. This is for usage with our persistant storage
* container so that we can safely send and retrieve the trails without having to directly reference a player
* object.
* <br>
* TL;DR Memory optimization!
*
* @return The UUID of the player associated with this trail.
*/
@NotNull
UUID getAssocPlayerUUID();
UUID getAssociatedPlayerUUID();
// Nullable because the player may not be online and trail selections should be persistent whether they are
// active or not.
@Nullable
Player getAssocPlayer();
/**
* Returns the player associated with this trail. Trails are user specific, and should be persistent across all
* usages. This is also used when displaying the particles, as they will be relative to the player's back, which
* is an inverse offset of the player's eye location. We use OfflinePlayer as we can make a simple check and cast
* to determine if the player is online when spawning trails.
*
* @return The player associated with this Trail.
*/
@NotNull
OfflinePlayer getAssociatedPlayer();
/**
* Gets the Trail Type of this trail. This is used to determine what type of trail this is, and what
* {@link Particle} it should use.
*
* @return The Trail Type of this trail.
* @see TrailType
*/
@NotNull
TrailType getTrailType();
@NotNull
/**
* This method is nullable because if the value of {@link #isGradient()} is true, then
* {@link #getColors()} should be used instead, as that will contain the color data for our trail.
* <br>
* However, this method will also be null if the particle type is not colorable.
*
* @return The color of the trail, or null if the trail is a gradient or non-colorable.
* @see Particle
* @see #getColors();
*/
@Nullable
Color getColor();
/**
* Sets the static color of the trail. If you are trying to use a gradient, use {@link #setColors(Set)} instead.
* <br>
*
* @param color The color to set the trail to.
*/
void setColor(@NotNull Color color);
/**
* This method is nullable because if the value of {@link #isGradient()} is false, then
* {@link #getColor()} should be used instead, as our trail is a single static color.
* <br>
* However, this method will also be null if the particle type is not colorable.
*
* @return The colors of the trail, or null if the trail is not a gradient or non-colorable.
* @see #getColor()
* @see Particle
* @see InterpolationUtils
* @see Interpolator
*/
@Nullable
Set<Color> getColors();
/**
* Sets the colors of the trail. If you are trying to use a static color, use {@link #setColor(Color)} instead.
* <br>
* This should be used for trails that iterate over a set of colors, such as a rainbow trail.
*
* @param colors The colors to set the trail to. It is recommended to use {@link InterpolationUtils} to generate
* interpolated gradients for this.
*/
void setColors(@NotNull Set<Color> colors);
/**
* Validates whether this Trail is a gradient or a static trail.
* <br>
* This is entirely based on whether {@link #getColors()} returns null or not.
*
* @return True if {@link #getColors()} is not null, false otherwise.
*/
boolean isGradient();
/**
* Gets whether the trail is active.
*
* @return True if the trail is active, false if it is not.
*/
boolean isActive();
void setActive(boolean active);
/**
* Turn the trail on or off.
*
* @param active True if the trail should be active, false if it should not.
*/
void setActive(final boolean active);
/**
* Spawns a particle (if gradient, the next particle) on the supplied location object.
*/
void spawnParticle();
}

View File

@ -0,0 +1,120 @@
package me.totalfreedom.utils;
import me.totalfreedom.api.Interpolator;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import org.bukkit.Color;
import java.util.LinkedHashSet;
import java.util.Set;
public class InterpolationUtils
{
public static Set<Color> rainbow(final int length)
{
final LinkedHashSet<Color> base = new LinkedHashSet<>();
final Set<Color> redToOrange = hsvGradient(length, Color.RED, Color.ORANGE, InterpolationUtils::linear);
final Set<Color> orangeToYellow = hsvGradient(length, Color.ORANGE, Color.YELLOW, InterpolationUtils::linear);
final Set<Color> yellowToGreen = hsvGradient(length, Color.YELLOW, Color.GREEN, InterpolationUtils::linear);
final Set<Color> greenToBlue = hsvGradient(length, Color.GREEN, Color.BLUE, InterpolationUtils::linear);
final Set<Color> blueToPurple = hsvGradient(length, Color.BLUE, Color.PURPLE, InterpolationUtils::linear);
final Set<Color> purpleToRed = hsvGradient(length, Color.PURPLE, Color.RED, InterpolationUtils::linear);
base.addAll(redToOrange);
base.addAll(orangeToYellow);
base.addAll(yellowToGreen);
base.addAll(greenToBlue);
base.addAll(blueToPurple);
base.addAll(purpleToRed);
return base;
}
private static Set<Color> hsvGradient(final int length, final Color from, final Color to,
final Interpolator interpolator)
{
// returns a float-array where hsv[0] = hue, hsv[1] = saturation, hsv[2] = value/brightness
final float[] hsvFrom = java.awt.Color.RGBtoHSB(from.getRed(), from.getGreen(), from.getBlue(), null);
final float[] hsvTo = java.awt.Color.RGBtoHSB(to.getRed(), to.getGreen(), to.getBlue(), null);
final double[] h = interpolator.interpolate(hsvFrom[0], hsvTo[0], length);
final double[] s = interpolator.interpolate(hsvFrom[1], hsvTo[1], length);
final double[] v = interpolator.interpolate(hsvFrom[2], hsvTo[2], length);
final LinkedHashSet<Color> gradient = new LinkedHashSet<>();
for (int i = 0; i < length; i++)
{
final int rgb = java.awt.Color.HSBtoRGB((float) h[i], (float) s[i], (float) v[i]);
final Color color = Color.fromRGB(rgb);
gradient.add(color);
}
return gradient;
}
private static double[] linear(final double from, final double to, final int max)
{
final double[] res = new double[max];
for (int i = 0; i < max; i++)
{
res[i] = from + i * ((to - from) / (max - 1));
}
return res;
}
public static Set<Color> rgbGradient(final int length, final Color from, final Color to,
final Interpolator interpolator)
{
final double[] r = interpolator.interpolate(from.getRed(), to.getRed(), length);
final double[] g = interpolator.interpolate(from.getGreen(), to.getGreen(), length);
final double[] b = interpolator.interpolate(from.getBlue(), to.getBlue(), length);
final LinkedHashSet<Color> gradient = new LinkedHashSet<>();
for (int i = 0; i < length; i++)
{
final Color color = Color.fromRGB((int) r[i], (int) g[i], (int) b[i]);
gradient.add(color);
}
return gradient;
}
public static Set<TextColor> rainbowComponent(final int length)
{
final LinkedHashSet<TextColor> base = new LinkedHashSet<>();
final Set<TextColor> redToOrange = componentRGBGradient(length, NamedTextColor.RED,
NamedTextColor.GOLD, InterpolationUtils::linear);
final Set<TextColor> orangeToYellow = componentRGBGradient(length, NamedTextColor.GOLD,
NamedTextColor.YELLOW, InterpolationUtils::linear);
final Set<TextColor> yellowToGreen = componentRGBGradient(length, NamedTextColor.YELLOW,
NamedTextColor.GREEN, InterpolationUtils::linear);
final Set<TextColor> greenToBlue = componentRGBGradient(length, NamedTextColor.GREEN,
NamedTextColor.BLUE, InterpolationUtils::linear);
final Set<TextColor> blueToPurple = componentRGBGradient(length, NamedTextColor.BLUE,
NamedTextColor.LIGHT_PURPLE, InterpolationUtils::linear);
final Set<TextColor> purpleToRed = componentRGBGradient(length, TextColor.color(75, 0, 130),
TextColor.color(255, 0, 0), InterpolationUtils::linear);
base.addAll(redToOrange);
base.addAll(orangeToYellow);
base.addAll(yellowToGreen);
base.addAll(greenToBlue);
base.addAll(blueToPurple);
base.addAll(purpleToRed);
return base;
}
public static Set<TextColor> componentRGBGradient(final int length, final TextColor from, final TextColor to,
final Interpolator interpolator)
{
final double[] r = interpolator.interpolate(from.red(), to.red(), length);
final double[] g = interpolator.interpolate(from.green(), to.green(), length);
final double[] b = interpolator.interpolate(from.blue(), to.blue(), length);
final LinkedHashSet<TextColor> gradient = new LinkedHashSet<>();
for (int i = 0; i < length; i++)
{
final TextColor color = TextColor.color((int) r[i], (int) g[i], (int) b[i]);
gradient.add(color);
}
return gradient;
}
}