From c3c8caac236be72a9aa8c181c7b48bc9a66265fd Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Sat, 7 Feb 2026 13:04:59 -0800 Subject: [PATCH 1/6] initial separation --- .gitignore | 4 + gradle.properties | 1 + gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle | 3 +- shapes-lib/build.gradle | 17 + .../java/com/sovdee/shapes/AbstractShape.java | 222 +++++++++ .../src/main/java/com/sovdee/shapes/Arc.java | 45 ++ .../java/com/sovdee/shapes/BezierCurve.java | 88 ++++ .../main/java/com/sovdee}/shapes/Circle.java | 72 +-- .../main/java/com/sovdee/shapes/Cuboid.java | 151 +++++++ .../java/com/sovdee/shapes/CutoffShape.java | 9 + .../main/java/com/sovdee}/shapes/Ellipse.java | 55 +-- .../java/com/sovdee}/shapes/Ellipsoid.java | 88 ++-- .../java/com/sovdee/shapes/EllipticalArc.java | 39 ++ .../main/java/com/sovdee/shapes/Heart.java | 72 +++ .../main/java/com/sovdee/shapes/Helix.java | 107 +++++ .../com/sovdee/shapes/IrregularPolygon.java | 104 +++++ .../main/java/com/sovdee/shapes/LWHShape.java | 13 + .../src/main/java/com/sovdee/shapes/Line.java | 96 ++++ .../java/com/sovdee/shapes/PolyShape.java | 11 + .../java/com/sovdee/shapes/RadialShape.java | 10 + .../java/com/sovdee/shapes/Rectangle.java | 156 +++++++ .../com/sovdee}/shapes/RegularPolygon.java | 80 +--- .../com/sovdee/shapes/RegularPolyhedron.java | 208 +++++++++ .../main/java/com/sovdee/shapes/Shape.java | 259 +++++++++++ .../main/java/com/sovdee}/shapes/Sphere.java | 33 +- .../java/com/sovdee}/shapes/SphericalCap.java | 15 +- .../main/java/com/sovdee}/shapes/Star.java | 61 +-- .../java/com/sovdee/shapes/util/MathUtil.java | 248 ++++++++++ .../com/sovdee/shapes/util/VectorUtil.java | 51 +++ build.gradle => skript-particle/build.gradle | 28 +- .../com/sovdee/skriptparticles/Metrics.java | 0 .../skriptparticles/SkriptParticle.java | 0 .../elements/effects/EffRotateShape.java | 0 .../elements/effects/EffSetOrdering.java | 0 .../elements/effects/EffToggleAxes.java | 0 .../elements/expressions/ExprDrawnShapes.java | 0 .../expressions/ExprLastCreatedParticle.java | 0 .../elements/expressions/ExprRotation.java | 0 .../elements/expressions/ExprShapeCopy.java | 0 .../expressions/constructors/ExprArc.java | 18 +- .../constructors/ExprBezierCurve.java | 16 +- .../expressions/constructors/ExprCircle.java | 18 +- .../expressions/constructors/ExprCuboid.java | 30 +- .../expressions/constructors/ExprEllipse.java | 18 +- .../constructors/ExprEllipsoid.java | 19 +- .../constructors/ExprEllipticalArc.java | 19 +- .../expressions/constructors/ExprHeart.java | 18 +- .../expressions/constructors/ExprHelix.java | 21 +- .../constructors/ExprIrregularPolygon.java | 33 +- .../expressions/constructors/ExprLine.java | 31 +- .../constructors/ExprRectangle.java | 39 +- .../constructors/ExprRegularPolygon.java | 19 +- .../constructors/ExprRegularPolyhedron.java | 20 +- .../expressions/constructors/ExprSphere.java | 18 +- .../constructors/ExprSphericalCap.java | 18 +- .../expressions/constructors/ExprStar.java | 18 +- .../properties/ExprHelixWindingRate.java | 9 +- .../properties/ExprShapeCutoffAngle.java | 24 +- .../expressions/properties/ExprShapeLWH.java | 36 +- .../properties/ExprShapeLocations.java | 0 .../properties/ExprShapeNormal.java | 0 .../properties/ExprShapeOffset.java | 0 .../properties/ExprShapeOrientation.java | 0 .../properties/ExprShapeParticle.java | 7 +- .../properties/ExprShapeParticleDensity.java | 0 .../properties/ExprShapePoints.java | 0 .../properties/ExprShapeRadius.java | 28 +- .../properties/ExprShapeRelativeAxis.java | 0 .../properties/ExprShapeScale.java | 0 .../properties/ExprShapeSideLength.java | 24 +- .../properties/ExprShapeSides.java | 24 +- .../properties/ExprShapeStyle.java | 0 .../properties/ExprStarPoints.java | 11 +- .../expressions/properties/ExprStarRadii.java | 11 +- .../elements/package-info.java | 0 .../sections/DrawShapeEffectSection.java | 0 .../elements/sections/EffSecDrawShape.java | 0 .../sections/EffSecDrawShapeAnimation.java | 0 .../elements/sections/SecParticle.java | 0 .../elements/types/ParticleTypes.java | 0 .../elements/types/RotationTypes.java | 0 .../elements/types/ShapeTypes.java | 77 ++++ .../skriptparticles/particles/Particle.java | 0 .../particles/ParticleGradient.java | 0 .../particles/ParticleMotion.java | 0 .../particles/package-info.java | 0 .../skriptparticles/shapes/DrawableShape.java | 422 ++++++++++++++++++ .../shapes/DynamicBezierCurve.java | 67 +++ .../skriptparticles/shapes/DynamicCuboid.java | 63 +++ .../skriptparticles/shapes/DynamicLine.java | 56 +++ .../shapes/DynamicRectangle.java | 77 ++++ .../sovdee/skriptparticles/shapes/Shape.java | 0 .../skriptparticles/shapes/package-info.java | 0 .../skriptparticles/util/DynamicLocation.java | 0 .../sovdee/skriptparticles/util/MathUtil.java | 0 .../skriptparticles/util/ParticleUtil.java | 0 .../sovdee/skriptparticles/util/Point.java | 0 .../skriptparticles/util/Quaternion.java | 0 .../skriptparticles/util/ReflectionUtils.java | 0 .../util/VectorConversion.java | 37 ++ .../skriptparticles/util/package-info.java | 0 .../src}/main/resources/lang/english.lang | 0 .../src}/main/resources/plugin.yml | 0 .../elements/types/ShapeTypes.java | 201 --------- .../skriptparticles/shapes/AbstractShape.java | 395 ---------------- .../sovdee/skriptparticles/shapes/Arc.java | 66 --- .../skriptparticles/shapes/BezierCurve.java | 121 ----- .../sovdee/skriptparticles/shapes/Cuboid.java | 254 ----------- .../skriptparticles/shapes/CutoffShape.java | 22 - .../skriptparticles/shapes/EllipticalArc.java | 63 --- .../sovdee/skriptparticles/shapes/Heart.java | 115 ----- .../sovdee/skriptparticles/shapes/Helix.java | 166 ------- .../shapes/IrregularPolygon.java | 144 ------ .../skriptparticles/shapes/LWHShape.java | 41 -- .../sovdee/skriptparticles/shapes/Line.java | 206 --------- .../skriptparticles/shapes/PolyShape.java | 31 -- .../skriptparticles/shapes/RadialShape.java | 21 - .../skriptparticles/shapes/Rectangle.java | 302 ------------- .../shapes/RegularPolyhedron.java | 261 ----------- 120 files changed, 3113 insertions(+), 2990 deletions(-) create mode 100644 shapes-lib/build.gradle create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/AbstractShape.java create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/Arc.java create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/BezierCurve.java rename {src/main/java/com/sovdee/skriptparticles => shapes-lib/src/main/java/com/sovdee}/shapes/Circle.java (55%) create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/Cuboid.java create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/CutoffShape.java rename {src/main/java/com/sovdee/skriptparticles => shapes-lib/src/main/java/com/sovdee}/shapes/Ellipse.java (59%) rename {src/main/java/com/sovdee/skriptparticles => shapes-lib/src/main/java/com/sovdee}/shapes/Ellipsoid.java (55%) create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/EllipticalArc.java create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/Heart.java create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/Helix.java create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/IrregularPolygon.java create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/LWHShape.java create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/Line.java create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/PolyShape.java create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/RadialShape.java create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/Rectangle.java rename {src/main/java/com/sovdee/skriptparticles => shapes-lib/src/main/java/com/sovdee}/shapes/RegularPolygon.java (58%) create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/RegularPolyhedron.java create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/Shape.java rename {src/main/java/com/sovdee/skriptparticles => shapes-lib/src/main/java/com/sovdee}/shapes/Sphere.java (69%) rename {src/main/java/com/sovdee/skriptparticles => shapes-lib/src/main/java/com/sovdee}/shapes/SphericalCap.java (54%) rename {src/main/java/com/sovdee/skriptparticles => shapes-lib/src/main/java/com/sovdee}/shapes/Star.java (50%) create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/util/MathUtil.java create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/util/VectorUtil.java rename build.gradle => skript-particle/build.gradle (66%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/Metrics.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/SkriptParticle.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/effects/EffRotateShape.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/effects/EffSetOrdering.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/effects/EffToggleAxes.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/ExprDrawnShapes.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/ExprLastCreatedParticle.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/ExprRotation.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/ExprShapeCopy.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprArc.java (88%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprBezierCurve.java (81%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCircle.java (87%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCuboid.java (83%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipse.java (89%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipsoid.java (87%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipticalArc.java (89%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHeart.java (88%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHelix.java (87%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprIrregularPolygon.java (73%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprLine.java (84%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRectangle.java (81%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolygon.java (89%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolyhedron.java (75%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphere.java (79%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphericalCap.java (85%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprStar.java (84%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprHelixWindingRate.java (87%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeCutoffAngle.java (72%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLWH.java (68%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLocations.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeNormal.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOffset.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOrientation.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticle.java (93%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticleDensity.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapePoints.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRadius.java (69%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRelativeAxis.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeScale.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSideLength.java (73%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSides.java (71%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeStyle.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarPoints.java (85%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarRadii.java (89%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/package-info.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/sections/DrawShapeEffectSection.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/sections/EffSecDrawShape.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/sections/EffSecDrawShapeAnimation.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/sections/SecParticle.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/types/ParticleTypes.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/elements/types/RotationTypes.java (100%) create mode 100644 skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/particles/Particle.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/particles/ParticleGradient.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/particles/ParticleMotion.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/particles/package-info.java (100%) create mode 100644 skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawableShape.java create mode 100644 skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicBezierCurve.java create mode 100644 skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicCuboid.java create mode 100644 skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicLine.java create mode 100644 skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicRectangle.java rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/shapes/Shape.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/shapes/package-info.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/util/DynamicLocation.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/util/MathUtil.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/util/ParticleUtil.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/util/Point.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/util/Quaternion.java (100%) rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/util/ReflectionUtils.java (100%) create mode 100644 skript-particle/src/main/java/com/sovdee/skriptparticles/util/VectorConversion.java rename {src => skript-particle/src}/main/java/com/sovdee/skriptparticles/util/package-info.java (100%) rename {src => skript-particle/src}/main/resources/lang/english.lang (100%) rename {src => skript-particle/src}/main/resources/plugin.yml (100%) delete mode 100644 src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java delete mode 100644 src/main/java/com/sovdee/skriptparticles/shapes/AbstractShape.java delete mode 100644 src/main/java/com/sovdee/skriptparticles/shapes/Arc.java delete mode 100644 src/main/java/com/sovdee/skriptparticles/shapes/BezierCurve.java delete mode 100644 src/main/java/com/sovdee/skriptparticles/shapes/Cuboid.java delete mode 100644 src/main/java/com/sovdee/skriptparticles/shapes/CutoffShape.java delete mode 100644 src/main/java/com/sovdee/skriptparticles/shapes/EllipticalArc.java delete mode 100644 src/main/java/com/sovdee/skriptparticles/shapes/Heart.java delete mode 100644 src/main/java/com/sovdee/skriptparticles/shapes/Helix.java delete mode 100644 src/main/java/com/sovdee/skriptparticles/shapes/IrregularPolygon.java delete mode 100644 src/main/java/com/sovdee/skriptparticles/shapes/LWHShape.java delete mode 100644 src/main/java/com/sovdee/skriptparticles/shapes/Line.java delete mode 100644 src/main/java/com/sovdee/skriptparticles/shapes/PolyShape.java delete mode 100644 src/main/java/com/sovdee/skriptparticles/shapes/RadialShape.java delete mode 100644 src/main/java/com/sovdee/skriptparticles/shapes/Rectangle.java delete mode 100644 src/main/java/com/sovdee/skriptparticles/shapes/RegularPolyhedron.java diff --git a/.gitignore b/.gitignore index 46dd638..965fe30 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,7 @@ /build/ /.git/ /src/test/skriptparticle/ +CLAUDE.md +/.claude +/shapes-lib/build +/skript-particle/build diff --git a/gradle.properties b/gradle.properties index 749ca75..d59f37d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1,2 @@ version = 1.4.0 +jomlVersion = 1.10.5 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 59bc51a..a595206 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle b/settings.gradle index 9f325b4..bd0f6f2 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,2 @@ -rootProject.name = 'skript-particle' +rootProject.name = 'skript-particle-root' +include 'shapes-lib', 'skript-particle' diff --git a/shapes-lib/build.gradle b/shapes-lib/build.gradle new file mode 100644 index 0000000..b5aef52 --- /dev/null +++ b/shapes-lib/build.gradle @@ -0,0 +1,17 @@ +plugins { + id 'java-library' +} + +group = 'com.sovdee' + +repositories { + mavenCentral() +} + +dependencies { + api "org.joml:joml:${jomlVersion}" +} + +java { + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) +} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/AbstractShape.java b/shapes-lib/src/main/java/com/sovdee/shapes/AbstractShape.java new file mode 100644 index 0000000..c648e05 --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/AbstractShape.java @@ -0,0 +1,222 @@ +package com.sovdee.shapes; + +import com.sovdee.shapes.util.MathUtil; +import org.joml.Quaterniond; +import org.joml.Vector3d; + +import java.util.Comparator; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.TreeSet; +import java.util.UUID; + +public abstract class AbstractShape implements Shape { + + private final UUID uuid; + private Set points; + + private Style style; + private final Quaterniond orientation; + private final Quaterniond lastOrientation; + private double scale; + private Vector3d offset; + private Comparator ordering; + private double particleDensity = 0.25; + + private State lastState; + private boolean needsUpdate = false; + + public AbstractShape() { + this.style = Style.OUTLINE; + this.points = new LinkedHashSet<>(); + + this.orientation = new Quaterniond(); + this.lastOrientation = new Quaterniond(); + this.scale = 1; + this.offset = new Vector3d(0, 0, 0); + + this.uuid = UUID.randomUUID(); + + this.lastState = getState(); + } + + @Override + public Set getPoints() { + return getPoints(this.orientation); + } + + @Override + public Set getPoints(Quaterniond orientation) { + State state = getState(orientation); + if (needsUpdate || !lastState.equals(state) || points.isEmpty()) { + if (ordering != null) + points = new TreeSet<>(ordering); + else + points = new LinkedHashSet<>(); + generatePoints(points); + for (Vector3d point : points) { + orientation.transform(point); + point.mul(scale); + point.add(offset); + } + lastState = state; + needsUpdate = false; + } + return points; + } + + @Override + public void setPoints(Set points) { + this.points = points; + } + + @Override + public void generateSurface(Set points) { + generateOutline(points); + } + + @Override + public void generateFilled(Set points) { + generateSurface(points); + } + + @Override + public Vector3d getRelativeXAxis(boolean useLastOrientation) { + return (useLastOrientation ? lastOrientation : orientation).transform(new Vector3d(1, 0, 0)); + } + + @Override + public Vector3d getRelativeYAxis(boolean useLastOrientation) { + return (useLastOrientation ? lastOrientation : orientation).transform(new Vector3d(0, 1, 0)); + } + + @Override + public Vector3d getRelativeZAxis(boolean useLastOrientation) { + return (useLastOrientation ? lastOrientation : orientation).transform(new Vector3d(0, 0, 1)); + } + + @Override + public Style getStyle() { + return style; + } + + @Override + public void setStyle(Style style) { + this.style = style; + this.setNeedsUpdate(true); + } + + @Override + public Quaterniond getOrientation() { + return new Quaterniond(orientation); + } + + @Override + public void setOrientation(Quaterniond orientation) { + this.orientation.set(orientation); + this.setNeedsUpdate(true); + } + + public Quaterniond getLastOrientation() { + return lastOrientation; + } + + public void setLastOrientation(Quaterniond orientation) { + this.lastOrientation.set(orientation); + } + + @Override + public double getScale() { + return scale; + } + + @Override + public void setScale(double scale) { + this.scale = scale; + this.setNeedsUpdate(true); + } + + @Override + public Vector3d getOffset() { + return new Vector3d(offset); + } + + @Override + public void setOffset(Vector3d offset) { + this.offset = offset; + this.setNeedsUpdate(true); + } + + @Override + public UUID getUUID() { + return uuid; + } + + @Override + public Comparator getOrdering() { + return ordering; + } + + @Override + public void setOrdering(Comparator comparator) { + ordering = comparator; + this.setNeedsUpdate(true); + } + + @Override + public double getParticleDensity() { + return particleDensity; + } + + @Override + public void setParticleDensity(double particleDensity) { + this.particleDensity = Math.max(particleDensity, MathUtil.EPSILON); + this.setNeedsUpdate(true); + } + + @Override + public int getParticleCount() { + return getPoints().size(); + } + + @Override + public boolean needsUpdate() { + return needsUpdate; + } + + @Override + public void setNeedsUpdate(boolean needsUpdate) { + this.needsUpdate = needsUpdate; + } + + public abstract Shape clone(); + + @Override + public Shape copyTo(Shape shape) { + shape.setOrientation(new Quaterniond(this.orientation)); + shape.setScale(this.scale); + shape.setOffset(new Vector3d(this.offset)); + shape.setParticleDensity(this.particleDensity); + shape.setStyle(this.style); + shape.setOrdering(this.ordering); + shape.setPoints(this.getPoints()); + shape.setNeedsUpdate(this.needsUpdate); + shape.setLastState(this.lastState); + return shape; + } + + @Override + public State getState() { + return new State(style, orientation.hashCode(), scale, offset.hashCode(), particleDensity); + } + + @Override + public State getState(Quaterniond orientation) { + return new State(style, orientation.hashCode(), scale, offset.hashCode(), particleDensity); + } + + @Override + public void setLastState(State state) { + this.lastState = state; + } +} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Arc.java b/shapes-lib/src/main/java/com/sovdee/shapes/Arc.java new file mode 100644 index 0000000..f971ba6 --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/Arc.java @@ -0,0 +1,45 @@ +package com.sovdee.shapes; + +import com.sovdee.shapes.util.MathUtil; +import org.joml.Vector3d; + +import java.util.Set; + +public class Arc extends Circle implements CutoffShape { + + public Arc(double radius, double cutoffAngle) { + super(radius); + this.cutoffAngle = MathUtil.clamp(cutoffAngle, 0, Math.PI * 2); + } + + public Arc(double radius, double height, double cutoffAngle) { + super(radius, height); + this.cutoffAngle = MathUtil.clamp(cutoffAngle, 0, Math.PI * 2); + } + + @Override + public void generateSurface(Set points) { + generateFilled(points); + } + + @Override + public double getCutoffAngle() { + return this.cutoffAngle; + } + + @Override + public void setCutoffAngle(double cutoffAngle) { + this.cutoffAngle = MathUtil.clamp(cutoffAngle, 0, Math.PI * 2); + this.setNeedsUpdate(true); + } + + @Override + public Shape clone() { + return this.copyTo(new Arc(this.getRadius(), this.getHeight(), cutoffAngle)); + } + + @Override + public String toString() { + return "Arc{radius=" + this.getRadius() + ", cutoffAngle=" + cutoffAngle + ", height=" + this.getHeight() + '}'; + } +} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/BezierCurve.java b/shapes-lib/src/main/java/com/sovdee/shapes/BezierCurve.java new file mode 100644 index 0000000..7319f62 --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/BezierCurve.java @@ -0,0 +1,88 @@ +package com.sovdee.shapes; + +import org.joml.Vector3d; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * A bezier curve defined by control points as Vector3d. + * For dynamic (entity-following) bezier curves, use the plugin-side wrapper. + */ +public class BezierCurve extends AbstractShape { + + private List controlPoints; + + /** + * Creates a bezier curve from the given control points. + * Must have at least 2 control points (start and end). + * + * @param controlPoints the control points, including start and end + */ + public BezierCurve(List controlPoints) { + super(); + if (controlPoints.size() < 2) + throw new IllegalArgumentException("A bezier curve must have at least 2 control points."); + this.controlPoints = new ArrayList<>(); + for (Vector3d cp : controlPoints) + this.controlPoints.add(new Vector3d(cp)); + } + + public BezierCurve(BezierCurve curve) { + super(); + this.controlPoints = new ArrayList<>(); + for (Vector3d cp : curve.controlPoints) + this.controlPoints.add(new Vector3d(cp)); + } + + @Override + public void generateOutline(Set points) { + int steps = (int) (estimateLength() / getParticleDensity()); + + for (double step = 0; step < steps; step++) { + double t = step / steps; + double nt = 1 - t; + List tempCP = new ArrayList<>(); + for (Vector3d cp : controlPoints) + tempCP.add(new Vector3d(cp)); + while (tempCP.size() > 1) { + for (int i = 0; i < tempCP.size() - 1; i++) + tempCP.set(i, new Vector3d(tempCP.get(i)).mul(nt).add(new Vector3d(tempCP.get(i + 1)).mul(t))); + tempCP.remove(tempCP.size() - 1); + } + points.add(tempCP.get(0)); + } + } + + private double estimateLength() { + double dist = 0; + for (int i = 0; i < controlPoints.size() - 1; i++) { + dist += controlPoints.get(i).distance(controlPoints.get(i + 1)); + } + return dist; + } + + @Override + public void setParticleCount(int particleCount) { + particleCount = Math.max(particleCount, 1); + this.setParticleDensity(estimateLength() / particleCount); + this.setNeedsUpdate(true); + } + + public List getControlPoints() { + return controlPoints; + } + + public void setControlPoints(List controlPoints) { + this.controlPoints = new ArrayList<>(); + for (Vector3d cp : controlPoints) + this.controlPoints.add(new Vector3d(cp)); + this.setNeedsUpdate(true); + } + + @Override + public Shape clone() { + return this.copyTo(new BezierCurve(this)); + } +} diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/Circle.java b/shapes-lib/src/main/java/com/sovdee/shapes/Circle.java similarity index 55% rename from src/main/java/com/sovdee/skriptparticles/shapes/Circle.java rename to shapes-lib/src/main/java/com/sovdee/shapes/Circle.java index 8de8df8..4e8d2bc 100644 --- a/src/main/java/com/sovdee/skriptparticles/shapes/Circle.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/Circle.java @@ -1,60 +1,38 @@ -package com.sovdee.skriptparticles.shapes; +package com.sovdee.shapes; -import com.sovdee.skriptparticles.util.MathUtil; -import org.bukkit.util.Vector; -import org.jetbrains.annotations.Contract; +import com.sovdee.shapes.util.MathUtil; +import org.joml.Vector3d; import java.util.Set; -/** - * A circle with a radius and optionally a height. - * Circle does not implement {@link LWHShape#setWidth(double)} or {@link LWHShape#setLength(double)}. - */ public class Circle extends AbstractShape implements RadialShape, LWHShape { private double radius; protected double cutoffAngle; private double height; - /** - * Creates a circle with the given radius and a height of 0. - * - * @param radius the radius of the circle. Must be greater than 0. - */ public Circle(double radius) { this(radius, 0); } - /** - * Creates a circle with the given radius and height. - * - * @param radius the radius of the circle. Must be greater than 0. - * @param height the height of the circle. Must be non-negative. - */ public Circle(double radius, double height) { super(); this.radius = Math.max(radius, MathUtil.EPSILON); this.height = Math.max(height, 0); - this.cutoffAngle = 2 * Math.PI; } @Override - @Contract(pure = true) - @SuppressWarnings("ConstantConditions") - public void generateOutline(Set points) { - Set circle = MathUtil.calculateCircle(radius, this.getParticleDensity(), cutoffAngle); + public void generateOutline(Set points) { + Set circle = MathUtil.calculateCircle(radius, this.getParticleDensity(), cutoffAngle); if (height != 0) points.addAll(MathUtil.fillVertically(circle, height, this.getParticleDensity())); else points.addAll(circle); } - @Override - @Contract(pure = true) - @SuppressWarnings("ConstantConditions") - public void generateSurface(Set points) { + public void generateSurface(Set points) { if (height != 0) points.addAll(MathUtil.calculateCylinder(radius, height, this.getParticleDensity(), cutoffAngle)); else @@ -62,10 +40,8 @@ public void generateSurface(Set points) { } @Override - @Contract(pure = true) - @SuppressWarnings("ConstantConditions") - public void generateFilled(Set points) { - Set disc = MathUtil.calculateDisc(radius, this.getParticleDensity(), cutoffAngle); + public void generateFilled(Set points) { + Set disc = MathUtil.calculateDisc(radius, this.getParticleDensity(), cutoffAngle); if (height != 0) points.addAll(MathUtil.fillVertically(disc, height, this.getParticleDensity())); else @@ -75,7 +51,6 @@ public void generateFilled(Set points) { @Override public void setParticleCount(int particleCount) { particleCount = Math.max(particleCount, 1); - if (this.getStyle() == Style.OUTLINE && height == 0) { this.setParticleDensity(cutoffAngle * radius / particleCount); } else if (this.getStyle() == Style.SURFACE || height == 0) { @@ -89,9 +64,7 @@ public void setParticleCount(int particleCount) { } @Override - public double getRadius() { - return radius; - } + public double getRadius() { return radius; } @Override public void setRadius(double radius) { @@ -100,29 +73,19 @@ public void setRadius(double radius) { } @Override - public double getLength() { - return 0; - } + public double getLength() { return 0; } @Override - public void setLength(double length) { - // intentionally left blank - } + public void setLength(double length) { } @Override - public double getWidth() { - return 0; - } + public double getWidth() { return 0; } @Override - public void setWidth(double width) { - // intentionally left blank - } + public void setWidth(double width) { } @Override - public double getHeight() { - return height; - } + public double getHeight() { return height; } @Override public void setHeight(double height) { @@ -131,17 +94,12 @@ public void setHeight(double height) { } @Override - @Contract("-> new") public Shape clone() { return this.copyTo(new Circle(radius, height)); } @Override public String toString() { - return "Circle{" + - "radius=" + radius + - ", cutoffAngle=" + cutoffAngle + - ", height=" + height + - '}'; + return "Circle{radius=" + radius + ", cutoffAngle=" + cutoffAngle + ", height=" + height + '}'; } } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Cuboid.java b/shapes-lib/src/main/java/com/sovdee/shapes/Cuboid.java new file mode 100644 index 0000000..9f89855 --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/Cuboid.java @@ -0,0 +1,151 @@ +package com.sovdee.shapes; + +import com.sovdee.shapes.util.MathUtil; +import org.joml.Vector3d; + +import java.util.Set; + +/** + * A cuboid shape, defined either by dimensions or by two corner vectors. + * For dynamic (entity-following) cuboids, use the plugin-side DynamicCuboid wrapper. + */ +public class Cuboid extends AbstractShape implements LWHShape { + + private double halfLength, halfWidth, halfHeight; + private double lengthStep, widthStep, heightStep; + private Vector3d centerOffset = new Vector3d(0, 0, 0); + + public Cuboid(double length, double width, double height) { + super(); + this.halfWidth = Math.max(width / 2, MathUtil.EPSILON); + this.halfLength = Math.max(length / 2, MathUtil.EPSILON); + this.halfHeight = Math.max(height / 2, MathUtil.EPSILON); + calculateSteps(); + } + + public Cuboid(Vector3d cornerA, Vector3d cornerB) { + super(); + if (cornerA.equals(cornerB)) + throw new IllegalArgumentException("Cuboid corners cannot be equal."); + this.halfLength = Math.abs(cornerB.x - cornerA.x) / 2; + this.halfWidth = Math.abs(cornerB.z - cornerA.z) / 2; + this.halfHeight = Math.abs(cornerB.y - cornerA.y) / 2; + centerOffset = new Vector3d(cornerB).add(cornerA).mul(0.5); + calculateSteps(); + } + + private void calculateSteps() { + widthStep = 2 * halfWidth / Math.round(2 * halfWidth / this.getParticleDensity()); + lengthStep = 2 * halfLength / Math.round(2 * halfLength / this.getParticleDensity()); + heightStep = 2 * halfHeight / Math.round(2 * halfHeight / this.getParticleDensity()); + } + + @Override + public void generateOutline(Set points) { + for (double x = -halfLength; x <= halfLength; x += lengthStep) { + points.add(new Vector3d(x, -halfHeight, -halfWidth)); + points.add(new Vector3d(x, -halfHeight, halfWidth)); + points.add(new Vector3d(x, halfHeight, -halfWidth)); + points.add(new Vector3d(x, halfHeight, halfWidth)); + } + for (double y = -halfHeight + heightStep; y < halfHeight; y += heightStep) { + points.add(new Vector3d(-halfLength, y, -halfWidth)); + points.add(new Vector3d(-halfLength, y, halfWidth)); + points.add(new Vector3d(halfLength, y, -halfWidth)); + points.add(new Vector3d(halfLength, y, halfWidth)); + } + for (double z = -halfWidth + widthStep; z < halfWidth; z += widthStep) { + points.add(new Vector3d(-halfLength, -halfHeight, z)); + points.add(new Vector3d(-halfLength, halfHeight, z)); + points.add(new Vector3d(halfLength, -halfHeight, z)); + points.add(new Vector3d(halfLength, halfHeight, z)); + } + } + + @Override + public void generateSurface(Set points) { + for (double x = -halfLength; x <= halfLength; x += lengthStep) { + for (double z = -halfWidth; z <= halfWidth; z += widthStep) { + points.add(new Vector3d(x, -halfHeight, z)); + points.add(new Vector3d(x, halfHeight, z)); + } + } + for (double y = -halfHeight + heightStep; y < halfHeight; y += heightStep) { + for (double z = -halfWidth; z <= halfWidth; z += widthStep) { + points.add(new Vector3d(-halfLength, y, z)); + points.add(new Vector3d(halfLength, y, z)); + } + } + for (double x = -halfLength + lengthStep; x < halfLength; x += lengthStep) { + for (double y = -halfHeight + heightStep; y < halfHeight; y += heightStep) { + points.add(new Vector3d(x, y, -halfWidth)); + points.add(new Vector3d(x, y, halfWidth)); + } + } + } + + @Override + public void generateFilled(Set points) { + for (double x = -halfLength; x <= halfLength; x += lengthStep) { + for (double y = -halfHeight; y <= halfHeight; y += heightStep) { + for (double z = -halfWidth; z <= halfWidth; z += widthStep) { + points.add(new Vector3d(x, y, z)); + } + } + } + } + + @Override + public void generatePoints(Set points) { + calculateSteps(); + super.generatePoints(points); + points.forEach(vector -> vector.add(centerOffset)); + } + + @Override + public void setParticleCount(int particleCount) { + particleCount = Math.max(1, particleCount); + this.setParticleDensity(switch (this.getStyle()) { + case OUTLINE -> 8 * (halfLength + halfHeight + halfWidth) / particleCount; + case SURFACE -> + Math.sqrt(8 * (halfLength * halfHeight + halfLength * halfWidth + halfHeight * halfWidth) / particleCount); + case FILL -> Math.cbrt(8 * halfLength * halfHeight * halfWidth / particleCount); + }); + calculateSteps(); + this.setNeedsUpdate(true); + } + + @Override + public double getLength() { return halfLength * 2; } + + @Override + public void setLength(double length) { + this.halfLength = Math.max(length / 2, MathUtil.EPSILON); + this.setNeedsUpdate(true); + } + + @Override + public double getWidth() { return halfWidth * 2; } + + @Override + public void setWidth(double width) { + this.halfWidth = Math.max(width / 2, MathUtil.EPSILON); + this.setNeedsUpdate(true); + } + + @Override + public double getHeight() { return halfHeight * 2; } + + @Override + public void setHeight(double height) { + this.halfHeight = Math.max(height / 2, MathUtil.EPSILON); + this.setNeedsUpdate(true); + } + + @Override + public Shape clone() { + Cuboid cuboid = new Cuboid(getLength(), getWidth(), getHeight()); + cuboid.centerOffset = new Vector3d(this.centerOffset); + return this.copyTo(cuboid); + } +} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/CutoffShape.java b/shapes-lib/src/main/java/com/sovdee/shapes/CutoffShape.java new file mode 100644 index 0000000..1da69c2 --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/CutoffShape.java @@ -0,0 +1,9 @@ +package com.sovdee.shapes; + +/** + * Represents a shape that has a cutoff angle, like an arc. + */ +public interface CutoffShape extends Shape { + double getCutoffAngle(); + void setCutoffAngle(double cutoffAngle); +} diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/Ellipse.java b/shapes-lib/src/main/java/com/sovdee/shapes/Ellipse.java similarity index 59% rename from src/main/java/com/sovdee/skriptparticles/shapes/Ellipse.java rename to shapes-lib/src/main/java/com/sovdee/shapes/Ellipse.java index 69f50d4..42f60d8 100644 --- a/src/main/java/com/sovdee/skriptparticles/shapes/Ellipse.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/Ellipse.java @@ -1,16 +1,11 @@ -package com.sovdee.skriptparticles.shapes; +package com.sovdee.shapes; -import com.sovdee.skriptparticles.util.MathUtil; -import org.bukkit.util.Vector; -import org.jetbrains.annotations.Contract; +import com.sovdee.shapes.util.MathUtil; +import org.joml.Vector3d; import java.util.LinkedHashSet; import java.util.Set; -/** - * Represents an ellipse shape with an x radius, z radius, and optional height. - * The radii must be greater than 0, and the height must be non-negative. - */ public class Ellipse extends AbstractShape implements LWHShape { private double xRadius; @@ -18,23 +13,10 @@ public class Ellipse extends AbstractShape implements LWHShape { private double height; protected double cutoffAngle; - /** - * Creates an ellipse with the given x radius and z radius. - * - * @param xRadius the x radius. Must be greater than 0. - * @param zRadius the z radius. Must be greater than 0. - */ public Ellipse(double xRadius, double zRadius) { this(xRadius, zRadius, 0); } - /** - * Creates an ellipse with the given x radius, z radius, and height. - * - * @param xRadius the x radius. Must be greater than 0. - * @param zRadius the z radius. Must be greater than 0. - * @param height the height. Must be non-negative. - */ public Ellipse(double xRadius, double zRadius, double height) { super(); this.xRadius = Math.max(xRadius, MathUtil.EPSILON); @@ -43,33 +25,26 @@ public Ellipse(double xRadius, double zRadius, double height) { this.cutoffAngle = 2 * Math.PI; } - @SuppressWarnings("ConstantConditions") @Override - @Contract(pure = true) - public void generateOutline(Set points) { - Set ellipse = new LinkedHashSet<>(MathUtil.calculateEllipse(xRadius, zRadius, this.getParticleDensity(), cutoffAngle)); + public void generateOutline(Set points) { + Set ellipse = new LinkedHashSet<>(MathUtil.calculateEllipse(xRadius, zRadius, this.getParticleDensity(), cutoffAngle)); if (height != 0) points.addAll(MathUtil.fillVertically(ellipse, height, this.getParticleDensity())); else points.addAll(ellipse); } - @SuppressWarnings("ConstantConditions") @Override - @Contract(pure = true) - public void generateSurface(Set points) { - // if height is not 0, make it a cylinder + public void generateSurface(Set points) { if (height != 0) points.addAll(MathUtil.calculateCylinder(xRadius, zRadius, height, this.getParticleDensity(), cutoffAngle)); else points.addAll(MathUtil.calculateEllipticalDisc(xRadius, zRadius, this.getParticleDensity(), cutoffAngle)); } - @SuppressWarnings("ConstantConditions") @Override - @Contract(pure = true) - public void generateFilled(Set points) { - Set disc = MathUtil.calculateEllipticalDisc(xRadius, zRadius, this.getParticleDensity(), cutoffAngle); + public void generateFilled(Set points) { + Set disc = MathUtil.calculateEllipticalDisc(xRadius, zRadius, this.getParticleDensity(), cutoffAngle); if (height != 0) points.addAll(MathUtil.fillVertically(disc, height, this.getParticleDensity())); else @@ -81,7 +56,6 @@ public void setParticleCount(int particleCount) { particleCount = Math.max(particleCount, 1); switch (this.getStyle()) { case OUTLINE -> { - // this is so fucking cringe double h = (xRadius - zRadius) * (xRadius - zRadius) / ((xRadius + zRadius) + (xRadius + zRadius)); double circumferenceXY = Math.PI * (xRadius + zRadius) * (1 + (3 * h / (10 + Math.sqrt(4 - 3 * h)))); this.setParticleDensity(circumferenceXY / particleCount); @@ -91,9 +65,7 @@ public void setParticleCount(int particleCount) { } @Override - public double getLength() { - return xRadius * 2; - } + public double getLength() { return xRadius * 2; } @Override public void setLength(double length) { @@ -102,9 +74,7 @@ public void setLength(double length) { } @Override - public double getWidth() { - return zRadius * 2; - } + public double getWidth() { return zRadius * 2; } @Override public void setWidth(double width) { @@ -113,9 +83,7 @@ public void setWidth(double width) { } @Override - public double getHeight() { - return height; - } + public double getHeight() { return height; } @Override public void setHeight(double height) { @@ -124,7 +92,6 @@ public void setHeight(double height) { } @Override - @Contract("-> new") public Shape clone() { return this.copyTo(new Ellipse(xRadius, zRadius, height)); } diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/Ellipsoid.java b/shapes-lib/src/main/java/com/sovdee/shapes/Ellipsoid.java similarity index 55% rename from src/main/java/com/sovdee/skriptparticles/shapes/Ellipsoid.java rename to shapes-lib/src/main/java/com/sovdee/shapes/Ellipsoid.java index dd6d44e..5eb7ad6 100644 --- a/src/main/java/com/sovdee/skriptparticles/shapes/Ellipsoid.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/Ellipsoid.java @@ -1,34 +1,22 @@ -package com.sovdee.skriptparticles.shapes; +package com.sovdee.shapes; -import com.sovdee.skriptparticles.util.MathUtil; -import com.sovdee.skriptparticles.util.Quaternion; -import org.bukkit.util.Vector; -import org.jetbrains.annotations.Contract; +import com.sovdee.shapes.util.MathUtil; +import com.sovdee.shapes.util.VectorUtil; +import org.joml.Quaterniond; +import org.joml.Vector3d; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; -/** - * An ellipsoid shape, with an x radius, y radius, and z radius. - * All radii must be greater than 0. - */ public class Ellipsoid extends AbstractShape implements LWHShape { - private static final Quaternion XY_ROTATION = new Quaternion(new Vector(1, 0, 0), (float) (Math.PI / 2)); - private static final Quaternion ZY_ROTATION = new Quaternion(new Vector(0, 0, 1), (float) (Math.PI / 2)); + private static final Quaterniond XY_ROTATION = new Quaterniond().rotateX(Math.PI / 2); + private static final Quaterniond ZY_ROTATION = new Quaterniond().rotateZ(Math.PI / 2); protected double xRadius; protected double yRadius; protected double zRadius; - /** - * Creates an ellipsoid with the given x radius, y radius, and z radius. - * All radii must be greater than 0. - * @param xRadius the x radius. Must be greater than 0. - * @param yRadius the y radius. Must be greater than 0. - * @param zRadius the z radius. Must be greater than 0. - */ public Ellipsoid(double xRadius, double yRadius, double zRadius) { super(); this.xRadius = Math.max(xRadius, MathUtil.EPSILON); @@ -36,64 +24,51 @@ public Ellipsoid(double xRadius, double yRadius, double zRadius) { this.zRadius = Math.max(zRadius, MathUtil.EPSILON); } - @SuppressWarnings("ConstantConditions") @Override - @Contract(pure = true) - public void generateOutline(Set points) { + public void generateOutline(Set points) { double particleDensity = this.getParticleDensity(); points.addAll(MathUtil.calculateEllipse(xRadius, zRadius, particleDensity, 2 * Math.PI)); - points.addAll(XY_ROTATION.transform(MathUtil.calculateEllipse(xRadius, yRadius, particleDensity, 2 * Math.PI))); - points.addAll(ZY_ROTATION.transform(MathUtil.calculateEllipse(yRadius, zRadius, particleDensity, 2 * Math.PI))); + points.addAll(VectorUtil.transform(XY_ROTATION, MathUtil.calculateEllipse(xRadius, yRadius, particleDensity, 2 * Math.PI))); + points.addAll(VectorUtil.transform(ZY_ROTATION, MathUtil.calculateEllipse(yRadius, zRadius, particleDensity, 2 * Math.PI))); } - @SuppressWarnings("ConstantConditions") @Override - @Contract(pure = true) - public void generateSurface(Set points) { - List ellipse; + public void generateSurface(Set points) { + List ellipse; double particleDensity = this.getParticleDensity(); if (xRadius > zRadius) { - ellipse = XY_ROTATION.transform(MathUtil.calculateEllipse(xRadius, yRadius, particleDensity, 2 * Math.PI)); + ellipse = VectorUtil.transform(XY_ROTATION, MathUtil.calculateEllipse(xRadius, yRadius, particleDensity, 2 * Math.PI)); } else { - ellipse = ZY_ROTATION.transform(MathUtil.calculateEllipse(yRadius, zRadius, particleDensity, 2 * Math.PI)); + ellipse = VectorUtil.transform(ZY_ROTATION, MathUtil.calculateEllipse(yRadius, zRadius, particleDensity, 2 * Math.PI)); } points.addAll(generateEllipsoid(ellipse, 1)); } - @SuppressWarnings("ConstantConditions") @Override - @Contract(pure = true) - public void generateFilled(Set points) { - List ellipse; + public void generateFilled(Set points) { + List ellipse; double radius = Math.max(xRadius, zRadius); double particleDensity = this.getParticleDensity(); int steps = (int) Math.round(radius / particleDensity); for (int i = steps; i > 0; i--) { double r = (i / (double) steps); if (xRadius > zRadius) { - ellipse = XY_ROTATION.transform(MathUtil.calculateEllipse(xRadius * r, yRadius * r, particleDensity, 2 * Math.PI)); + ellipse = VectorUtil.transform(XY_ROTATION, MathUtil.calculateEllipse(xRadius * r, yRadius * r, particleDensity, 2 * Math.PI)); } else { - ellipse = ZY_ROTATION.transform(MathUtil.calculateEllipse(yRadius * r, zRadius * r, particleDensity, 2 * Math.PI)); + ellipse = VectorUtil.transform(ZY_ROTATION, MathUtil.calculateEllipse(yRadius * r, zRadius * r, particleDensity, 2 * Math.PI)); } points.addAll(generateEllipsoid(ellipse, r)); } } - /** - * Generates the point on an ellipsoid with the given elliptical cross-section and third radius. - * - * @param ellipse the elliptical cross-section. - * @param radius the third radius. Must be greater than 0. - * @return the points on the ellipsoid. - */ - private Set generateEllipsoid(List ellipse, double radius) { - Set points = new LinkedHashSet<>(); + private Set generateEllipsoid(List ellipse, double radius) { + Set points = new LinkedHashSet<>(); for (int i = 0; i < Math.ceil(ellipse.size() / 4.0); i++) { - double y = ellipse.get(i).getY(); + double y = ellipse.get(i).y; double theta = Math.asin(y / (yRadius * radius)); - for (Vector v2 : MathUtil.calculateEllipse(radius * xRadius * Math.cos(theta), radius * zRadius * Math.cos(theta), this.getParticleDensity(), 2 * Math.PI)) { - points.add(new Vector(v2.getX(), y, v2.getZ())); - points.add(new Vector(v2.getX(), -y, v2.getZ())); + for (Vector3d v2 : MathUtil.calculateEllipse(radius * xRadius * Math.cos(theta), radius * zRadius * Math.cos(theta), this.getParticleDensity(), 2 * Math.PI)) { + points.add(new Vector3d(v2.x, y, v2.z)); + points.add(new Vector3d(v2.x, -y, v2.z)); } } points.addAll(MathUtil.calculateEllipse(radius * xRadius, radius * zRadius, this.getParticleDensity(), 2 * Math.PI)); @@ -105,7 +80,6 @@ public void setParticleCount(int particleCount) { particleCount = Math.max(particleCount, 1); switch (this.getStyle()) { case OUTLINE -> { - // this is so fucking cringe double h = (xRadius - yRadius) * (xRadius - yRadius) / ((xRadius + yRadius) + (xRadius + yRadius)); double circumferenceXY = Math.PI * (xRadius + yRadius) * (1 + (3 * h / (10 + Math.sqrt(4 - 3 * h)))); h = (xRadius - zRadius) * (xRadius - zRadius) / ((xRadius + zRadius) + (xRadius + zRadius)); @@ -126,9 +100,7 @@ public void setParticleCount(int particleCount) { } @Override - public double getLength() { - return xRadius * 2; - } + public double getLength() { return xRadius * 2; } @Override public void setLength(double length) { @@ -137,9 +109,7 @@ public void setLength(double length) { } @Override - public double getWidth() { - return zRadius * 2; - } + public double getWidth() { return zRadius * 2; } @Override public void setWidth(double width) { @@ -148,9 +118,7 @@ public void setWidth(double width) { } @Override - public double getHeight() { - return yRadius * 2; - } + public double getHeight() { return yRadius * 2; } @Override public void setHeight(double height) { @@ -159,9 +127,7 @@ public void setHeight(double height) { } @Override - @Contract("-> new") public Shape clone() { return this.copyTo(new Ellipsoid(xRadius, yRadius, zRadius)); } - } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/EllipticalArc.java b/shapes-lib/src/main/java/com/sovdee/shapes/EllipticalArc.java new file mode 100644 index 0000000..2654203 --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/EllipticalArc.java @@ -0,0 +1,39 @@ +package com.sovdee.shapes; + +import com.sovdee.shapes.util.MathUtil; +import org.joml.Vector3d; + +import java.util.Set; + +public class EllipticalArc extends Ellipse implements CutoffShape { + + public EllipticalArc(double xRadius, double zRadius, double cutoffAngle) { + this(xRadius, zRadius, 0, cutoffAngle); + } + + public EllipticalArc(double xRadius, double zRadius, double height, double cutoffAngle) { + super(xRadius, zRadius, height); + this.cutoffAngle = MathUtil.clamp(cutoffAngle, 0, Math.PI * 2); + } + + @Override + public void generateSurface(Set points) { + generateFilled(points); + } + + @Override + public double getCutoffAngle() { + return cutoffAngle; + } + + @Override + public void setCutoffAngle(double cutoffAngle) { + this.cutoffAngle = MathUtil.clamp(cutoffAngle, 0, Math.PI * 2); + this.setNeedsUpdate(true); + } + + @Override + public Shape clone() { + return this.copyTo(new EllipticalArc(this.getLength(), this.getWidth(), this.getHeight(), cutoffAngle)); + } +} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Heart.java b/shapes-lib/src/main/java/com/sovdee/shapes/Heart.java new file mode 100644 index 0000000..87ed198 --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/Heart.java @@ -0,0 +1,72 @@ +package com.sovdee.shapes; + +import com.sovdee.shapes.util.MathUtil; +import org.joml.Vector3d; + +import java.util.Set; + +public class Heart extends AbstractShape implements LWHShape { + + private double length; + private double width; + private double eccentricity; + + public Heart(double length, double width, double eccentricity) { + super(); + this.length = Math.max(length, MathUtil.EPSILON); + this.width = Math.max(width, MathUtil.EPSILON); + this.eccentricity = Math.max(eccentricity, 1); + } + + @Override + public void generateOutline(Set points) { + points.addAll(MathUtil.calculateHeart(length / 2, width / 2, eccentricity, this.getParticleDensity())); + } + + @Override + public void generateSurface(Set points) { + double particleDensity = this.getParticleDensity(); + for (double w = width, l = length; w > 0 && l > 0; w -= particleDensity * 1.5, l -= particleDensity * 1.5) { + points.addAll(MathUtil.calculateHeart(l / 2, w / 2, eccentricity, particleDensity)); + } + } + + @Override + public void setParticleCount(int particleCount) { } + + @Override + public double getHeight() { return 0; } + + @Override + public void setHeight(double height) { } + + @Override + public double getWidth() { return width; } + + @Override + public void setWidth(double width) { + this.width = Math.max(width, MathUtil.EPSILON); + this.setNeedsUpdate(true); + } + + @Override + public double getLength() { return length; } + + @Override + public void setLength(double length) { + this.length = Math.max(length, MathUtil.EPSILON); + this.setNeedsUpdate(true); + } + + public double getEccentricity() { return eccentricity; } + + public void setEccentricity(double eccentricity) { + this.eccentricity = Math.max(1, eccentricity); + this.setNeedsUpdate(true); + } + + @Override + public Shape clone() { + return this.copyTo(new Heart(length, width, eccentricity)); + } +} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Helix.java b/shapes-lib/src/main/java/com/sovdee/shapes/Helix.java new file mode 100644 index 0000000..9d0d966 --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/Helix.java @@ -0,0 +1,107 @@ +package com.sovdee.shapes; + +import com.sovdee.shapes.util.MathUtil; +import org.joml.Vector3d; + +import java.util.Set; + +public class Helix extends AbstractShape implements RadialShape, LWHShape { + + private double radius; + private double height; + private double slope; + private int direction = 1; + + public Helix(double radius, double height, double slope) { + super(); + this.radius = Math.max(radius, MathUtil.EPSILON); + this.height = Math.max(height, MathUtil.EPSILON); + this.slope = Math.max(slope, MathUtil.EPSILON); + } + + public Helix(double radius, double height, double slope, int direction) { + super(); + this.radius = Math.max(radius, MathUtil.EPSILON); + this.height = Math.max(height, MathUtil.EPSILON); + this.slope = Math.max(slope, MathUtil.EPSILON); + if (direction != 1 && direction != -1) + throw new IllegalArgumentException("Direction must be 1 or -1"); + this.direction = direction; + } + + @Override + public void generateOutline(Set points) { + points.addAll(MathUtil.calculateHelix(radius, height, slope, direction, this.getParticleDensity())); + } + + @Override + public void generateSurface(Set points) { + double particleDensity = this.getParticleDensity(); + for (double r = radius; r > 0; r -= particleDensity) { + points.addAll(MathUtil.calculateHelix(r, height, slope, direction, particleDensity)); + } + } + + @Override + public void setParticleCount(int particleCount) { + particleCount = Math.max(particleCount, 1); + this.setParticleDensity(switch (this.getStyle()) { + case OUTLINE -> (Math.sqrt(slope * slope + radius * radius) * (height / slope) / particleCount); + case FILL, SURFACE -> Math.sqrt(slope * slope + radius * radius * (height / slope) / particleCount); + }); + } + + public double getSlope() { return slope; } + + public void setSlope(double slope) { + this.slope = Math.max(slope, MathUtil.EPSILON); + this.setNeedsUpdate(true); + } + + public int getDirection() { return direction; } + + public void setDirection(int direction) { + if (direction != 1 && direction != -1) + throw new IllegalArgumentException("Direction must be 1 or -1"); + this.direction = direction; + this.setNeedsUpdate(true); + } + + @Override + public double getLength() { return height; } + + @Override + public void setLength(double length) { + height = Math.max(length, MathUtil.EPSILON); + this.setNeedsUpdate(true); + } + + @Override + public double getWidth() { return 0; } + + @Override + public void setWidth(double width) { } + + @Override + public double getHeight() { return height; } + + @Override + public void setHeight(double height) { + this.height = Math.max(height, MathUtil.EPSILON); + this.setNeedsUpdate(true); + } + + @Override + public double getRadius() { return radius; } + + @Override + public void setRadius(double radius) { + this.radius = Math.max(radius, MathUtil.EPSILON); + this.setNeedsUpdate(true); + } + + @Override + public Shape clone() { + return this.copyTo(new Helix(radius, height, slope, direction)); + } +} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/IrregularPolygon.java b/shapes-lib/src/main/java/com/sovdee/shapes/IrregularPolygon.java new file mode 100644 index 0000000..ff00a15 --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/IrregularPolygon.java @@ -0,0 +1,104 @@ +package com.sovdee.shapes; + +import com.sovdee.shapes.util.MathUtil; +import org.joml.Vector3d; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +public class IrregularPolygon extends AbstractShape implements LWHShape { + + private final List vertices; + private double height; + + public IrregularPolygon(Collection vertices) { + super(); + if (vertices.size() < 3) + throw new IllegalArgumentException("A polygon must have at least 3 vertices."); + setBounds(vertices); + this.vertices = flattenVertices(vertices); + } + + public IrregularPolygon(Collection vertices, double height) { + this(vertices); + this.height = Math.max(height, 0); + } + + private List flattenVertices(Collection vertices) { + List flattened = new ArrayList<>(); + for (Vector3d v : vertices) { + flattened.add(new Vector3d(v.x, 0, v.z)); + } + return flattened; + } + + private void setBounds(Collection vertices) { + double low = Double.MAX_VALUE; + double high = -Double.MAX_VALUE; + for (Vector3d v : vertices) { + if (v.y < low) low = v.y; + if (v.y > high) high = v.y; + } + this.height = high - low; + } + + @Override + public void generateOutline(Set points) { + double particleDensity = this.getParticleDensity(); + points.addAll(MathUtil.connectPoints(vertices, particleDensity)); + points.addAll(MathUtil.calculateLine(vertices.get(0), vertices.get(vertices.size() - 1), particleDensity)); + if (height != 0) { + Set upperPoints = new LinkedHashSet<>(); + for (Vector3d v : points) { + upperPoints.add(new Vector3d(v.x, height, v.z)); + } + points.addAll(upperPoints); + for (Vector3d v : vertices) { + points.addAll(MathUtil.calculateLine(v, new Vector3d(v.x, height, v.z), particleDensity)); + } + } + } + + @Override + public void setParticleCount(int particleCount) { + particleCount = Math.max(particleCount, 1); + double perimeter = 0; + for (int i = 0; i < vertices.size() - 1; i++) { + perimeter += vertices.get(i).distance(vertices.get(i + 1)); + } + perimeter += vertices.get(0).distance(vertices.get(vertices.size() - 1)); + perimeter *= 2; + perimeter += vertices.size() * height; + this.setParticleDensity(perimeter / particleCount); + this.setNeedsUpdate(true); + } + + @Override + public double getLength() { return 0; } + + @Override + public void setLength(double length) { } + + @Override + public double getWidth() { return 0; } + + @Override + public void setWidth(double width) { } + + @Override + public double getHeight() { return height; } + + @Override + public void setHeight(double height) { + this.height = Math.max(height, 0); + this.setNeedsUpdate(true); + } + + @Override + public Shape clone() { + return this.copyTo(new IrregularPolygon(vertices, height)); + } +} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/LWHShape.java b/shapes-lib/src/main/java/com/sovdee/shapes/LWHShape.java new file mode 100644 index 0000000..b142db2 --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/LWHShape.java @@ -0,0 +1,13 @@ +package com.sovdee.shapes; + +/** + * Represents a shape that has a length, width, and/or height. + */ +public interface LWHShape extends Shape { + double getLength(); + void setLength(double length); + double getWidth(); + void setWidth(double width); + double getHeight(); + void setHeight(double height); +} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Line.java b/shapes-lib/src/main/java/com/sovdee/shapes/Line.java new file mode 100644 index 0000000..dda5661 --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/Line.java @@ -0,0 +1,96 @@ +package com.sovdee.shapes; + +import com.sovdee.shapes.util.MathUtil; +import org.joml.Vector3d; + +import java.util.Set; + +/** + * A line shape defined by two vector endpoints. + * For dynamic (entity-following) lines, use the plugin-side DynamicLine wrapper. + */ +public class Line extends AbstractShape implements LWHShape { + + private Vector3d start; + private Vector3d end; + + public Line(Vector3d end) { + this(new Vector3d(0, 0, 0), end); + } + + public Line(Vector3d start, Vector3d end) { + super(); + if (start.equals(end)) + throw new IllegalArgumentException("Start and end locations cannot be the same."); + this.start = new Vector3d(start); + this.end = new Vector3d(end); + } + + @Override + public void generateOutline(Set points) { + points.addAll(MathUtil.calculateLine(start, end, this.getParticleDensity())); + } + + public Vector3d getStart() { + return new Vector3d(start); + } + + public void setStart(Vector3d start) { + if (start.equals(end)) + throw new IllegalArgumentException("Start and end points must not be identical"); + this.start = new Vector3d(start); + this.setNeedsUpdate(true); + } + + public Vector3d getEnd() { + return new Vector3d(end); + } + + public void setEnd(Vector3d end) { + if (end.equals(start)) + throw new IllegalArgumentException("Start and end points must not be identical"); + this.end = new Vector3d(end); + this.setNeedsUpdate(true); + } + + @Override + public void setParticleCount(int particleCount) { + particleCount = Math.max(particleCount, 1); + this.setParticleDensity(new Vector3d(end).sub(start).length() / particleCount); + this.setNeedsUpdate(true); + } + + @Override + public double getLength() { + return new Vector3d(start).sub(end).length(); + } + + @Override + public void setLength(double length) { + length = Math.max(length, MathUtil.EPSILON); + Vector3d direction = new Vector3d(end).sub(start).normalize(); + end = new Vector3d(start).add(direction.mul(length)); + this.setNeedsUpdate(true); + } + + @Override + public double getWidth() { return 0; } + + @Override + public void setWidth(double width) { } + + @Override + public double getHeight() { return 0; } + + @Override + public void setHeight(double height) { } + + @Override + public Shape clone() { + return this.copyTo(new Line(new Vector3d(this.start), new Vector3d(this.end))); + } + + public String toString() { + return "Line from " + this.start + " to " + this.end; + } +} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/PolyShape.java b/shapes-lib/src/main/java/com/sovdee/shapes/PolyShape.java new file mode 100644 index 0000000..3213314 --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/PolyShape.java @@ -0,0 +1,11 @@ +package com.sovdee.shapes; + +/** + * Represents a shape that has a number of sides and a side length. + */ +public interface PolyShape extends Shape { + int getSides(); + void setSides(int sides); + double getSideLength(); + void setSideLength(double sideLength); +} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/RadialShape.java b/shapes-lib/src/main/java/com/sovdee/shapes/RadialShape.java new file mode 100644 index 0000000..109d226 --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/RadialShape.java @@ -0,0 +1,10 @@ +package com.sovdee.shapes; + +/** + * Represents a shape that has a radius. + * The radius must be greater than 0. + */ +public interface RadialShape extends Shape { + double getRadius(); + void setRadius(double radius); +} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Rectangle.java b/shapes-lib/src/main/java/com/sovdee/shapes/Rectangle.java new file mode 100644 index 0000000..9b685bd --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/Rectangle.java @@ -0,0 +1,156 @@ +package com.sovdee.shapes; + +import com.sovdee.shapes.util.MathUtil; +import org.joml.Vector3d; + +import java.util.Set; + +/** + * A rectangle shape, defined by a plane and dimensions. + * For dynamic (entity-following) rectangles, use the plugin-side DynamicRectangle wrapper. + */ +public class Rectangle extends AbstractShape implements LWHShape { + + private Plane plane; + private double halfLength; + private double halfWidth; + private double lengthStep = 1.0; + private double widthStep = 1.0; + private Vector3d centerOffset = new Vector3d(0, 0, 0); + + public Rectangle(double length, double width, Plane plane) { + super(); + this.plane = plane; + this.halfLength = Math.max(length / 2, MathUtil.EPSILON); + this.halfWidth = Math.max(width / 2, MathUtil.EPSILON); + calculateSteps(); + } + + public Rectangle(Vector3d cornerA, Vector3d cornerB, Plane plane) { + super(); + if (cornerA.equals(cornerB)) + throw new IllegalArgumentException("Corners cannot be the same."); + this.plane = plane; + setLengthWidth(cornerA, cornerB); + centerOffset = new Vector3d(cornerB).add(cornerA).mul(0.5); + switch (plane) { + case XZ -> centerOffset.y = 0; + case XY -> centerOffset.z = 0; + case YZ -> centerOffset.x = 0; + } + calculateSteps(); + } + + private void setLengthWidth(Vector3d cornerA, Vector3d cornerB) { + double length = switch (plane) { + case XZ, XY -> Math.abs(cornerA.x - cornerB.x); + case YZ -> Math.abs(cornerA.y - cornerB.y); + }; + double width = switch (plane) { + case XZ, YZ -> Math.abs(cornerA.z - cornerB.z); + case XY -> Math.abs(cornerA.y - cornerB.y); + }; + this.halfWidth = Math.abs(width) / 2; + this.halfLength = Math.abs(length) / 2; + } + + private Vector3d vectorFromLengthWidth(double length, double width) { + return switch (plane) { + case XZ -> new Vector3d(length, 0, width); + case XY -> new Vector3d(length, width, 0); + case YZ -> new Vector3d(0, length, width); + }; + } + + private void calculateSteps() { + double particleDensity = this.getParticleDensity(); + lengthStep = 2 * halfWidth / Math.round(2 * halfWidth / particleDensity); + widthStep = 2 * halfLength / Math.round(2 * halfLength / particleDensity); + } + + @Override + public void generateOutline(Set points) { + for (double l = -halfLength + widthStep; l < halfLength; l += widthStep) { + points.add(vectorFromLengthWidth(l, -halfWidth)); + points.add(vectorFromLengthWidth(l, halfWidth)); + } + for (double w = -halfWidth; w <= halfWidth; w += lengthStep) { + points.add(vectorFromLengthWidth(-halfLength, w)); + points.add(vectorFromLengthWidth(halfLength, w)); + } + } + + @Override + public void generateSurface(Set points) { + for (double w = -halfWidth; w <= halfWidth; w += lengthStep) { + for (double l = -halfLength; l <= halfLength; l += widthStep) { + points.add(vectorFromLengthWidth(l, w)); + } + } + } + + @Override + public void generatePoints(Set points) { + calculateSteps(); + super.generatePoints(points); + points.forEach(vector -> vector.add(centerOffset)); + } + + @Override + public void setParticleCount(int particleCount) { + particleCount = Math.max(particleCount, 1); + switch (this.getStyle()) { + case FILL, SURFACE -> this.setParticleDensity(Math.sqrt(4 * halfWidth * halfLength / particleCount)); + case OUTLINE -> this.setParticleDensity(4 * (halfWidth + halfLength) / particleCount); + } + this.setNeedsUpdate(true); + } + + @Override + public double getLength() { return halfLength * 2; } + + @Override + public void setLength(double length) { + this.halfLength = Math.max(length / 2, MathUtil.EPSILON); + this.setNeedsUpdate(true); + } + + @Override + public double getWidth() { return halfWidth * 2; } + + @Override + public void setWidth(double width) { + this.halfWidth = Math.max(width / 2, MathUtil.EPSILON); + this.setNeedsUpdate(true); + } + + @Override + public double getHeight() { return 0; } + + @Override + public void setHeight(double height) { } + + public Plane getPlane() { return plane; } + + public void setPlane(Plane plane) { + this.plane = plane; + this.setNeedsUpdate(true); + } + + @Override + public Shape clone() { + Rectangle rectangle = new Rectangle(this.getLength(), this.getWidth(), plane); + rectangle.centerOffset = new Vector3d(this.centerOffset); + return this.copyTo(rectangle); + } + + @Override + public String toString() { + String axis = this.plane.toString().toLowerCase(); + return axis + " rectangle with length " + this.getLength() + " and width " + this.getWidth(); + } + + public enum Plane { + XZ, XY, YZ + } +} diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/RegularPolygon.java b/shapes-lib/src/main/java/com/sovdee/shapes/RegularPolygon.java similarity index 58% rename from src/main/java/com/sovdee/skriptparticles/shapes/RegularPolygon.java rename to shapes-lib/src/main/java/com/sovdee/shapes/RegularPolygon.java index 1b062d2..85f48b0 100644 --- a/src/main/java/com/sovdee/skriptparticles/shapes/RegularPolygon.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/RegularPolygon.java @@ -1,55 +1,28 @@ -package com.sovdee.skriptparticles.shapes; +package com.sovdee.shapes; -import com.sovdee.skriptparticles.util.MathUtil; -import org.bukkit.util.Vector; -import org.jetbrains.annotations.Contract; +import com.sovdee.shapes.util.MathUtil; +import org.joml.Vector3d; import java.util.Set; -/** - * A regular polygon shape. This shape is defined by a radius and either a side count or an angle between adjacent vertices. - * The polygon can be a prism if a height is given. - */ public class RegularPolygon extends AbstractShape implements PolyShape, RadialShape, LWHShape { private double angle; private double radius; private double height; - /** - * Creates a new regular polygon shape with the given side count and radius. - * @param sides the number of sides of the polygon. Must be greater than 2. - * @param radius the radius of the polygon. Must be greater than 0. - */ public RegularPolygon(int sides, double radius) { this((Math.PI * 2) / sides, radius, 0); } - /** - * Creates a new regular polygon shape with the given angle and radius. - * @param angle the angle between adjacent vertices in radians. Must evenly divide 2pi and be between 0 (exclusive) and 2pi/3 (inclusive). - * @param radius the radius of the polygon. Must be greater than 0. - */ public RegularPolygon(double angle, double radius) { this(angle, radius, 0); } - /** - * Creates a new regular polygon shape with the given side count, radius, and height. - * @param sides the number of sides of the polygon. Must be greater than 2. - * @param radius the radius of the polygon. Must be greater than 0. - * @param height the height of the polygon. Must be non-negative. - */ public RegularPolygon(int sides, double radius, double height) { this((Math.PI * 2) / sides, radius, height); } - /** - * Creates a new regular polygon shape with the given angle, radius, and height. - * @param angle the angle between adjacent vertices in radians. Should evenly divide 2pi and must be between 0 (exclusive) and 2pi/3 (inclusive). - * @param radius the radius of the polygon. Must be greater than 0. - * @param height the height of the polygon. Must be non-negative. - */ public RegularPolygon(double angle, double radius, double height) { super(); this.angle = MathUtil.clamp(angle, MathUtil.EPSILON, Math.PI * 2 / 3); @@ -57,34 +30,29 @@ public RegularPolygon(double angle, double radius, double height) { this.height = Math.max(height, 0); } - @SuppressWarnings("ConstantConditions") @Override - @Contract(pure = true) - public void generateOutline(Set points) { + public void generateOutline(Set points) { if (height == 0) points.addAll(MathUtil.calculateRegularPolygon(this.radius, this.angle, this.getParticleDensity(), true)); else points.addAll(MathUtil.calculateRegularPrism(this.radius, this.angle, this.height, this.getParticleDensity(), true)); } - @SuppressWarnings("ConstantConditions") @Override - @Contract(pure = true) - public void generateSurface(Set points) { + public void generateSurface(Set points) { if (height == 0) points.addAll(MathUtil.calculateRegularPolygon(this.radius, this.angle, this.getParticleDensity(), false)); else points.addAll(MathUtil.calculateRegularPrism(this.radius, this.angle, this.height, this.getParticleDensity(), false)); } - @SuppressWarnings("ConstantConditions") @Override - public void generateFilled(Set points) { + public void generateFilled(Set points) { if (height == 0) generateSurface(points); else { double particleDensity = this.getParticleDensity(); - Set polygon = MathUtil.calculateRegularPolygon(this.radius, this.angle, particleDensity, false); + Set polygon = MathUtil.calculateRegularPolygon(this.radius, this.angle, particleDensity, false); points.addAll(MathUtil.fillVertically(polygon, height, particleDensity)); } } @@ -110,9 +78,7 @@ public void setParticleCount(int particleCount) { } @Override - public int getSides() { - return (int) (Math.PI * 2 / this.angle); - } + public int getSides() { return (int) (Math.PI * 2 / this.angle); } @Override public void setSides(int sides) { @@ -121,9 +87,7 @@ public void setSides(int sides) { } @Override - public double getSideLength() { - return this.radius * 2 * Math.sin(this.angle / 2); - } + public double getSideLength() { return this.radius * 2 * Math.sin(this.angle / 2); } @Override public void setSideLength(double sideLength) { @@ -134,9 +98,7 @@ public void setSideLength(double sideLength) { } @Override - public double getRadius() { - return this.radius; - } + public double getRadius() { return this.radius; } @Override public void setRadius(double radius) { @@ -145,29 +107,19 @@ public void setRadius(double radius) { } @Override - public double getLength() { - return 0; - } + public double getLength() { return 0; } @Override - public void setLength(double length) { - // intentionally empty - } + public void setLength(double length) { } @Override - public double getWidth() { - return 0; - } + public double getWidth() { return 0; } @Override - public void setWidth(double width) { - // intentionally empty - } + public void setWidth(double width) { } @Override - public double getHeight() { - return height; - } + public double getHeight() { return height; } @Override public void setHeight(double height) { @@ -176,7 +128,6 @@ public void setHeight(double height) { } @Override - @Contract("-> new") public Shape clone() { return this.copyTo(new RegularPolygon(angle, radius, height)); } @@ -185,5 +136,4 @@ public Shape clone() { public String toString() { return "regular polygon with " + getSides() + " sides and radius " + getRadius(); } - } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/RegularPolyhedron.java b/shapes-lib/src/main/java/com/sovdee/shapes/RegularPolyhedron.java new file mode 100644 index 0000000..5d6ed5a --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/RegularPolyhedron.java @@ -0,0 +1,208 @@ +package com.sovdee.shapes; + +import com.sovdee.shapes.util.MathUtil; +import com.sovdee.shapes.util.VectorUtil; +import org.joml.Quaterniond; +import org.joml.Vector3d; + +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; + +public class RegularPolyhedron extends AbstractShape implements RadialShape, PolyShape { + + private static final Quaterniond[] TETRAHEDRON_FACES = { + new Quaterniond(1.0, 0.0, 0.0, 0), + new Quaterniond(-0.5, -0.0, -0.288675134594813, 0.816496580927726), + new Quaterniond(0.5, 0.0, -0.288675134594813, 0.816496580927726), + new Quaterniond(0.0, 0.0, 0.5773502691896258, 0.816496580927726) + }; + private static final Quaterniond[] OCTAHEDRON_FACES = { + new Quaterniond(0.0, 0.0, 0.45970084338098305, 0.8880738339771153), + new Quaterniond(0.3250575836718681, 0.6279630301995544, 0.32505758367186816, 0.6279630301995545), + new Quaterniond(0.45970084338098305, 0.8880738339771153, 0, 0), + new Quaterniond(0.32505758367186816, 0.6279630301995545, -0.3250575836718681, -0.6279630301995544), + new Quaterniond(0.0, 0.0, 0.8880738339771153, -0.45970084338098316), + new Quaterniond(0.6279630301995544, -0.3250575836718682, 0.6279630301995545, -0.3250575836718682), + new Quaterniond(0.8880738339771153, -0.45970084338098316, 0, 0), + new Quaterniond(0.6279630301995545, -0.3250575836718682, -0.6279630301995544, 0.3250575836718682) + }; + private static final Quaterniond[] ICOSAHEDRON_FACES = { + new Quaterniond(1.0, 0.0, 0.0, 0), + new Quaterniond(0.0, 1.0, 0.0, 0), + new Quaterniond(0.3090169943749475, 0.0, 0.17841104488654494, 0.9341723589627158), + new Quaterniond(-0.5, 0.8090169943749475, 0.288675134594813, 0.110264089708268), + new Quaterniond(0, 0.8090169943749475, 0.5773502691896256, -0.110264089708268), + new Quaterniond(0.3090169943749475, 0.0, -0.1784110448865451, -0.9341723589627157), + new Quaterniond(0.5, -0.8090169943749475, 0.288675134594813, 0.110264089708268), + new Quaterniond(0, 0.8090169943749475, -0.5773502691896261, 0.110264089708268), + new Quaterniond(0.0, 0.0, 0.35682208977309, -0.9341723589627157), + new Quaterniond(-0.5, -0.8090169943749475, -0.288675134594813, -0.110264089708268), + new Quaterniond(0.5, 0.8090169943749475, -0.288675134594813, -0.110264089708268), + new Quaterniond(-0.8090169943749475, -0.0, -0.46708617948135794, 0.35682208977309), + new Quaterniond(0.3090169943749475, 0.5, -0.7557613140761709, -0.288675134594813), + new Quaterniond(0.8090169943749475, 0.0, -0.46708617948135794, 0.35682208977309), + new Quaterniond(-0.5, -0.5, -0.6454972243679027, 0.288675134594813), + new Quaterniond(0.0, 0.0, 0.9341723589627157, 0.35682208977309), + new Quaterniond(-0.8090169943749475, 0.5, 0.110264089708268, -0.288675134594813) + }; + private static final Quaterniond[] DODECAHEDRON_FACES = { + new Quaterniond(0.0, 0.3090169943749475, 0.0, 0.9510565162951536), + new Quaterniond(-0.3090169943749475, 0, 0.9510565162951536, 0), + new Quaterniond(0.0, 0.0, 0.8506508083520399, 0.5257311121191337), + new Quaterniond(0.0, 0.0, 0.5257311121191337, -0.8506508083520399), + new Quaterniond(0.5, 0.3090169943749475, 0.6881909602355868, 0.42532540417602), + new Quaterniond(0.3090169943749475, -0.5, 0.42532540417602, -0.6881909602355868), + new Quaterniond(0.8090169943749475, 0.5, 0.2628655560595668, 0.1624598481164532), + new Quaterniond(0.5, -0.8090169943749475, 0.1624598481164532, -0.2628655560595668), + new Quaterniond(0.8090169943749475, 0.5, -0.2628655560595668, -0.1624598481164532), + new Quaterniond(0.5, -0.8090169943749475, -0.1624598481164532, 0.2628655560595668), + new Quaterniond(0.5, 0.3090169943749475, -0.6881909602355868, -0.42532540417602), + new Quaterniond(0.3090169943749475, -0.5, -0.42532540417602, 0.6881909602355868) + }; + private double radius; + private int faces; + + public RegularPolyhedron(double radius, int faces) { + super(); + this.radius = Math.max(radius, MathUtil.EPSILON); + this.faces = switch (faces) { + case 4, 8, 12, 20 -> faces; + default -> 4; + }; + } + + @Override + public void generateOutline(Set points) { + points.addAll(switch (faces) { + case 4 -> generatePolyhedron(TETRAHEDRON_FACES, radius); + case 8 -> generatePolyhedron(OCTAHEDRON_FACES, radius); + case 20 -> generatePolyhedron(ICOSAHEDRON_FACES, radius); + case 12 -> generatePolyhedron(DODECAHEDRON_FACES, radius); + default -> new HashSet<>(); + }); + } + + @Override + public void generateFilled(Set points) { + double step = radius / Math.round(radius / this.getParticleDensity()); + switch (faces) { + case 4: + for (double i = radius; i > 0; i -= step) + points.addAll(generatePolyhedron(TETRAHEDRON_FACES, i)); + break; + case 8: + for (double i = radius; i > 0; i -= step) + points.addAll(generatePolyhedron(OCTAHEDRON_FACES, i)); + break; + case 12: + for (double i = radius; i > 0; i -= step) + points.addAll(generatePolyhedron(DODECAHEDRON_FACES, i)); + break; + case 20: + for (double i = radius; i > 0; i -= step) + points.addAll(generatePolyhedron(ICOSAHEDRON_FACES, i)); + break; + } + } + + private Set generatePolyhedron(Quaterniond[] rotations, double radius) { + Set points = new LinkedHashSet<>(); + int sides = this.faces == 12 ? 5 : 3; + double sideLength = switch (faces) { + case 4 -> radius / 0.6123724356957945; + case 8 -> radius / 0.7071067811865; + case 12 -> radius / 1.401258538; + case 20 -> radius / 0.9510565162951535; + default -> 0.0; + }; + double inscribedRadius = switch (this.faces) { + case 4 -> sideLength / 4.89897948556; + case 8 -> sideLength * 0.408248290; + case 12 -> sideLength * 1.113516364; + case 20 -> sideLength * 0.7557613141; + default -> 1; + }; + Vector3d offset = new Vector3d(0, inscribedRadius, 0); + double faceRadius = sideLength / (2 * Math.sin(Math.PI / sides)); + Style style = this.getStyle(); + for (Quaterniond rotation : rotations) { + Set facePoints = new LinkedHashSet<>(switch (style) { + case OUTLINE -> generateFaceOutline(sides, faceRadius); + case FILL, SURFACE -> generateFaceSurface(sides, faceRadius); + }); + facePoints.forEach(point -> rotation.transform(point.add(offset))); + points.addAll(facePoints); + } + return points; + } + + private Set generateFaceOutline(int sides, double radius) { + return new LinkedHashSet<>(MathUtil.calculateRegularPolygon(radius, 2 * Math.PI / sides, this.getParticleDensity(), true)); + } + + private Set generateFaceSurface(int sides, double radius) { + Set facePoints = new LinkedHashSet<>(); + double particleDensity = this.getParticleDensity(); + double apothem = radius * Math.cos(Math.PI / sides); + double radiusStep = radius / Math.round(apothem / particleDensity); + for (double subRadius = radius; subRadius > 0; subRadius -= radiusStep) { + facePoints.addAll(MathUtil.calculateRegularPolygon(subRadius, 2 * Math.PI / sides, particleDensity, false)); + } + facePoints.add(new Vector3d(0, 0, 0)); + return facePoints; + } + + @Override + public void setParticleCount(int particleCount) { } + + @Override + public Shape clone() { + return this.copyTo(new RegularPolyhedron(radius, faces)); + } + + @Override + public int getSides() { return faces; } + + @Override + public void setSides(int sides) { + switch (sides) { + case 4, 8, 12, 20 -> this.faces = sides; + default -> { return; } + } + this.setNeedsUpdate(true); + } + + @Override + public double getSideLength() { + return switch (faces) { + case 4 -> radius / 0.6123724356957945; + case 8 -> radius / 0.7071067811865; + case 12 -> radius / 1.401258538; + case 20 -> radius / 0.9510565162951535; + default -> 0.0; + }; + } + + @Override + public void setSideLength(double sideLength) { + sideLength = Math.max(sideLength, MathUtil.EPSILON); + switch (faces) { + case 4 -> this.radius = sideLength * 0.6123724356957945; + case 8 -> this.radius = sideLength * 0.7071067811865; + case 12 -> this.radius = sideLength * 1.401258538; + case 20 -> this.radius = sideLength * 0.9510565162951535; + default -> { return; } + } + this.setNeedsUpdate(true); + } + + @Override + public double getRadius() { return radius; } + + @Override + public void setRadius(double radius) { + this.radius = Math.max(radius, MathUtil.EPSILON); + this.setNeedsUpdate(true); + } +} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Shape.java b/shapes-lib/src/main/java/com/sovdee/shapes/Shape.java new file mode 100644 index 0000000..2c274a8 --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/Shape.java @@ -0,0 +1,259 @@ +package com.sovdee.shapes; + +import org.joml.Quaterniond; +import org.joml.Vector3d; + +import java.util.Comparator; +import java.util.Set; +import java.util.UUID; +import java.util.function.BiConsumer; + +/** + * Represents a geometric shape defined by a set of points. + * Shapes have a style, orientation, scale, and offset which affect point generation. + */ +public interface Shape extends Cloneable { + + /** + * Gets the points for the shape using the shape's own orientation. + * Uses cached points if the shape has not been modified. + * + * @return A set of points that make up the shape. + */ + Set getPoints(); + + /** + * Sets the points for the shape. + * + * @param points The points to use. + */ + void setPoints(Set points); + + /** + * Gets the points for the shape using the given orientation. + * Uses cached points if the shape has not been modified. + * + * @param orientation The orientation to apply. + * @return A set of points that make up the shape. + */ + Set getPoints(Quaterniond orientation); + + /** + * Generates the points for the shape based on the current style. + * No orientation, offset, or scale is applied. + */ + default void generatePoints(Set points) { + getStyle().generatePoints(this, points); + } + + /** + * Generates the outline points for the shape. + * + * @param points A set that will be filled with the points. + */ + void generateOutline(Set points); + + /** + * Generates the surface points for the shape. + * Default implementation returns the same as generateOutline(). + * + * @param points A set that will be filled with the points. + */ + void generateSurface(Set points); + + /** + * Generates the filled points for the shape. + * Default implementation returns the same as generateSurface(). + * + * @param points A set that will be filled with the points. + */ + void generateFilled(Set points); + + /** + * @return the relative X axis of the shape. + */ + Vector3d getRelativeXAxis(boolean useLastOrientation); + + /** + * @return the relative Y axis of the shape. + */ + Vector3d getRelativeYAxis(boolean useLastOrientation); + + /** + * @return the relative Z axis of the shape. + */ + Vector3d getRelativeZAxis(boolean useLastOrientation); + + /** + * @return the style of the shape. + */ + Style getStyle(); + + /** + * Sets the style of the shape. + * + * @param style The style to use. + */ + void setStyle(Style style); + + /** + * @return a clone of the orientation of the shape. + */ + Quaterniond getOrientation(); + + /** + * Sets the orientation of the shape. + * + * @param orientation The orientation to use. + */ + void setOrientation(Quaterniond orientation); + + /** + * @return the scale of the shape. + */ + double getScale(); + + /** + * Sets the scale of the shape. + * + * @param scale The scale to use. + */ + void setScale(double scale); + + /** + * @return the offset of the shape. + */ + Vector3d getOffset(); + + /** + * Sets the offset of the shape. + * + * @param offset The offset vector to use. + */ + void setOffset(Vector3d offset); + + /** + * @return the UUID of the shape. + */ + UUID getUUID(); + + /** + * @return The comparator used to order points, or null for default ordering. + */ + Comparator getOrdering(); + + /** + * Sets the comparator to order the points. + * + * @param comparator the Comparator to use, or null for default ordering. + */ + void setOrdering(Comparator comparator); + + /** + * @return the particle density of the shape. + */ + double getParticleDensity(); + + /** + * Sets the particle density of the shape. + * + * @param particleDensity The density in meters per particle. Must be greater than 0. + */ + void setParticleDensity(double particleDensity); + + /** + * @return the number of points in the shape. + */ + int getParticleCount(); + + /** + * Sets the approximate number of points for the shape. + * + * @param particleCount The target number of points. Must be greater than 0. + */ + void setParticleCount(int particleCount); + + /** + * @return whether the shape needs a point update. + */ + boolean needsUpdate(); + + /** + * Marks the shape as needing an update or not. + * + * @param needsUpdate Whether the shape needs an update. + */ + void setNeedsUpdate(boolean needsUpdate); + + /** + * @return a deep copy of the shape. + */ + Shape clone(); + + /** + * Copies the shape's properties to a new shape. + * + * @param shape The shape to copy properties to. + * @return the updated shape. + */ + Shape copyTo(Shape shape); + + /** + * Gets the physical state of the shape for change detection. + * + * @return A state object representing the shape's current state. + */ + State getState(); + + /** + * Gets the physical state with a custom orientation. + * + * @param orientation The orientation to use for the state. + * @return A state object representing the shape's state. + */ + State getState(Quaterniond orientation); + + /** + * Sets the last state of the shape for change detection. + * + * @param state The state to set. + */ + void setLastState(State state); + + + /** + * The style of a shape, which determines how it is drawn. + */ + enum Style { + OUTLINE((shape, points) -> shape.generateOutline(points)), + SURFACE((shape, points) -> shape.generateSurface(points)), + FILL((shape, points) -> shape.generateFilled(points)); + + private final BiConsumer> generatePoints; + + Style(BiConsumer> generatePoints) { + this.generatePoints = generatePoints; + } + + public void generatePoints(Shape shape, Set points) { + generatePoints.accept(shape, points); + } + + public String toString() { + return name().toLowerCase(); + } + } + + /** + * A state object for checking if a shape has changed. + */ + record State(Style style, int orientationHash, double scale, int offsetHash, double particleDensity) { + public boolean equals(State state) { + return state.style() == style && + state.orientationHash() == orientationHash && + state.scale() == scale && + state.offsetHash() == offsetHash && + state.particleDensity() == particleDensity; + } + } +} diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/Sphere.java b/shapes-lib/src/main/java/com/sovdee/shapes/Sphere.java similarity index 69% rename from src/main/java/com/sovdee/skriptparticles/shapes/Sphere.java rename to shapes-lib/src/main/java/com/sovdee/shapes/Sphere.java index d6d9f3d..4cf6a49 100644 --- a/src/main/java/com/sovdee/skriptparticles/shapes/Sphere.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/Sphere.java @@ -1,26 +1,16 @@ -package com.sovdee.skriptparticles.shapes; +package com.sovdee.shapes; -import com.sovdee.skriptparticles.util.MathUtil; -import org.bukkit.util.Vector; -import org.jetbrains.annotations.Contract; +import com.sovdee.shapes.util.MathUtil; +import org.joml.Vector3d; import java.util.Set; -/** - * A sphere is a 3D shape that is generated by rotating a circle around a central axis. - * The radius of the sphere is the distance from the central point to the circle. - * The generated points are calculated using the golden ratio. - */ public class Sphere extends AbstractShape implements RadialShape { private double radius; protected double cutoffAngle; protected double cutoffAngleCos; - /** - * Creates a sphere with the given radius. - * @param radius the radius of the sphere. Must be greater than 0. - */ public Sphere(double radius) { super(); this.radius = Math.max(radius, MathUtil.EPSILON); @@ -30,25 +20,19 @@ public Sphere(double radius) { } @Override - @Contract(pure = true) - public void generateOutline(Set points) { + public void generateOutline(Set points) { this.generateSurface(points); } - - @SuppressWarnings("ConstantConditions") @Override - @Contract(pure = true) - public void generateSurface(Set points) { + public void generateSurface(Set points) { double particleDensity = this.getParticleDensity(); int pointCount = 4 * (int) (Math.PI * radius * radius / (particleDensity * particleDensity)); points.addAll(MathUtil.calculateFibonacciSphere(pointCount, radius, cutoffAngle)); } - @SuppressWarnings("ConstantConditions") @Override - @Contract(pure = true) - public void generateFilled(Set points) { + public void generateFilled(Set points) { double particleDensity = this.getParticleDensity(); int subSpheres = (int) (radius / particleDensity) - 1; double radiusStep = radius / subSpheres; @@ -71,9 +55,7 @@ public void setParticleCount(int particleCount) { } @Override - public double getRadius() { - return radius; - } + public double getRadius() { return radius; } @Override public void setRadius(double radius) { @@ -82,7 +64,6 @@ public void setRadius(double radius) { } @Override - @Contract("-> new") public Shape clone() { return this.copyTo(new Sphere(radius)); } diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/SphericalCap.java b/shapes-lib/src/main/java/com/sovdee/shapes/SphericalCap.java similarity index 54% rename from src/main/java/com/sovdee/skriptparticles/shapes/SphericalCap.java rename to shapes-lib/src/main/java/com/sovdee/shapes/SphericalCap.java index 47987fe..964c646 100644 --- a/src/main/java/com/sovdee/skriptparticles/shapes/SphericalCap.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/SphericalCap.java @@ -1,19 +1,9 @@ -package com.sovdee.skriptparticles.shapes; +package com.sovdee.shapes; -import com.sovdee.skriptparticles.util.MathUtil; -import org.jetbrains.annotations.Contract; +import com.sovdee.shapes.util.MathUtil; -/** - * A spherical cap is a sphere with a portion of it cut off. - * The cutoff angle is the angle between the center of the sphere and the cutoff plane. - */ public class SphericalCap extends Sphere implements CutoffShape { - /** - * Generates a spherical cap with the given radius and cutoff angle. - * @param radius the radius of the sphere. Must be greater than 0. - * @param cutoffAngle the angle in radians between the center of the sphere and the cutoff plane. Will be clamped to be between 0 and pi. - */ public SphericalCap(double radius, double cutoffAngle) { super(radius); this.cutoffAngle = MathUtil.clamp(cutoffAngle, 0, Math.PI); @@ -33,7 +23,6 @@ public void setCutoffAngle(double cutoffAngle) { } @Override - @Contract("-> new") public Shape clone() { return this.copyTo(new SphericalCap(this.getRadius(), cutoffAngle)); } diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/Star.java b/shapes-lib/src/main/java/com/sovdee/shapes/Star.java similarity index 50% rename from src/main/java/com/sovdee/skriptparticles/shapes/Star.java rename to shapes-lib/src/main/java/com/sovdee/shapes/Star.java index 9d5694c..80a8b79 100644 --- a/src/main/java/com/sovdee/skriptparticles/shapes/Star.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/Star.java @@ -1,28 +1,16 @@ -package com.sovdee.skriptparticles.shapes; +package com.sovdee.shapes; -import com.sovdee.skriptparticles.util.MathUtil; -import org.bukkit.util.Vector; -import org.jetbrains.annotations.Contract; +import com.sovdee.shapes.util.MathUtil; +import org.joml.Vector3d; -import java.util.LinkedHashSet; import java.util.Set; -/** - * A star shape. This shape is defined by an inner radius, an outer radius, and an angle. - * The angle is the angle between the points of the star. - */ public class Star extends AbstractShape { private double innerRadius; private double outerRadius; private double angle; - /** - * Creates a new star shape with the given inner radius, outer radius, and angle. - * @param innerRadius the inner radius of the star. Must be greater than 0. - * @param outerRadius the outer radius of the star. Must be greater than 0. - * @param angle the angle between the points of the star in radians. Must be between 0 (exclusive) and pi (inclusive), and should evenly divide 2*pi. - */ public Star(double innerRadius, double outerRadius, double angle) { super(); this.innerRadius = Math.max(innerRadius, MathUtil.EPSILON); @@ -30,17 +18,13 @@ public Star(double innerRadius, double outerRadius, double angle) { this.angle = MathUtil.clamp(angle, MathUtil.EPSILON, Math.PI); } - @SuppressWarnings("ConstantConditions") @Override - @Contract(pure = true) - public void generateOutline(Set points) { + public void generateOutline(Set points) { points.addAll(MathUtil.calculateStar(innerRadius, outerRadius, angle, this.getParticleDensity())); } - @SuppressWarnings("ConstantConditions") @Override - @Contract(pure = true) - public void generateSurface(Set points) { + public void generateSurface(Set points) { double minRadius = Math.min(innerRadius, outerRadius); double particleDensity = this.getParticleDensity(); for (double r = 0; r < minRadius; r += particleDensity) { @@ -56,54 +40,24 @@ public void setParticleCount(int particleCount) { this.setParticleDensity(perimeter / particleCount); } - /** - * Gets the inner radius of the star. - * @return the inner radius of the star. - */ - public double getInnerRadius() { - return innerRadius; - } + public double getInnerRadius() { return innerRadius; } - /** - * Sets the inner radius of the star. - * @param innerRadius the new inner radius of the star. Must be greater than 0. - */ public void setInnerRadius(double innerRadius) { this.innerRadius = Math.max(innerRadius, MathUtil.EPSILON); this.setNeedsUpdate(true); } - /** - * Gets the outer radius of the star. - * @return the outer radius of the star. - */ - public double getOuterRadius() { - return outerRadius; - } + public double getOuterRadius() { return outerRadius; } - /** - * Sets the outer radius of the star. - * @param outerRadius the new outer radius of the star. Must be greater than 0. - */ public void setOuterRadius(double outerRadius) { this.outerRadius = Math.max(outerRadius, MathUtil.EPSILON); this.setNeedsUpdate(true); } - /** - * Gets the number of points on the star. - * - * @return the number of points on the star. - */ public int getStarPoints() { return (int) (Math.PI * 2 / angle); } - /** - * Sets the number of points on the star. - * - * @param starPoints the new number of points on the star. Must be at least 2. - */ public void setStarPoints(int starPoints) { starPoints = Math.max(starPoints, 2); this.angle = Math.PI * 2 / starPoints; @@ -111,7 +65,6 @@ public void setStarPoints(int starPoints) { } @Override - @Contract("-> new") public Shape clone() { return this.copyTo(new Star(innerRadius, outerRadius, angle)); } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/util/MathUtil.java b/shapes-lib/src/main/java/com/sovdee/shapes/util/MathUtil.java new file mode 100644 index 0000000..7152b04 --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/util/MathUtil.java @@ -0,0 +1,248 @@ +package com.sovdee.shapes.util; + +import org.joml.Vector3d; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class MathUtil { + public static final double PHI = Math.PI * (3.0 - Math.sqrt(5.0)); + public static final double PHI_RECIPROCAL = 1.0 / PHI; + public static final double PHI_SQUARED = PHI * PHI; + public static final double[] SPHERE_THETA_COS = new double[4096]; + public static final double[] SPHERE_THETA_SIN = new double[4096]; + public static final double EPSILON = 0.0001; + + static { + for (int i = 0; i < SPHERE_THETA_COS.length; i++) { + SPHERE_THETA_COS[i] = Math.cos(MathUtil.PHI * i); + SPHERE_THETA_SIN[i] = Math.sin(MathUtil.PHI * i); + } + } + + public static double clamp(double value, double min, double max) { + return Math.max(min, Math.min(max, value)); + } + + public static Set calculateFibonacciSphere(int pointCount, double radius) { + return calculateFibonacciSphere(pointCount, radius, Math.PI); + } + + public static Set calculateFibonacciSphere(int pointCount, double radius, double angleCutoff) { + Set points = new LinkedHashSet<>(); + double y = 1; + if (angleCutoff > Math.PI) angleCutoff = Math.PI; + double yLimit = Math.cos(angleCutoff); + + double yStep = 2.0 / pointCount; + int preCompPoints = Math.min(pointCount, MathUtil.SPHERE_THETA_COS.length); + for (int i = 0; i < preCompPoints; i++) { + double r = Math.sqrt(1 - y * y) * radius; + points.add(new Vector3d(r * MathUtil.SPHERE_THETA_COS[i], y * radius, r * MathUtil.SPHERE_THETA_SIN[i])); + y -= yStep; + if (y <= yLimit) { + return points; + } + } + if (pointCount > preCompPoints) { + for (int i = preCompPoints; i < pointCount; i++) { + double r = Math.sqrt(1 - y * y) * radius; + double theta = MathUtil.PHI * i; + points.add(new Vector3d(r * Math.cos(theta), y * radius, r * Math.sin(theta))); + y -= yStep; + if (y <= yLimit) { + return points; + } + } + } + return points; + } + + public static Set calculateCircle(double radius, double particleDensity, double cutoffAngle) { + Set points = new LinkedHashSet<>(); + double stepSize = particleDensity / radius; + for (double theta = 0; theta < cutoffAngle; theta += stepSize) { + points.add(new Vector3d(Math.cos(theta) * radius, 0, Math.sin(theta) * radius)); + } + return points; + } + + public static Set calculateDisc(double radius, double particleDensity, double cutoffAngle) { + Set points = new LinkedHashSet<>(); + for (double subRadius = particleDensity; subRadius < radius; subRadius += particleDensity) { + points.addAll(calculateCircle(subRadius, particleDensity, cutoffAngle)); + } + points.addAll(calculateCircle(radius, particleDensity, cutoffAngle)); + return points; + } + + public static Set calculateHelix(double radius, double height, double slope, int direction, double particleDensity) { + Set points = new LinkedHashSet<>(); + if (radius <= 0 || height <= 0) { + return points; + } + double loops = Math.abs(height / slope); + double length = slope * slope + radius * radius; + double stepSize = particleDensity / length; + for (double t = 0; t < loops; t += stepSize) { + double x = radius * Math.cos(direction * t); + double z = radius * Math.sin(direction * t); + points.add(new Vector3d(x, t * slope, z)); + } + return points; + } + + public static Set calculateLine(Vector3d start, Vector3d end, double particleDensity) { + Set points = new LinkedHashSet<>(); + Vector3d direction = new Vector3d(end).sub(start); + double length = direction.length(); + double step = length / Math.round(length / particleDensity); + direction.normalize().mul(step); + + for (double i = 0; i <= (length / step); i++) { + points.add(new Vector3d(start).add(new Vector3d(direction).mul(i))); + } + return points; + } + + public static Set calculateRegularPolygon(double radius, double angle, double particleDensity, boolean wireframe) { + angle = Math.max(angle, MathUtil.EPSILON); + + Set points = new LinkedHashSet<>(); + double apothem = radius * Math.cos(angle / 2); + double radiusStep = radius / Math.round(apothem / particleDensity); + if (wireframe) { + radiusStep = 2 * radius; + } else { + points.add(new Vector3d(0, 0, 0)); + } + for (double subRadius = radius; subRadius >= 0; subRadius -= radiusStep) { + Vector3d vertex = new Vector3d(subRadius, 0, 0); + for (double i = 0; i < 2 * Math.PI; i += angle) { + points.addAll(calculateLine( + VectorUtil.rotateAroundY(new Vector3d(vertex), i), + VectorUtil.rotateAroundY(new Vector3d(vertex), i + angle), + particleDensity)); + } + } + return points; + } + + public static Set calculateRegularPrism(double radius, double angle, double height, double particleDensity, boolean wireframe) { + Set points = new LinkedHashSet<>(); + Vector3d vertex = new Vector3d(radius, 0, 0); + for (double i = 0; i < 2 * Math.PI; i += angle) { + Vector3d currentVertex = VectorUtil.rotateAroundY(new Vector3d(vertex), i); + for (Vector3d vector : calculateLine(currentVertex, VectorUtil.rotateAroundY(new Vector3d(vertex), i + angle), particleDensity)) { + points.add(vector); + if (wireframe) { + points.add(new Vector3d(vector.x, height, vector.z)); + } else { + points.addAll(calculateLine(vector, new Vector3d(vector.x, height, vector.z), particleDensity)); + } + } + if (wireframe) + points.addAll(calculateLine(currentVertex, new Vector3d(currentVertex.x, height, currentVertex.z), particleDensity)); + } + return points; + } + + public static Set connectPoints(List points, double particleDensity) { + Set connectedPoints = new LinkedHashSet<>(); + for (int i = 0; i < points.size() - 1; i++) { + connectedPoints.addAll(calculateLine(points.get(i), points.get(i + 1), particleDensity)); + } + return connectedPoints; + } + + private static double ellipseCircumference(double r1, double r2) { + double a = Math.max(r1, r2); + double b = Math.min(r1, r2); + double h = Math.pow(a - b, 2) / Math.pow(a + b, 2); + return Math.PI * (a + b) * (1 + 3 * h / (10 + Math.sqrt(4 - 3 * h))); + } + + public static List calculateEllipse(double r1, double r2, double particleDensity, double cutoffAngle) { + List points = new ArrayList<>(); + double circumference = ellipseCircumference(r1, r2); + + int steps = (int) Math.round(circumference / particleDensity); + double theta = 0; + double angleStep = 0; + for (int i = 0; i < steps; i++) { + if (theta > cutoffAngle) { + break; + } + points.add(new Vector3d(r1 * Math.cos(theta), 0, r2 * Math.sin(theta))); + double dx = r1 * Math.sin(theta + 0.5 * angleStep); + double dy = r2 * Math.cos(theta + 0.5 * angleStep); + angleStep = particleDensity / Math.sqrt(dx * dx + dy * dy); + theta += angleStep; + } + return points; + } + + public static Set calculateEllipticalDisc(double r1, double r2, double particleDensity, double cutoffAngle) { + Set points = new LinkedHashSet<>(); + int steps = (int) Math.round(Math.max(r1, r2) / particleDensity); + double r; + for (double i = 1; i <= steps; i += 1) { + r = i / steps; + points.addAll(calculateEllipse(r1 * r, r2 * r, particleDensity, cutoffAngle)); + } + return points; + } + + public static Set calculateCylinder(double r1, double height, double particleDensity, double cutoffAngle) { + Set points = calculateDisc(r1, particleDensity, cutoffAngle); + points.addAll(points.stream().map(v -> new Vector3d(v.x, height, v.z)).collect(Collectors.toSet())); + Set wall = calculateCircle(r1, particleDensity, cutoffAngle); + points.addAll(fillVertically(wall, height, particleDensity)); + return points; + } + + public static Set calculateCylinder(double r1, double r2, double height, double particleDensity, double cutoffAngle) { + Set points = calculateEllipticalDisc(r1, r2, particleDensity, cutoffAngle); + points.addAll(points.stream().map(v -> new Vector3d(v.x, height, v.z)).collect(Collectors.toSet())); + Set wall = new LinkedHashSet<>(calculateEllipse(r1, r2, particleDensity, cutoffAngle)); + points.addAll(fillVertically(wall, height, particleDensity)); + return points; + } + + public static Set fillVertically(Set vectors, double height, double particleDensity) { + Set points = new LinkedHashSet<>(vectors); + double heightStep = height / Math.round(height / particleDensity); + for (double i = 0; i < height; i += heightStep) { + for (Vector3d vector : vectors) { + points.add(new Vector3d(vector.x, i, vector.z)); + } + } + return points; + } + + public static Set calculateHeart(double length, double width, double eccentricity, double particleDensity) { + Set points = new LinkedHashSet<>(); + double angleStep = 4 / 3.0 * particleDensity / (width + length); + for (double theta = 0; theta < Math.PI * 2; theta += angleStep) { + double x = width * Math.pow(Math.sin(theta), 3); + double y = length * (Math.cos(theta) - 1 / eccentricity * Math.cos(2 * theta) - 1.0 / 6 * Math.cos(3 * theta) - 1.0 / 16 * Math.cos(4 * theta)); + points.add(new Vector3d(x, 0, y)); + } + return points; + } + + public static Set calculateStar(double innerRadius, double outerRadius, double angle, double particleDensity) { + Set points = new LinkedHashSet<>(); + Vector3d outerVertex = new Vector3d(outerRadius, 0, 0); + Vector3d innerVertex = new Vector3d(innerRadius, 0, 0); + for (double theta = 0; theta < 2 * Math.PI; theta += angle) { + Vector3d currentVertex = VectorUtil.rotateAroundY(new Vector3d(outerVertex), theta); + points.addAll(calculateLine(currentVertex, VectorUtil.rotateAroundY(new Vector3d(innerVertex), theta + angle / 2), particleDensity)); + points.addAll(calculateLine(currentVertex, VectorUtil.rotateAroundY(new Vector3d(innerVertex), theta - angle / 2), particleDensity)); + } + return points; + } +} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/util/VectorUtil.java b/shapes-lib/src/main/java/com/sovdee/shapes/util/VectorUtil.java new file mode 100644 index 0000000..dc05b57 --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/util/VectorUtil.java @@ -0,0 +1,51 @@ +package com.sovdee.shapes.util; + +import org.joml.Quaterniond; +import org.joml.Vector3d; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Helper methods for JOML Vector3d operations that mirror Bukkit Vector convenience methods. + */ +public class VectorUtil { + + /** + * Rotates a vector around the Y axis by the given angle in radians. + * Modifies and returns the same vector. + * + * @param v the vector to rotate + * @param angle the angle in radians + * @return the rotated vector (same instance) + */ + public static Vector3d rotateAroundY(Vector3d v, double angle) { + double cos = Math.cos(angle); + double sin = Math.sin(angle); + double x = v.x * cos + v.z * sin; + double z = -v.x * sin + v.z * cos; + v.x = x; + v.z = z; + return v; + } + + /** + * Transforms a list of vectors using a quaternion, modifying them in place. + */ + public static List transform(Quaterniond quaternion, List vectors) { + vectors.replaceAll(v -> quaternion.transform(v)); + return vectors; + } + + /** + * Transforms a set of vectors using a quaternion, returning a new set. + */ + public static Set transform(Quaterniond quaternion, Set vectors) { + Set newVectors = new HashSet<>(); + for (Vector3d vector : vectors) { + newVectors.add(quaternion.transform(new Vector3d(vector))); + } + return newVectors; + } +} diff --git a/build.gradle b/skript-particle/build.gradle similarity index 66% rename from build.gradle rename to skript-particle/build.gradle index 5852bf1..4c7c539 100644 --- a/build.gradle +++ b/skript-particle/build.gradle @@ -2,10 +2,10 @@ import org.apache.tools.ant.filters.ReplaceTokens plugins { id 'java' + id 'com.gradleup.shadow' version '8.3.5' } group 'com.sovdee' -project.ext.jomlVersion = "1.10.5" configurations.configureEach { resolutionStrategy.cacheChangingModulesFor 1, 'minutes' @@ -22,17 +22,14 @@ repositories { } dependencies { + implementation project(':shapes-lib') compileOnly("io.papermc.paper:paper-api:1.21.11-R0.1-SNAPSHOT") compileOnly("com.github.SkriptLang:Skript:2.14.0-pre1") - implementation "org.joml:joml:${jomlVersion}" + compileOnly "org.joml:joml:${jomlVersion}" } processResources { filter ReplaceTokens, tokens: ["version": project.property("version")] - from("lang/") { - include '*.lang' - into 'lang/' - } } javadoc { @@ -45,8 +42,23 @@ java { toolchain.languageVersion.set(JavaLanguageVersion.of(21)) } +shadowJar { + archiveClassifier.set('') + configurations = [project.configurations.runtimeClasspath] + exclude 'org/joml/**' + exclude 'META-INF/maven/org.joml/**' +} + +tasks.named('jar') { + enabled = false +} + +tasks.named('build') { + dependsOn shadowJar +} + tasks.register('copyJar', Copy) { - dependsOn jar - from "build/libs/skript-particle.jar" + dependsOn shadowJar + from shadowJar.archiveFile into "e:/PaperServer/plugins" } diff --git a/src/main/java/com/sovdee/skriptparticles/Metrics.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/Metrics.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/Metrics.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/Metrics.java diff --git a/src/main/java/com/sovdee/skriptparticles/SkriptParticle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/SkriptParticle.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/SkriptParticle.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/SkriptParticle.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/effects/EffRotateShape.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffRotateShape.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/effects/EffRotateShape.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffRotateShape.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/effects/EffSetOrdering.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffSetOrdering.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/effects/EffSetOrdering.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffSetOrdering.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/effects/EffToggleAxes.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffToggleAxes.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/effects/EffToggleAxes.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffToggleAxes.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprDrawnShapes.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprDrawnShapes.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprDrawnShapes.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprDrawnShapes.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprLastCreatedParticle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprLastCreatedParticle.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprLastCreatedParticle.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprLastCreatedParticle.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprRotation.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprRotation.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprRotation.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprRotation.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprShapeCopy.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprShapeCopy.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprShapeCopy.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprShapeCopy.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprArc.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprArc.java similarity index 88% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprArc.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprArc.java index 5413e63..07acc16 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprArc.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprArc.java @@ -11,7 +11,7 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.Arc; +import com.sovdee.skriptparticles.shapes.DrawableShape; import com.sovdee.skriptparticles.shapes.Shape; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; @@ -30,10 +30,10 @@ "set {_shape} to a cylindrical sector of radius 1, height 0.5, and angle 45" }) @Since("1.0.0") -public class ExprArc extends SimpleExpression { +public class ExprArc extends SimpleExpression { static { - Skript.registerExpression(ExprArc.class, Arc.class, ExpressionType.COMBINED, + Skript.registerExpression(ExprArc.class, Shape.class, ExpressionType.COMBINED, "[a[n]] [circular] (arc|:sector) (with|of) radius %number% and [cutoff] angle [of] %number% [degrees|:radians]", "[a[n]] [cylindrical] (arc|:sector) (with|of) radius %number%(,| and) height %-number%[,] and [cutoff] angle [of] %number% [degrees|:radians]"); } @@ -79,7 +79,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override @Nullable - protected Arc[] get(Event event) { + protected Shape[] get(Event event) { Number radius = this.radius.getSingle(event); Number angle = this.angle.getSingle(event); Number height = (this.height != null) ? this.height.getSingle(event) : 0; @@ -93,11 +93,11 @@ protected Arc[] get(Event event) { height = Math.max(height.doubleValue(), 0); angle = MathUtil.clamp(angle.doubleValue(), 0, 2 * Math.PI); - Arc arc = new Arc(radius.doubleValue(), height.doubleValue(), angle.doubleValue()); + DrawableShape shape = new DrawableShape(new com.sovdee.shapes.Arc(radius.doubleValue(), height.doubleValue(), angle.doubleValue())); if (isSector) - arc.setStyle(Shape.Style.FILL); + shape.setStyle(Shape.Style.FILL); - return new Arc[]{arc}; + return new Shape[]{shape}; } @Override @@ -106,8 +106,8 @@ public boolean isSingle() { } @Override - public Class getReturnType() { - return Arc.class; + public Class getReturnType() { + return Shape.class; } @Override diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprBezierCurve.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprBezierCurve.java similarity index 81% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprBezierCurve.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprBezierCurve.java index 2129dcd..698474f 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprBezierCurve.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprBezierCurve.java @@ -10,7 +10,9 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.BezierCurve; +import com.sovdee.skriptparticles.shapes.DrawableShape; +import com.sovdee.skriptparticles.shapes.DynamicBezierCurve; +import com.sovdee.skriptparticles.shapes.Shape; import com.sovdee.skriptparticles.util.Point; import org.bukkit.event.Event; import org.checkerframework.checker.nullness.qual.NonNull; @@ -28,10 +30,10 @@ "set {_shape} to a curve from player to player's target with control point (location 3 above player)" }) @Since("1.3.0") -public class ExprBezierCurve extends SimpleExpression { +public class ExprBezierCurve extends SimpleExpression { static { - Skript.registerExpression(ExprBezierCurve.class, BezierCurve.class, ExpressionType.COMBINED, + Skript.registerExpression(ExprBezierCurve.class, Shape.class, ExpressionType.COMBINED, "[a] [bezier] curve from [start] %vector/entity/location% to [end] %vector/entity/location% (with|using) control point[s] %vectors/entities/locations%"); } @@ -48,7 +50,7 @@ public boolean init(Expression[] exprs, int matchedPattern, @NonNull Kleenean } @Override - protected BezierCurve @Nullable [] get(@NonNull Event event) { + protected Shape @Nullable [] get(@NonNull Event event) { @Nullable Point start = Point.of(this.start.getSingle(event)); @Nullable Point end = Point.of(this.end.getSingle(event)); if (start == null || end == null) @@ -58,7 +60,7 @@ public boolean init(Expression[] exprs, int matchedPattern, @NonNull Kleenean controlPoints.add(Point.of(value)); } - return new BezierCurve[]{new BezierCurve(start, end, controlPoints)}; + return new Shape[]{new DrawableShape(new DynamicBezierCurve(start, end, controlPoints))}; } @Override @@ -68,8 +70,8 @@ public boolean isSingle() { @Override @NonNull - public Class getReturnType() { - return BezierCurve.class; + public Class getReturnType() { + return Shape.class; } @Override diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCircle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCircle.java similarity index 87% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCircle.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCircle.java index 5d19fd3..23e1a8b 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCircle.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCircle.java @@ -11,7 +11,7 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.Circle; +import com.sovdee.skriptparticles.shapes.DrawableShape; import com.sovdee.skriptparticles.shapes.Shape; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; @@ -28,10 +28,10 @@ "set {_shape} to a solid cylinder with radius 3 and height 5" }) @Since("1.0.0") -public class ExprCircle extends SimpleExpression { +public class ExprCircle extends SimpleExpression { static { - Skript.registerExpression(ExprCircle.class, Circle.class, ExpressionType.COMBINED, + Skript.registerExpression(ExprCircle.class, Shape.class, ExpressionType.COMBINED, "[a] (circle|:disc) (with|of) radius %number%", "[a] [hollow|2:solid] (cylinder|1:tube) (with|of) radius %number% and height %number%"); } @@ -73,7 +73,7 @@ public boolean init(Expression[] exprs, int matchedPattern, @NonNull Kleenean @Override @Nullable - protected Circle[] get(@NonNull Event event) { + protected Shape[] get(@NonNull Event event) { Number radius = this.radius.getSingle(event); Number height = this.height != null ? this.height.getSingle(event) : 0; if (radius == null || height == null) @@ -82,9 +82,9 @@ protected Circle[] get(@NonNull Event event) { radius = Math.max(radius.doubleValue(), MathUtil.EPSILON); height = Math.max(height.doubleValue(), 0); - Circle circle = new Circle(radius.doubleValue(), height.doubleValue()); - circle.setStyle(style); - return new Circle[]{circle}; + DrawableShape shape = new DrawableShape(new com.sovdee.shapes.Circle(radius.doubleValue(), height.doubleValue())); + shape.setStyle(style); + return new Shape[]{shape}; } @Override @@ -94,8 +94,8 @@ public boolean isSingle() { @Override @NonNull - public Class getReturnType() { - return Circle.class; + public Class getReturnType() { + return Shape.class; } @Override diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCuboid.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCuboid.java similarity index 83% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCuboid.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCuboid.java index 120e56d..095cf1c 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCuboid.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCuboid.java @@ -10,10 +10,13 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.Cuboid; +import com.sovdee.skriptparticles.shapes.DrawableShape; +import com.sovdee.skriptparticles.shapes.DynamicCuboid; +import com.sovdee.skriptparticles.shapes.Shape; import com.sovdee.skriptparticles.shapes.Shape.Style; import com.sovdee.skriptparticles.util.DynamicLocation; import com.sovdee.skriptparticles.util.MathUtil; +import com.sovdee.skriptparticles.util.VectorConversion; import org.bukkit.event.Event; import org.bukkit.util.Vector; import org.jetbrains.annotations.Nullable; @@ -32,10 +35,10 @@ "draw the shape of a cuboid from player to player's target" }) @Since("1.0.0") -public class ExprCuboid extends SimpleExpression { +public class ExprCuboid extends SimpleExpression { static { - Skript.registerExpression(ExprCuboid.class, Cuboid.class, ExpressionType.COMBINED, + Skript.registerExpression(ExprCuboid.class, Shape.class, ExpressionType.COMBINED, "[a] [:hollow|:solid] cuboid (with|of) length %number%(,| and) width %number%[,] and height %number%", "[a] [:hollow|:solid] cuboid (from|between) %location/entity/vector% (to|and) %location/entity/vector%"); @@ -76,8 +79,8 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override @Nullable - protected Cuboid[] get(Event event) { - Cuboid cuboid; + protected Shape[] get(Event event) { + DrawableShape shape; // from width, length, height if (matchedPattern == 0) { if (width == null || length == null || height == null) return null; @@ -88,7 +91,7 @@ protected Cuboid[] get(Event event) { width = Math.max(width.doubleValue(), MathUtil.EPSILON); length = Math.max(length.doubleValue(), MathUtil.EPSILON); height = Math.max(height.doubleValue(), MathUtil.EPSILON); - cuboid = new Cuboid(width.doubleValue(), length.doubleValue(), height.doubleValue()); + shape = new DrawableShape(new com.sovdee.shapes.Cuboid(length.doubleValue(), width.doubleValue(), height.doubleValue())); // from location/entity/vector to location/entity/vector } else { if (corner1 == null || corner2 == null) return null; @@ -98,22 +101,19 @@ protected Cuboid[] get(Event event) { // vector check if (corner1 instanceof Vector && corner2 instanceof Vector) { - // if both are vectors, create a static cuboid - cuboid = new Cuboid((Vector) corner1, (Vector) corner2); + shape = new DrawableShape(new com.sovdee.shapes.Cuboid(VectorConversion.toJOML((Vector) corner1), VectorConversion.toJOML((Vector) corner2))); } else if (corner1 instanceof Vector || corner2 instanceof Vector) { - // if one is a vector, return empty array return null; } else { - // if neither are vectors, create a dynamic cuboid corner1 = DynamicLocation.fromLocationEntity(corner1); corner2 = DynamicLocation.fromLocationEntity(corner2); if (corner1 == null || corner2 == null) return null; - cuboid = new Cuboid((DynamicLocation) corner1, (DynamicLocation) corner2); + shape = new DrawableShape(new DynamicCuboid((DynamicLocation) corner1, (DynamicLocation) corner2)); } } - cuboid.setStyle(style); - return new Cuboid[]{cuboid}; + shape.setStyle(style); + return new Shape[]{shape}; } @Override @@ -122,8 +122,8 @@ public boolean isSingle() { } @Override - public Class getReturnType() { - return Cuboid.class; + public Class getReturnType() { + return Shape.class; } @Override diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipse.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipse.java similarity index 89% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipse.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipse.java index 620734e..f304e1f 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipse.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipse.java @@ -11,7 +11,7 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.Ellipse; +import com.sovdee.skriptparticles.shapes.DrawableShape; import com.sovdee.skriptparticles.shapes.Shape; import com.sovdee.skriptparticles.shapes.Shape.Style; import com.sovdee.skriptparticles.util.MathUtil; @@ -31,10 +31,10 @@ "set {_shape} to a hollow elliptical cylinder with radii 3 and 6 and height 5" }) @Since("1.0.0") -public class ExprEllipse extends SimpleExpression { +public class ExprEllipse extends SimpleExpression { static { - Skript.registerExpression(ExprEllipse.class, Ellipse.class, ExpressionType.COMBINED, + Skript.registerExpression(ExprEllipse.class, Shape.class, ExpressionType.COMBINED, "[a[n]] [surface:(solid|filled)] (ellipse|oval) (with|of) radi(i|us) %number% and %number%", "[a[n]] [hollow|2:solid] elliptical (cylinder|1:tube) (with|of) radi(i|us) %number%(,| and) %number%[,] and height %number%"); } @@ -84,7 +84,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override @Nullable - protected Ellipse[] get(Event event) { + protected Shape[] get(Event event) { Number xRadius = this.xRadius.getSingle(event); Number zRadius = this.zRadius.getSingle(event); Number height = this.height == null ? 0 : this.height.getSingle(event); @@ -95,9 +95,9 @@ protected Ellipse[] get(Event event) { zRadius = Math.max(zRadius.doubleValue(), MathUtil.EPSILON); height = Math.max(height.doubleValue(), 0); - Ellipse ellipse = new Ellipse(xRadius.doubleValue(), zRadius.doubleValue(), height.doubleValue()); - ellipse.setStyle(style); - return new Ellipse[]{ellipse}; + DrawableShape shape = new DrawableShape(new com.sovdee.shapes.Ellipse(xRadius.doubleValue(), zRadius.doubleValue(), height.doubleValue())); + shape.setStyle(style); + return new Shape[]{shape}; } @Override @@ -106,8 +106,8 @@ public boolean isSingle() { } @Override - public Class getReturnType() { - return Ellipse.class; + public Class getReturnType() { + return Shape.class; } @Override diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipsoid.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipsoid.java similarity index 87% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipsoid.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipsoid.java index 599ffdc..c3da3d9 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipsoid.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipsoid.java @@ -11,7 +11,8 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.Ellipsoid; +import com.sovdee.skriptparticles.shapes.DrawableShape; +import com.sovdee.skriptparticles.shapes.Shape; import com.sovdee.skriptparticles.shapes.Shape.Style; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; @@ -32,10 +33,10 @@ "set {_shape} to a hollow ellipsoid with radii 3, 6 and 5" }) @Since("1.0.0") -public class ExprEllipsoid extends SimpleExpression { +public class ExprEllipsoid extends SimpleExpression { static { - Skript.registerExpression(ExprEllipsoid.class, Ellipsoid.class, ExpressionType.COMBINED, + Skript.registerExpression(ExprEllipsoid.class, Shape.class, ExpressionType.COMBINED, "[a[n]] [(outlined|wireframe|:hollow|fill:(solid|filled))] ellipsoid (with|of) radi(i|us) %number%(,| and) %number%[,] and %number%"); } @@ -74,7 +75,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override @Nullable - protected Ellipsoid[] get(Event event) { + protected Shape[] get(Event event) { Number xRadius = this.xRadius.getSingle(event); Number yRadius = this.yRadius.getSingle(event); Number zRadius = this.zRadius.getSingle(event); @@ -85,9 +86,9 @@ protected Ellipsoid[] get(Event event) { yRadius = Math.max(yRadius.doubleValue(), MathUtil.EPSILON); zRadius = Math.max(zRadius.doubleValue(), MathUtil.EPSILON); - Ellipsoid ellipsoid = new Ellipsoid(xRadius.doubleValue(), yRadius.doubleValue(), zRadius.doubleValue()); - ellipsoid.setStyle(style); - return new Ellipsoid[]{ellipsoid}; + DrawableShape shape = new DrawableShape(new com.sovdee.shapes.Ellipsoid(xRadius.doubleValue(), yRadius.doubleValue(), zRadius.doubleValue())); + shape.setStyle(style); + return new Shape[]{shape}; } @Override @@ -96,8 +97,8 @@ public boolean isSingle() { } @Override - public Class getReturnType() { - return Ellipsoid.class; + public Class getReturnType() { + return Shape.class; } @Override diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipticalArc.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipticalArc.java similarity index 89% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipticalArc.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipticalArc.java index a3de25f..3cfc14e 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipticalArc.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipticalArc.java @@ -11,7 +11,8 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.EllipticalArc; +import com.sovdee.skriptparticles.shapes.DrawableShape; +import com.sovdee.skriptparticles.shapes.Shape; import com.sovdee.skriptparticles.shapes.Shape.Style; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; @@ -30,10 +31,10 @@ "set {_shape} to a elliptical cylinder with radii 3 and 5, height 10, and cutoff angle of 3.1415 radians" }) @Since("1.0.0") -public class ExprEllipticalArc extends SimpleExpression { +public class ExprEllipticalArc extends SimpleExpression { static { - Skript.registerExpression(ExprEllipticalArc.class, EllipticalArc.class, ExpressionType.COMBINED, + Skript.registerExpression(ExprEllipticalArc.class, Shape.class, ExpressionType.COMBINED, "[an] elliptical (arc|:sector) (with|of) radi(i|us) %number%(,| and) %number%[,] and [cutoff] angle [of] %number% [degrees|:radians]", "[an] elliptical [cylindrical] (arc|:sector) (with|of) radi(i|us) %number%(,| and) %number%(,| and) height %-number%[,] and [cutoff] angle [of] %number% [degrees|:radians]"); } @@ -91,7 +92,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override @Nullable - protected EllipticalArc[] get(Event event) { + protected Shape[] get(Event event) { Number xRadius = this.xRadius.getSingle(event); Number zRadius = this.zRadius.getSingle(event); Number height = this.height == null ? 0 : this.height.getSingle(event); @@ -106,9 +107,9 @@ protected EllipticalArc[] get(Event event) { angle = Math.toRadians(angle.doubleValue()); angle = MathUtil.clamp(angle.doubleValue(), 0, Math.PI * 2); - EllipticalArc arc = new EllipticalArc(xRadius.doubleValue(), zRadius.doubleValue(), height.doubleValue(), angle.doubleValue()); - arc.setStyle(style); - return new EllipticalArc[]{arc}; + DrawableShape shape = new DrawableShape(new com.sovdee.shapes.EllipticalArc(xRadius.doubleValue(), zRadius.doubleValue(), height.doubleValue(), angle.doubleValue())); + shape.setStyle(style); + return new Shape[]{shape}; } @Override @@ -117,8 +118,8 @@ public boolean isSingle() { } @Override - public Class getReturnType() { - return EllipticalArc.class; + public Class getReturnType() { + return Shape.class; } @Override diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHeart.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHeart.java similarity index 88% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHeart.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHeart.java index e387a5c..7bc0781 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHeart.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHeart.java @@ -11,7 +11,7 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.Heart; +import com.sovdee.skriptparticles.shapes.DrawableShape; import com.sovdee.skriptparticles.shapes.Shape; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; @@ -30,10 +30,10 @@ "draw the shape of a heart of width 2 and length 2 at player" }) @Since("1.0.1") -public class ExprHeart extends SimpleExpression { +public class ExprHeart extends SimpleExpression { static { - Skript.registerExpression(ExprHeart.class, Heart.class, ExpressionType.COMBINED, "[a] [:solid] heart [shape] (with|of) width %number%[,] [and] length %number%[[,] [and] eccentricity %-number%]"); + Skript.registerExpression(ExprHeart.class, Shape.class, ExpressionType.COMBINED, "[a] [:solid] heart [shape] (with|of) width %number%[,] [and] length %number%[[,] [and] eccentricity %-number%]"); } private Expression width; @@ -71,7 +71,7 @@ public boolean init(Expression[] expressions, int i, Kleenean kleenean, Parse @Override @Nullable - protected Heart[] get(Event event) { + protected Shape[] get(Event event) { Number width = this.width.getSingle(event); Number length = this.length.getSingle(event); Number eccentricity = this.eccentricity == null ? 3 : this.eccentricity.getSingle(event); @@ -82,11 +82,11 @@ protected Heart[] get(Event event) { length = Math.max(length.doubleValue(), MathUtil.EPSILON); eccentricity = Math.max(eccentricity.doubleValue(), 1); - Heart heart = new Heart(width.doubleValue(), length.doubleValue(), eccentricity.doubleValue()); + DrawableShape shape = new DrawableShape(new com.sovdee.shapes.Heart(width.doubleValue(), length.doubleValue(), eccentricity.doubleValue())); if (isSolid) { - heart.setStyle(Shape.Style.SURFACE); + shape.setStyle(Shape.Style.SURFACE); } - return new Heart[]{heart}; + return new Shape[]{shape}; } @Override @@ -95,8 +95,8 @@ public boolean isSingle() { } @Override - public Class getReturnType() { - return Heart.class; + public Class getReturnType() { + return Shape.class; } @Override diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHelix.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHelix.java similarity index 87% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHelix.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHelix.java index 87ecbef..43d26a2 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHelix.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHelix.java @@ -11,7 +11,8 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.Helix; +import com.sovdee.skriptparticles.shapes.DrawableShape; +import com.sovdee.skriptparticles.shapes.Shape; import com.sovdee.skriptparticles.shapes.Shape.Style; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; @@ -29,10 +30,10 @@ "set {_shape} to a solid anti-clockwise helix with radius 3, height 5, winding rate 1" }) @Since("1.0.0") -public class ExprHelix extends SimpleExpression { +public class ExprHelix extends SimpleExpression { static { - Skript.registerExpression(ExprHelix.class, Helix.class, ExpressionType.COMBINED, + Skript.registerExpression(ExprHelix.class, Shape.class, ExpressionType.COMBINED, "[a[n]] [:solid] [[1:(counter|anti)[-]]clockwise] (helix|spiral) (with|of) radius %number%[,] [and] height %number%[[,] [and] winding rate [of] %-number% [loops per (meter|block)]]"); } @@ -75,20 +76,20 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override @Nullable - protected Helix[] get(Event event) { + protected Shape[] get(Event event) { @Nullable Number radius = this.radius.getSingle(event); @Nullable Number height = this.height.getSingle(event); @Nullable Number windingRate = this.windingRate == null ? 1 : this.windingRate.getSingle(event); if (radius == null || height == null || windingRate == null) - return new Helix[0]; + return new Shape[0]; radius = Math.max(radius.doubleValue(), MathUtil.EPSILON); height = Math.max(height.doubleValue(), MathUtil.EPSILON); double slope = 1.0 / Math.max(windingRate.doubleValue(), MathUtil.EPSILON); int direction = isClockwise ? 1 : -1; - Helix helix = new Helix(radius.doubleValue(), height.doubleValue(), slope / (2 * Math.PI), direction); - helix.setStyle(style); - return new Helix[]{helix}; + DrawableShape shape = new DrawableShape(new com.sovdee.shapes.Helix(radius.doubleValue(), height.doubleValue(), slope / (2 * Math.PI), direction)); + shape.setStyle(style); + return new Shape[]{shape}; } @Override @@ -97,8 +98,8 @@ public boolean isSingle() { } @Override - public Class getReturnType() { - return Helix.class; + public Class getReturnType() { + return Shape.class; } @Override diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprIrregularPolygon.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprIrregularPolygon.java similarity index 73% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprIrregularPolygon.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprIrregularPolygon.java index c120a73..670cc79 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprIrregularPolygon.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprIrregularPolygon.java @@ -11,12 +11,15 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.IrregularPolygon; +import com.sovdee.skriptparticles.shapes.DrawableShape; +import com.sovdee.skriptparticles.shapes.Shape; import com.sovdee.skriptparticles.util.DynamicLocation; +import com.sovdee.skriptparticles.util.VectorConversion; import org.bukkit.Location; import org.bukkit.event.Event; import org.bukkit.util.Vector; import org.jetbrains.annotations.Nullable; +import org.joml.Vector3d; import java.util.ArrayList; import java.util.List; @@ -33,10 +36,10 @@ "set {_shape} to a 2d polygon from points vector(0,0,1), vector(1,0,1), vector(0,0,-1) and height 0.5" }) @Since("1.0.0") -public class ExprIrregularPolygon extends SimpleExpression { +public class ExprIrregularPolygon extends SimpleExpression { static { - Skript.registerExpression(ExprIrregularPolygon.class, IrregularPolygon.class, ExpressionType.COMBINED, + Skript.registerExpression(ExprIrregularPolygon.class, Shape.class, ExpressionType.COMBINED, "[a] [2d] polygon (from|with) [vertices|points] %vectors/locations% [and height %-number%]"); } @@ -61,31 +64,33 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override @Nullable - protected IrregularPolygon[] get(Event event) { + protected Shape[] get(Event event) { Object[] points = this.points.getArray(event); - List vertices = new ArrayList<>(points.length); + List vertices = new ArrayList<>(points.length); Vector locationOffset = null; for (Object point : points) { if (point instanceof Vector) { - vertices.add((Vector) point); + vertices.add(VectorConversion.toJOML((Vector) point)); } else if (point instanceof Location) { if (locationOffset == null) { locationOffset = ((Location) point).toVector(); } - vertices.add(((Location) point).toVector().subtract(locationOffset)); + Vector relative = ((Location) point).toVector().subtract(locationOffset); + vertices.add(VectorConversion.toJOML(relative)); } } Number height = this.height != null ? this.height.getSingle(event) : null; - IrregularPolygon polygon; + com.sovdee.shapes.IrregularPolygon libPolygon; if (height != null) { - polygon = new IrregularPolygon(vertices, height.doubleValue()); + libPolygon = new com.sovdee.shapes.IrregularPolygon(vertices, height.doubleValue()); } else { - polygon = new IrregularPolygon(vertices); + libPolygon = new com.sovdee.shapes.IrregularPolygon(vertices); } + DrawableShape shape = new DrawableShape(libPolygon); if (locationOffset != null) { - polygon.setLocation(new DynamicLocation((Location) points[0])); + shape.setLocation(new DynamicLocation((Location) points[0])); } - return new IrregularPolygon[]{polygon}; + return new Shape[]{shape}; } @Override @@ -94,8 +99,8 @@ public boolean isSingle() { } @Override - public Class getReturnType() { - return IrregularPolygon.class; + public Class getReturnType() { + return Shape.class; } @Override diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprLine.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprLine.java similarity index 84% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprLine.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprLine.java index 5c97264..9764cf7 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprLine.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprLine.java @@ -11,9 +11,12 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.Line; +import com.sovdee.skriptparticles.shapes.DrawableShape; +import com.sovdee.skriptparticles.shapes.DynamicLine; +import com.sovdee.skriptparticles.shapes.Shape; import com.sovdee.skriptparticles.util.DynamicLocation; import com.sovdee.skriptparticles.util.MathUtil; +import com.sovdee.skriptparticles.util.VectorConversion; import org.bukkit.event.Event; import org.bukkit.util.Vector; import org.jetbrains.annotations.Nullable; @@ -42,10 +45,10 @@ "draw the shape of a line connecting {_locations::*}" }) @Since("1.0.0") -public class ExprLine extends SimpleExpression { +public class ExprLine extends SimpleExpression { static { - Skript.registerExpression(ExprLine.class, Line.class, ExpressionType.COMBINED, + Skript.registerExpression(ExprLine.class, Shape.class, ExpressionType.COMBINED, "[a] line[s] (from|between) %locations/entities/vectors% (to|and) %locations/entities/vectors%", "[a] line (in [the]|from) direction %vector% [(and|[and] with) length %number%]", "[a] line (between|connecting) %locations/entities/vectors%"); @@ -87,28 +90,26 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override @Nullable - protected Line[] get(Event event) { - List lines = new ArrayList<>(); + protected Shape[] get(Event event) { + List lines = new ArrayList<>(); switch (matchedPattern) { case 0 -> { Object[] start = this.start.getArray(event); Object[] end = this.end.getArray(event); - // if both are vectors, create a line from them for (Object startObject : start) { for (Object endObject : end) { if (startObject instanceof Vector startVector && endObject instanceof Vector endVector) { - lines.add(new Line(startVector, endVector)); + lines.add(new DrawableShape(new com.sovdee.shapes.Line(VectorConversion.toJOML(startVector), VectorConversion.toJOML(endVector)))); continue; } else if (startObject instanceof Vector || endObject instanceof Vector) { continue; } - // if neither are vectors, create a dynamic line DynamicLocation startPoint = DynamicLocation.fromLocationEntity(startObject); DynamicLocation endPoint = DynamicLocation.fromLocationEntity(endObject); if (endPoint == null || startPoint == null) { continue; } - lines.add(new Line(startPoint, endPoint)); + lines.add(new DrawableShape(new DynamicLine(startPoint, endPoint))); } } } @@ -121,13 +122,13 @@ protected Line[] get(Event event) { if (length != null) v.multiply(Math.max(length.doubleValue(), MathUtil.EPSILON)); - lines.add(new Line(v)); + lines.add(new DrawableShape(new com.sovdee.shapes.Line(VectorConversion.toJOML(v)))); } case 2 -> { Object[] points = this.points.getArray(event); if (points instanceof Vector[] vectors) { for (int i = 0; i < vectors.length - 1; i++) { - lines.add(new Line(vectors[i], vectors[i + 1])); + lines.add(new DrawableShape(new com.sovdee.shapes.Line(VectorConversion.toJOML(vectors[i]), VectorConversion.toJOML(vectors[i + 1])))); } } else { List locations = new ArrayList<>(); @@ -140,7 +141,7 @@ protected Line[] get(Event event) { locations.add(location); } for (int i = 0; i < locations.size() - 1; i++) { - lines.add(new Line(locations.get(i), locations.get(i + 1))); + lines.add(new DrawableShape(new DynamicLine(locations.get(i), locations.get(i + 1)))); } } } @@ -148,7 +149,7 @@ protected Line[] get(Event event) { return null; } } - return lines.toArray(new Line[0]); + return lines.toArray(new Shape[0]); } @Override @@ -161,8 +162,8 @@ public boolean isSingle() { } @Override - public Class getReturnType() { - return Line.class; + public Class getReturnType() { + return Shape.class; } @Override diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRectangle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRectangle.java similarity index 81% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRectangle.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRectangle.java index 025741a..b236b23 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRectangle.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRectangle.java @@ -10,10 +10,13 @@ import ch.njol.skript.lang.SkriptParser; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.Rectangle; +import com.sovdee.shapes.Rectangle.Plane; +import com.sovdee.skriptparticles.shapes.DrawableShape; +import com.sovdee.skriptparticles.shapes.DynamicRectangle; import com.sovdee.skriptparticles.shapes.Shape; import com.sovdee.skriptparticles.util.DynamicLocation; import com.sovdee.skriptparticles.util.MathUtil; +import com.sovdee.skriptparticles.util.VectorConversion; import org.bukkit.event.Event; import org.bukkit.util.Vector; import org.jetbrains.annotations.Nullable; @@ -36,10 +39,10 @@ "draw the shape of a rectangle from player to player's target" }) @Since("1.0.0") -public class ExprRectangle extends SimpleExpression { +public class ExprRectangle extends SimpleExpression { static { - Skript.registerExpression(ExprRectangle.class, Rectangle.class, ExpressionType.COMBINED, + Skript.registerExpression(ExprRectangle.class, Shape.class, ExpressionType.COMBINED, "[a[n]] [solid:(solid|filled)] [:xz|:xy|:yz] rectangle (with|of) length %number% and width %number%", "[a[n]] [solid:(solid|filled)] [:xz|:xy|:yz] rectangle (from|with corners [at]) %location/entity/vector% (to|and) %location/entity/vector%" ); @@ -51,7 +54,7 @@ public class ExprRectangle extends SimpleExpression { private Expression corner1Expr; private Expression corner2Expr; private int matchedPattern; - private Rectangle.Plane plane; + private Plane plane; @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { @@ -64,15 +67,15 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye corner1Expr = exprs[0]; corner2Expr = exprs[1]; } - plane = Rectangle.Plane.XZ; - if (parseResult.hasTag("xy")) plane = Rectangle.Plane.XY; - else if (parseResult.hasTag("yz")) plane = Rectangle.Plane.YZ; + plane = Plane.XZ; + if (parseResult.hasTag("xy")) plane = Plane.XY; + else if (parseResult.hasTag("yz")) plane = Plane.YZ; return true; } @Override - protected @Nullable Rectangle[] get(Event event) { - Rectangle rectangle; + protected @Nullable Shape[] get(Event event) { + DrawableShape shape; if (matchedPattern == 0) { if (lengthExpr == null || widthExpr == null) return null; Number length = lengthExpr.getSingle(event); @@ -80,31 +83,27 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye if (length == null || width == null) return null; length = Math.max(length.doubleValue(), MathUtil.EPSILON); width = Math.max(width.doubleValue(), MathUtil.EPSILON); - rectangle = new Rectangle(length.doubleValue(), width.doubleValue(), plane); + shape = new DrawableShape(new com.sovdee.shapes.Rectangle(length.doubleValue(), width.doubleValue(), plane)); } else { if (corner1Expr == null || corner2Expr == null) return null; Object corner1 = corner1Expr.getSingle(event); Object corner2 = corner2Expr.getSingle(event); if (corner1 == null || corner2 == null) return null; - // vector check if (corner1 instanceof Vector && corner2 instanceof Vector) { - // if both are vectors, create a static rectangle - rectangle = new Rectangle((Vector) corner1, (Vector) corner2, plane); + shape = new DrawableShape(new com.sovdee.shapes.Rectangle(VectorConversion.toJOML((Vector) corner1), VectorConversion.toJOML((Vector) corner2), plane)); } else if (corner1 instanceof Vector || corner2 instanceof Vector) { - // if only one is a vector, return empty array return null; } else { - // if neither are vectors, create a dynamic rectangle corner1 = DynamicLocation.fromLocationEntity(corner1); corner2 = DynamicLocation.fromLocationEntity(corner2); if (corner1 == null || corner2 == null) return null; - rectangle = new Rectangle((DynamicLocation) corner1, (DynamicLocation) corner2, plane); + shape = new DrawableShape(new DynamicRectangle((DynamicLocation) corner1, (DynamicLocation) corner2, plane)); } } - rectangle.setStyle(style); - return new Rectangle[]{rectangle}; + shape.setStyle(style); + return new Shape[]{shape}; } @Override @@ -113,8 +112,8 @@ public boolean isSingle() { } @Override - public Class getReturnType() { - return Rectangle.class; + public Class getReturnType() { + return Shape.class; } @Override diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolygon.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolygon.java similarity index 89% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolygon.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolygon.java index 720a709..f6d3808 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolygon.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolygon.java @@ -11,7 +11,8 @@ import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.skript.lang.util.SimpleLiteral; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.RegularPolygon; +import com.sovdee.skriptparticles.shapes.DrawableShape; +import com.sovdee.skriptparticles.shapes.Shape; import com.sovdee.skriptparticles.shapes.Shape.Style; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; @@ -27,12 +28,12 @@ "set {_shape} to a solid regular polygon with 6 sides and side length 3", "draw the shape of a triangle with side length 5 at player" }) -public class ExprRegularPolygon extends SimpleExpression { +public class ExprRegularPolygon extends SimpleExpression { // TODO: add ExprRegularPrism static { - Skript.registerExpression(ExprRegularPolygon.class, RegularPolygon.class, ExpressionType.COMBINED, + Skript.registerExpression(ExprRegularPolygon.class, Shape.class, ExpressionType.COMBINED, "[a] [solid:(solid|filled)] regular polygon with %number% sides and radius %number%", "[a] [solid:(solid|filled)] regular polygon with %number% sides and side length %number%", "[a[n]] [solid:(solid|filled)] (3:[equilateral ]triangle|4:square|5:pentagon|6:hexagon|7:heptagon|8:octagon) with radius %number%", @@ -89,7 +90,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override @Nullable - protected RegularPolygon[] get(Event event) { + protected Shape[] get(Event event) { Number sides = this.sides.getSingle(event); if (sides == null) return null; @@ -109,9 +110,9 @@ protected RegularPolygon[] get(Event event) { sides = Math.max(sides.intValue(), 3); radius = Math.max(radius.doubleValue(), MathUtil.EPSILON); - RegularPolygon polygon = new RegularPolygon(sides.intValue(), radius.doubleValue()); - polygon.setStyle(style); - return new RegularPolygon[]{polygon}; + DrawableShape shape = new DrawableShape(new com.sovdee.shapes.RegularPolygon(sides.intValue(), radius.doubleValue())); + shape.setStyle(style); + return new Shape[]{shape}; } @Override @@ -120,8 +121,8 @@ public boolean isSingle() { } @Override - public Class getReturnType() { - return RegularPolygon.class; + public Class getReturnType() { + return Shape.class; } @Override diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolyhedron.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolyhedron.java similarity index 75% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolyhedron.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolyhedron.java index 59b57af..d3b986a 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolyhedron.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolyhedron.java @@ -10,7 +10,7 @@ import ch.njol.skript.lang.SkriptParser; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.RegularPolyhedron; +import com.sovdee.skriptparticles.shapes.DrawableShape; import com.sovdee.skriptparticles.shapes.Shape; import com.sovdee.skriptparticles.shapes.Shape.Style; import org.bukkit.event.Event; @@ -28,10 +28,10 @@ "set {_shape} to a solid icosahedron with radius 2", "draw the shape of a tetrahedron with radius 5 at player" }) -public class ExprRegularPolyhedron extends SimpleExpression { +public class ExprRegularPolyhedron extends SimpleExpression { static { - Skript.registerExpression(ExprRegularPolyhedron.class, RegularPolyhedron.class, ExpressionType.COMBINED, "[a[n]] [outlined|:hollow|:solid] (:tetra|:octa|:icosa|:dodeca)hedron (with|of) radius %number%"); + Skript.registerExpression(ExprRegularPolyhedron.class, Shape.class, ExpressionType.COMBINED, "[a[n]] [outlined|:hollow|:solid] (:tetra|:octa|:icosa|:dodeca)hedron (with|of) radius %number%"); } private Expression radius; @@ -54,12 +54,12 @@ public boolean init(Expression[] expressions, int i, Kleenean kleenean, Skrip } @Override - protected @Nullable RegularPolyhedron[] get(Event event) { + protected @Nullable Shape[] get(Event event) { if (radius.getSingle(event) == null) - return new RegularPolyhedron[0]; - RegularPolyhedron regularPolyhedron = new RegularPolyhedron(radius.getSingle(event).doubleValue(), faces); - regularPolyhedron.setStyle(style); - return new RegularPolyhedron[]{regularPolyhedron}; + return new Shape[0]; + DrawableShape shape = new DrawableShape(new com.sovdee.shapes.RegularPolyhedron(radius.getSingle(event).doubleValue(), faces)); + shape.setStyle(style); + return new Shape[]{shape}; } @Override @@ -68,8 +68,8 @@ public boolean isSingle() { } @Override - public Class getReturnType() { - return RegularPolyhedron.class; + public Class getReturnType() { + return Shape.class; } @Override diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphere.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphere.java similarity index 79% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphere.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphere.java index c6d601b..aeedef8 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphere.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphere.java @@ -11,8 +11,8 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; +import com.sovdee.skriptparticles.shapes.DrawableShape; import com.sovdee.skriptparticles.shapes.Shape; -import com.sovdee.skriptparticles.shapes.Sphere; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; import org.jetbrains.annotations.NotNull; @@ -28,10 +28,10 @@ "set {_shape} to solid sphere with radius 10" }) @Since("1.0.0") -public class ExprSphere extends SimpleExpression { +public class ExprSphere extends SimpleExpression { static { - Skript.registerExpression(ExprSphere.class, Sphere.class, ExpressionType.COMBINED, "[a] [:solid] sphere (with|of) radius %number%"); + Skript.registerExpression(ExprSphere.class, Shape.class, ExpressionType.COMBINED, "[a] [:solid] sphere (with|of) radius %number%"); } private Expression radius; @@ -49,16 +49,16 @@ public boolean init(Expression[] exprs, int matchedPattern, @NotNull Kleenean } @Override - protected Sphere[] get(@NotNull Event event) { + protected Shape[] get(@NotNull Event event) { Number radius = this.radius.getSingle(event); if (radius == null) return null; radius = Math.max(radius.doubleValue(), MathUtil.EPSILON); - Sphere sphere = new Sphere(radius.doubleValue()); - if (isSolid) sphere.setStyle(Shape.Style.FILL); - return new Sphere[]{sphere}; + DrawableShape shape = new DrawableShape(new com.sovdee.shapes.Sphere(radius.doubleValue())); + if (isSolid) shape.setStyle(Shape.Style.FILL); + return new Shape[]{shape}; } @Override @@ -68,8 +68,8 @@ public boolean isSingle() { @Override @NotNull - public Class getReturnType() { - return Sphere.class; + public Class getReturnType() { + return Shape.class; } @Override diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphericalCap.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphericalCap.java similarity index 85% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphericalCap.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphericalCap.java index 5e616cc..3d69302 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphericalCap.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphericalCap.java @@ -11,8 +11,8 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; +import com.sovdee.skriptparticles.shapes.DrawableShape; import com.sovdee.skriptparticles.shapes.Shape; -import com.sovdee.skriptparticles.shapes.SphericalCap; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -28,10 +28,10 @@ "set {_shape} to a spherical sector of radius 3 and angle 90 degrees" }) @Since("1.0.0") -public class ExprSphericalCap extends SimpleExpression { +public class ExprSphericalCap extends SimpleExpression { static { - Skript.registerExpression(ExprSphericalCap.class, SphericalCap.class, ExpressionType.COMBINED, + Skript.registerExpression(ExprSphericalCap.class, Shape.class, ExpressionType.COMBINED, "[a] spherical (cap|:sector) (with|of) radius %number% and [cutoff] angle [of] %number% [degrees|:radians]"); } @@ -65,7 +65,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override @Nullable - protected SphericalCap[] get(Event event) { + protected Shape[] get(Event event) { Number radius = this.radius.getSingle(event); Number angle = this.angle.getSingle(event); if (radius == null || angle == null) @@ -75,11 +75,11 @@ protected SphericalCap[] get(Event event) { if (!isRadians) angle = Math.toRadians(angle.doubleValue()); - SphericalCap cap = new SphericalCap(radius.doubleValue(), angle.doubleValue()); + DrawableShape shape = new DrawableShape(new com.sovdee.shapes.SphericalCap(radius.doubleValue(), angle.doubleValue())); if (isSector) - cap.setStyle(Shape.Style.FILL); + shape.setStyle(Shape.Style.FILL); - return new SphericalCap[]{cap}; + return new Shape[]{shape}; } @Override @@ -88,8 +88,8 @@ public boolean isSingle() { } @Override - public Class getReturnType() { - return SphericalCap.class; + public Class getReturnType() { + return Shape.class; } @Override diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprStar.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprStar.java similarity index 84% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprStar.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprStar.java index 213d3b8..906793c 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprStar.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprStar.java @@ -10,8 +10,8 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; +import com.sovdee.skriptparticles.shapes.DrawableShape; import com.sovdee.skriptparticles.shapes.Shape; -import com.sovdee.skriptparticles.shapes.Star; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -25,10 +25,10 @@ "set {_shape} to star with 5 points, inner radius 1, and outer radius 2", "draw the shape of a star with 4 points, inner radius 2, and outer radius 4 at player" }) -public class ExprStar extends SimpleExpression { +public class ExprStar extends SimpleExpression { static { - Skript.registerExpression(ExprStar.class, Star.class, ExpressionType.COMBINED, "[a] [:solid] star with %number% points(,| and) inner radius %number%[,] and outer radius %number%"); + Skript.registerExpression(ExprStar.class, Shape.class, ExpressionType.COMBINED, "[a] [:solid] star with %number% points(,| and) inner radius %number%[,] and outer radius %number%"); } private Expression points; @@ -64,7 +64,7 @@ public boolean init(Expression[] expressions, int i, Kleenean kleenean, Parse @Override @Nullable - protected Star[] get(Event event) { + protected Shape[] get(Event event) { Number points = this.points.getSingle(event); Number innerRadius = this.innerRadius.getSingle(event); Number outerRadius = this.outerRadius.getSingle(event); @@ -75,11 +75,11 @@ protected Star[] get(Event event) { innerRadius = Math.max(innerRadius.doubleValue(), MathUtil.EPSILON); outerRadius = Math.max(outerRadius.doubleValue(), MathUtil.EPSILON); - Star star = new Star(innerRadius.doubleValue(), outerRadius.doubleValue(), angle); + DrawableShape shape = new DrawableShape(new com.sovdee.shapes.Star(innerRadius.doubleValue(), outerRadius.doubleValue(), angle)); if (isSolid) - star.setStyle(Shape.Style.SURFACE); + shape.setStyle(Shape.Style.SURFACE); - return new Star[]{star}; + return new Shape[]{shape}; } @Override @@ -88,8 +88,8 @@ public boolean isSingle() { } @Override - public Class getReturnType() { - return Star.class; + public Class getReturnType() { + return Shape.class; } @Override diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprHelixWindingRate.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprHelixWindingRate.java similarity index 87% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprHelixWindingRate.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprHelixWindingRate.java index 0ddb67f..7418580 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprHelixWindingRate.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprHelixWindingRate.java @@ -6,7 +6,8 @@ import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import com.sovdee.skriptparticles.shapes.Helix; +import com.sovdee.shapes.Helix; +import com.sovdee.skriptparticles.shapes.DrawableShape; import com.sovdee.skriptparticles.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -32,7 +33,7 @@ public class ExprHelixWindingRate extends SimplePropertyExpression { +public class ExprShapeCutoffAngle extends SimplePropertyExpression { static { - register(ExprShapeCutoffAngle.class, Number.class, "cutoff angle", "cutoffshapes"); + register(ExprShapeCutoffAngle.class, Number.class, "cutoff angle", "shapes"); } @Override - public @Nullable Number convert(CutoffShape cutoffShape) { - return cutoffShape.getCutoffAngle(); + public @Nullable Number convert(Shape shape) { + if (shape instanceof DrawableShape ds && ds.getShape() instanceof CutoffShape cs) + return cs.getCutoffAngle(); + return null; } @Override @@ -52,15 +56,17 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { case REMOVE: angle = -angle; case ADD: - for (CutoffShape cutoffShape : getExpr().getArray(event)) { - cutoffShape.setCutoffAngle(cutoffShape.getCutoffAngle() + angle); + for (Shape shape : getExpr().getArray(event)) { + if (shape instanceof DrawableShape ds && ds.getShape() instanceof CutoffShape cs) + cs.setCutoffAngle(cs.getCutoffAngle() + angle); } break; case DELETE: case RESET: case SET: - for (CutoffShape cutoffShape : getExpr().getArray(event)) { - cutoffShape.setCutoffAngle(angle); + for (Shape shape : getExpr().getArray(event)) { + if (shape instanceof DrawableShape ds && ds.getShape() instanceof CutoffShape cs) + cs.setCutoffAngle(angle); } break; case REMOVE_ALL: diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLWH.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLWH.java similarity index 68% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLWH.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLWH.java index 198a985..ab1e101 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLWH.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLWH.java @@ -9,7 +9,9 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.LWHShape; +import com.sovdee.shapes.LWHShape; +import com.sovdee.skriptparticles.shapes.DrawableShape; +import com.sovdee.skriptparticles.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -26,10 +28,10 @@ "add 6 to {_shape}'s shape height" }) @Since("1.0.0") -public class ExprShapeLWH extends SimplePropertyExpression { +public class ExprShapeLWH extends SimplePropertyExpression { static { - register(ExprShapeLWH.class, Number.class, "shape (:length|:width|:height)", "lwhshapes"); + register(ExprShapeLWH.class, Number.class, "shape (:length|:width|:height)", "shapes"); } private int lwh; @@ -42,7 +44,9 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override @Nullable - public Number convert(LWHShape lwhShape) { + public Number convert(Shape shape) { + if (!(shape instanceof DrawableShape ds && ds.getShape() instanceof LWHShape lwhShape)) + return null; return switch (lwh) { case 0 -> lwhShape.getLength(); case 1 -> lwhShape.getWidth(); @@ -70,22 +74,26 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { case REMOVE: value = -value; case ADD: - for (LWHShape shape : getExpr().getArray(event)) { - switch (lwh) { - case 0 -> shape.setLength(shape.getLength() + value); - case 1 -> shape.setWidth(shape.getWidth() + value); - case 2 -> shape.setHeight(shape.getHeight() + value); + for (Shape shape : getExpr().getArray(event)) { + if (shape instanceof DrawableShape ds && ds.getShape() instanceof LWHShape lwhShape) { + switch (lwh) { + case 0 -> lwhShape.setLength(lwhShape.getLength() + value); + case 1 -> lwhShape.setWidth(lwhShape.getWidth() + value); + case 2 -> lwhShape.setHeight(lwhShape.getHeight() + value); + } } } break; case DELETE: case RESET: case SET: - for (LWHShape shape : getExpr().getArray(event)) { - switch (lwh) { - case 0 -> shape.setLength(value); - case 1 -> shape.setWidth(value); - case 2 -> shape.setHeight(value); + for (Shape shape : getExpr().getArray(event)) { + if (shape instanceof DrawableShape ds && ds.getShape() instanceof LWHShape lwhShape) { + switch (lwh) { + case 0 -> lwhShape.setLength(value); + case 1 -> lwhShape.setWidth(value); + case 2 -> lwhShape.setHeight(value); + } } } break; diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLocations.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLocations.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLocations.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLocations.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeNormal.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeNormal.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeNormal.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeNormal.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOffset.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOffset.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOffset.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOffset.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOrientation.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOrientation.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOrientation.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOrientation.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticle.java similarity index 93% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticle.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticle.java index 6ff255e..8cdf0b7 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticle.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticle.java @@ -20,12 +20,11 @@ "You can set the particle to any base or custom particle." }) @Examples({ - "set {_shape}'s particle to flame", - "set {_shape}'s particle to 1 of electric spark with extra 0", + "set {_shape}'s particle to flame particle", + "set {_shape}'s particle to 1 of electric spark particle with extra 0", "reset {_shape}'s particle", "", - "create a new custom particle:", - "\tparticle: soul fire flame", + "create a custom soul fire flame particle:", "\tvelocity: inwards", "\textra: 0.5", "\tforce: true", diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticleDensity.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticleDensity.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticleDensity.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticleDensity.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapePoints.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapePoints.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapePoints.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapePoints.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRadius.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRadius.java similarity index 69% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRadius.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRadius.java index f3cda5e..e1adbc7 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRadius.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRadius.java @@ -7,7 +7,9 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.PropertyExpression; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import com.sovdee.skriptparticles.shapes.RadialShape; +import com.sovdee.shapes.RadialShape; +import com.sovdee.skriptparticles.shapes.DrawableShape; +import com.sovdee.skriptparticles.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -23,16 +25,18 @@ "reset {_shape}'s radius" }) @Since("1.0.0") -public class ExprShapeRadius extends SimplePropertyExpression { +public class ExprShapeRadius extends SimplePropertyExpression { static { - PropertyExpression.register(ExprShapeRadius.class, Number.class, "radius", "radialshapes"); + PropertyExpression.register(ExprShapeRadius.class, Number.class, "radius", "shapes"); } @Override @Nullable - public Number convert(RadialShape shape) { - return shape.getRadius(); + public Number convert(Shape shape) { + if (shape instanceof DrawableShape ds && ds.getShape() instanceof RadialShape rs) + return rs.getRadius(); + return null; } @Override @@ -46,8 +50,8 @@ public Class[] acceptChange(ChangeMode mode) { @Override public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { - RadialShape[] radialShapes = getExpr().getArray(event); - if (radialShapes.length == 0) + Shape[] shapes = getExpr().getArray(event); + if (shapes.length == 0) return; double deltaValue = (delta != null) ? ((Number) delta[0]).doubleValue() : 1; @@ -55,16 +59,18 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { case REMOVE: deltaValue = -deltaValue; case ADD: - for (RadialShape shape : radialShapes) { - shape.setRadius(Math.max(0.001, shape.getRadius() + deltaValue)); + for (Shape shape : shapes) { + if (shape instanceof DrawableShape ds && ds.getShape() instanceof RadialShape rs) + rs.setRadius(Math.max(0.001, rs.getRadius() + deltaValue)); } break; case RESET: case DELETE: case SET: deltaValue = Math.max(0.001, deltaValue); - for (RadialShape shape : radialShapes) { - shape.setRadius(deltaValue); + for (Shape shape : shapes) { + if (shape instanceof DrawableShape ds && ds.getShape() instanceof RadialShape rs) + rs.setRadius(deltaValue); } break; case REMOVE_ALL: diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRelativeAxis.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRelativeAxis.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRelativeAxis.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRelativeAxis.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeScale.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeScale.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeScale.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeScale.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSideLength.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSideLength.java similarity index 73% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSideLength.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSideLength.java index 5222127..d297db6 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSideLength.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSideLength.java @@ -6,7 +6,9 @@ import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import com.sovdee.skriptparticles.shapes.PolyShape; +import com.sovdee.shapes.PolyShape; +import com.sovdee.skriptparticles.shapes.DrawableShape; +import com.sovdee.skriptparticles.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -23,16 +25,18 @@ "send side length of {_shape}" }) @Since("1.0.0") -public class ExprShapeSideLength extends SimplePropertyExpression { +public class ExprShapeSideLength extends SimplePropertyExpression { static { - register(ExprShapeSideLength.class, Number.class, "side length", "polyshapes"); + register(ExprShapeSideLength.class, Number.class, "side length", "shapes"); } @Override @Nullable - public Number convert(PolyShape polyShape) { - return polyShape.getSideLength(); + public Number convert(Shape shape) { + if (shape instanceof DrawableShape ds && ds.getShape() instanceof PolyShape ps) + return ps.getSideLength(); + return null; } @Override @@ -52,16 +56,18 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { case REMOVE: change = -change; case ADD: - for (PolyShape polyShape : getExpr().getArray(event)) { - polyShape.setSideLength(Math.max(0.001, polyShape.getSideLength() + change)); + for (Shape shape : getExpr().getArray(event)) { + if (shape instanceof DrawableShape ds && ds.getShape() instanceof PolyShape ps) + ps.setSideLength(Math.max(0.001, ps.getSideLength() + change)); } break; case RESET: case DELETE: case SET: change = Math.max(0.001, change); - for (PolyShape polyShape : getExpr().getArray(event)) { - polyShape.setSideLength(change); + for (Shape shape : getExpr().getArray(event)) { + if (shape instanceof DrawableShape ds && ds.getShape() instanceof PolyShape ps) + ps.setSideLength(change); } break; case REMOVE_ALL: diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSides.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSides.java similarity index 71% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSides.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSides.java index 5d251c6..99a95f3 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSides.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSides.java @@ -6,7 +6,9 @@ import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import com.sovdee.skriptparticles.shapes.PolyShape; +import com.sovdee.shapes.PolyShape; +import com.sovdee.skriptparticles.shapes.DrawableShape; +import com.sovdee.skriptparticles.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -23,16 +25,18 @@ "send sides of {_shape}" }) @Since("1.0.0") -public class ExprShapeSides extends SimplePropertyExpression { +public class ExprShapeSides extends SimplePropertyExpression { static { - register(ExprShapeSides.class, Integer.class, "side(s| count)", "polyshapes"); + register(ExprShapeSides.class, Integer.class, "side(s| count)", "shapes"); } @Override @Nullable - public Integer convert(PolyShape polyShape) { - return polyShape.getSides(); + public Integer convert(Shape shape) { + if (shape instanceof DrawableShape ds && ds.getShape() instanceof PolyShape ps) + return ps.getSides(); + return null; } @Override @@ -52,16 +56,18 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { case REMOVE: change = -change; case ADD: - for (PolyShape polyShape : getExpr().getArray(event)) { - polyShape.setSides(Math.max(3, polyShape.getSides() + change)); + for (Shape shape : getExpr().getArray(event)) { + if (shape instanceof DrawableShape ds && ds.getShape() instanceof PolyShape ps) + ps.setSides(Math.max(3, ps.getSides() + change)); } break; case RESET: case DELETE: case SET: change = Math.max(3, change); - for (PolyShape polyShape : getExpr().getArray(event)) { - polyShape.setSides(change); + for (Shape shape : getExpr().getArray(event)) { + if (shape instanceof DrawableShape ds && ds.getShape() instanceof PolyShape ps) + ps.setSides(change); } break; case REMOVE_ALL: diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeStyle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeStyle.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeStyle.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeStyle.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarPoints.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarPoints.java similarity index 85% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarPoints.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarPoints.java index 9e493e3..0bdb6ed 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarPoints.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarPoints.java @@ -6,8 +6,9 @@ import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; +import com.sovdee.shapes.Star; +import com.sovdee.skriptparticles.shapes.DrawableShape; import com.sovdee.skriptparticles.shapes.Shape; -import com.sovdee.skriptparticles.shapes.Star; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -30,8 +31,8 @@ public class ExprStarPoints extends SimplePropertyExpression { @Override public @Nullable Number convert(Shape shape) { - if (shape instanceof Star) - return ((Star) shape).getStarPoints(); + if (shape instanceof DrawableShape ds && ds.getShape() instanceof Star star) + return star.getStarPoints(); return null; } @@ -55,7 +56,7 @@ public void change(Event event, Object @Nullable [] delta, Changer.ChangeMode mo deltaValue = -deltaValue; case ADD: for (Shape shape : shapes) { - if (shape instanceof Star star) { + if (shape instanceof DrawableShape ds && ds.getShape() instanceof Star star) { star.setStarPoints(Math.max(star.getStarPoints() + deltaValue, 2)); } } @@ -63,7 +64,7 @@ public void change(Event event, Object @Nullable [] delta, Changer.ChangeMode mo case SET: deltaValue = Math.max(deltaValue, 2); for (Shape shape : shapes) { - if (shape instanceof Star star) { + if (shape instanceof DrawableShape ds && ds.getShape() instanceof Star star) { star.setStarPoints(deltaValue); } } diff --git a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarRadii.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarRadii.java similarity index 89% rename from src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarRadii.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarRadii.java index 1bb94a9..1e7abb1 100644 --- a/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarRadii.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarRadii.java @@ -9,9 +9,10 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.util.Kleenean; +import com.sovdee.shapes.Star; +import com.sovdee.shapes.util.MathUtil; +import com.sovdee.skriptparticles.shapes.DrawableShape; import com.sovdee.skriptparticles.shapes.Shape; -import com.sovdee.skriptparticles.shapes.Star; -import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -43,7 +44,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override public @Nullable Number convert(Shape shape) { - if (shape instanceof Star star) { + if (shape instanceof DrawableShape ds && ds.getShape() instanceof Star star) { return (isInner ? star.getInnerRadius() : star.getOuterRadius()); } return null; @@ -69,7 +70,7 @@ public void change(Event event, Object @Nullable [] delta, Changer.ChangeMode mo deltaValue = -deltaValue; case ADD: for (Shape shape : shapes) { - if (shape instanceof Star star) { + if (shape instanceof DrawableShape ds && ds.getShape() instanceof Star star) { if (isInner) { star.setInnerRadius(Math.max(star.getInnerRadius() + deltaValue, MathUtil.EPSILON)); } else { @@ -81,7 +82,7 @@ public void change(Event event, Object @Nullable [] delta, Changer.ChangeMode mo case SET: deltaValue = Math.max(deltaValue, MathUtil.EPSILON); for (Shape shape : shapes) { - if (shape instanceof Star star) { + if (shape instanceof DrawableShape ds && ds.getShape() instanceof Star star) { if (isInner) { star.setInnerRadius(deltaValue); } else { diff --git a/src/main/java/com/sovdee/skriptparticles/elements/package-info.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/package-info.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/package-info.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/package-info.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/sections/DrawShapeEffectSection.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/DrawShapeEffectSection.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/sections/DrawShapeEffectSection.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/DrawShapeEffectSection.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/sections/EffSecDrawShape.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/EffSecDrawShape.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/sections/EffSecDrawShape.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/EffSecDrawShape.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/sections/EffSecDrawShapeAnimation.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/EffSecDrawShapeAnimation.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/sections/EffSecDrawShapeAnimation.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/EffSecDrawShapeAnimation.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/sections/SecParticle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/SecParticle.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/sections/SecParticle.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/SecParticle.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/types/ParticleTypes.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ParticleTypes.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/types/ParticleTypes.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ParticleTypes.java diff --git a/src/main/java/com/sovdee/skriptparticles/elements/types/RotationTypes.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/RotationTypes.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/elements/types/RotationTypes.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/RotationTypes.java diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java new file mode 100644 index 0000000..1ff19cb --- /dev/null +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java @@ -0,0 +1,77 @@ +package com.sovdee.skriptparticles.elements.types; + +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.Parser; +import ch.njol.skript.lang.ParseContext; +import ch.njol.skript.registrations.Classes; +import com.sovdee.skriptparticles.shapes.Shape; +import org.jetbrains.annotations.Nullable; + +public class ShapeTypes { + static { + // Shape + Classes.registerClass(new ClassInfo<>(Shape.class, "shape") + .user("shapes?") + .name("Shape") + .description("Represents an abstract particle shape. E.g. circle, line, etc.") + .parser(new Parser<>() { + + @Override + public Shape parse(String input, ParseContext context) { + return null; + } + + @Override + public boolean canParse(ParseContext context) { + return false; + } + + @Override + public String toString(Shape o, int flags) { + return o.toString(); + } + + @Override + public String toVariableNameString(Shape shape) { + return "shape:" + shape.getUUID(); + } + }) + .cloner(Shape::clone) + ); + + // Style + Classes.registerClass(new ClassInfo<>(Shape.Style.class, "shapestyle") + .user("shape ?styles?") + .name("Shape Style") + .description("Represents the way the shape is drawn. Outlined is a wireframe representation, Surface is filling in all the surfaces of the shape, and Filled is filling in the entire shape.") + .parser(new Parser<>() { + @Override + public @Nullable Shape.Style parse(String s, ParseContext context) { + s = s.toUpperCase(); + if (s.matches("OUTLINE(D)?") || s.matches("WIREFRAME")) { + return Shape.Style.OUTLINE; + } else if (s.matches("SURFACE") || s.matches("HOLLOW")) { + return Shape.Style.SURFACE; + } else if (s.matches("FILL(ED)?") || s.matches("SOLID")) { + return Shape.Style.FILL; + } + return null; + } + + @Override + public boolean canParse(ParseContext context) { + return true; + } + + @Override + public String toString(Shape.Style style, int i) { + return style.toString(); + } + + @Override + public String toVariableNameString(Shape.Style style) { + return "shapestyle:" + style; + } + })); + } +} diff --git a/src/main/java/com/sovdee/skriptparticles/particles/Particle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/particles/Particle.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/particles/Particle.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/particles/Particle.java diff --git a/src/main/java/com/sovdee/skriptparticles/particles/ParticleGradient.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/particles/ParticleGradient.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/particles/ParticleGradient.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/particles/ParticleGradient.java diff --git a/src/main/java/com/sovdee/skriptparticles/particles/ParticleMotion.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/particles/ParticleMotion.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/particles/ParticleMotion.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/particles/ParticleMotion.java diff --git a/src/main/java/com/sovdee/skriptparticles/particles/package-info.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/particles/package-info.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/particles/package-info.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/particles/package-info.java diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawableShape.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawableShape.java new file mode 100644 index 0000000..be90260 --- /dev/null +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawableShape.java @@ -0,0 +1,422 @@ +package com.sovdee.skriptparticles.shapes; + +import ch.njol.skript.Skript; +import com.sovdee.shapes.AbstractShape; +import com.sovdee.skriptparticles.particles.Particle; +import com.sovdee.skriptparticles.particles.ParticleGradient; +import com.sovdee.skriptparticles.util.DynamicLocation; +import com.sovdee.skriptparticles.util.MathUtil; +import com.sovdee.skriptparticles.util.ParticleUtil; +import com.sovdee.skriptparticles.util.Quaternion; +import com.sovdee.skriptparticles.util.VectorConversion; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.util.Vector; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.Contract; +import org.joml.Quaterniond; +import org.joml.Quaternionf; +import org.joml.Vector3d; + +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.function.Consumer; + +/** + * Wraps a library {@link com.sovdee.shapes.Shape} with rendering metadata (particle, location, animation, etc.) + * This is the type that Skript elements operate on. + */ +public class DrawableShape implements Shape { + + private final com.sovdee.shapes.Shape shape; + + private Particle particle; + private @Nullable DynamicLocation location; + private @Nullable DynamicLocation lastLocation; + private final Quaternion lastOrientation; + private long animationDuration = 0; + private boolean drawLocalAxes = false; + private boolean drawGlobalAxes = false; + + public DrawableShape(com.sovdee.shapes.Shape shape) { + this.shape = shape; + this.particle = (Particle) new Particle(org.bukkit.Particle.FLAME).parent(this).extra(0); + this.lastOrientation = Quaternion.IDENTITY.clone(); + } + + /** + * Gets the underlying library shape. + */ + public com.sovdee.shapes.Shape getShape() { + return shape; + } + + // ---- Delegated geometric methods ---- + + @Override + public Set getPoints() { + return VectorConversion.toBukkit(shape.getPoints()); + } + + @Override + public void setPoints(Set points) { + shape.setPoints(VectorConversion.toJOML(points)); + } + + @Override + public Set getPoints(Quaternion orientation) { + Quaterniond qd = new Quaterniond(orientation.x, orientation.y, orientation.z, orientation.w); + return VectorConversion.toBukkit(shape.getPoints(qd)); + } + + @Override + public void generatePoints(Set points) { + Set jomlPoints = new LinkedHashSet<>(); + shape.generatePoints(jomlPoints); + points.addAll(VectorConversion.toBukkit(jomlPoints)); + } + + @Override + public void generateOutline(Set points) { + Set jomlPoints = new LinkedHashSet<>(); + shape.generateOutline(jomlPoints); + points.addAll(VectorConversion.toBukkit(jomlPoints)); + } + + @Override + public void generateSurface(Set points) { + Set jomlPoints = new LinkedHashSet<>(); + shape.generateSurface(jomlPoints); + points.addAll(VectorConversion.toBukkit(jomlPoints)); + } + + @Override + public void generateFilled(Set points) { + Set jomlPoints = new LinkedHashSet<>(); + shape.generateFilled(jomlPoints); + points.addAll(VectorConversion.toBukkit(jomlPoints)); + } + + // ---- Draw methods ---- + + @Override + public void draw(Collection recipients) { + assert location != null; + draw(location, Quaternion.IDENTITY, particle, recipients); + } + + @Override + public void draw(DynamicLocation location, Collection recipients) { + draw(location, Quaternion.IDENTITY, particle, recipients); + } + + @Override + public void draw(DynamicLocation location, Quaternion baseOrientation, Particle particle, Collection recipients) { + if (location.isNull()) { + if (this.location == null) + return; + location = this.location.clone(); + } + + lastLocation = location.clone(); + Quaterniond shapeOrientation = shape.getOrientation(); + Quaternion shapeOrientationQ = new Quaternion((float) shapeOrientation.x, (float) shapeOrientation.y, (float) shapeOrientation.z, (float) shapeOrientation.w); + lastOrientation.set(baseOrientation.clone().mul(shapeOrientationQ)); + + if (!particle.override()) { + this.particle.parent(this); + particle = this.particle; + @Nullable ParticleGradient gradient = particle.gradient(); + if (gradient != null && gradient.isLocal()) + gradient.setOrientation(lastOrientation); + } + + particle.receivers(recipients); + + // Get points from library using the last orientation + Quaterniond lastOrientationD = new Quaterniond(lastOrientation.x, lastOrientation.y, lastOrientation.z, lastOrientation.w); + Collection toDraw = VectorConversion.toBukkit(shape.getPoints(lastOrientationD)); + + if (animationDuration > 0) { + int particleCount = toDraw.size(); + double millisecondsPerPoint = animationDuration / (double) particleCount; + Iterator> batchIterator = MathUtil.batch(toDraw, millisecondsPerPoint).iterator(); + Particle finalParticle = particle; + BukkitRunnable runnable = new BukkitRunnable() { + @Override + public void run() { + if (!batchIterator.hasNext()) { + this.cancel(); + return; + } + List batch = batchIterator.next(); + try { + for (Vector point : batch) { + finalParticle.spawn(point); + } + } catch (IllegalArgumentException e) { + Skript.error("Failed to spawn particle! Error: " + e.getMessage()); + } + } + }; + runnable.runTaskTimerAsynchronously(Skript.getInstance(), 0, 1); + } else { + for (Vector point : toDraw) { + try { + particle.spawn(point); + } catch (IllegalArgumentException e) { + Skript.error("Failed to spawn particle! Error: " + e.getMessage()); + return; + } + } + } + + if (drawLocalAxes) { + ParticleUtil.drawAxes(location.getLocation().add(VectorConversion.toBukkit(shape.getOffset())), lastOrientation, recipients); + } + if (drawGlobalAxes) { + ParticleUtil.drawAxes(location.getLocation().add(VectorConversion.toBukkit(shape.getOffset())), Quaternion.IDENTITY, recipients); + } + } + + @Override + public void draw(DynamicLocation location, Consumer consumer, Collection recipients) { + consumer.accept(this); + Quaterniond shapeOrientation = shape.getOrientation(); + Quaternion shapeOrientationQ = new Quaternion((float) shapeOrientation.x, (float) shapeOrientation.y, (float) shapeOrientation.z, (float) shapeOrientation.w); + draw(location, shapeOrientationQ, this.particle, recipients); + } + + // ---- Axis methods ---- + + @Override + public Vector getRelativeXAxis(boolean useLastOrientation) { + if (useLastOrientation) { + return lastOrientation.transform(new Vector(1, 0, 0)); + } + return VectorConversion.toBukkit(shape.getRelativeXAxis(false)); + } + + @Override + public Vector getRelativeYAxis(boolean useLastOrientation) { + if (useLastOrientation) { + return lastOrientation.transform(new Vector(0, 1, 0)); + } + return VectorConversion.toBukkit(shape.getRelativeYAxis(false)); + } + + @Override + public Vector getRelativeZAxis(boolean useLastOrientation) { + if (useLastOrientation) { + return lastOrientation.transform(new Vector(0, 0, 1)); + } + return VectorConversion.toBukkit(shape.getRelativeZAxis(false)); + } + + // ---- Debug axes ---- + + @Override + public void showLocalAxes(boolean show) { drawLocalAxes = show; } + + @Override + public boolean showLocalAxes() { return drawLocalAxes; } + + @Override + public void showGlobalAxes(boolean show) { drawGlobalAxes = show; } + + @Override + public boolean showGlobalAxes() { return drawGlobalAxes; } + + // ---- Location ---- + + @Override + @Nullable + public DynamicLocation getLastLocation() { return lastLocation; } + + @Override + @Nullable + public DynamicLocation getLocation() { + if (location == null) return null; + return location.clone(); + } + + @Override + public void setLocation(DynamicLocation location) { this.location = location; } + + // ---- Style ---- + + @Override + public Style getStyle() { + return Style.valueOf(shape.getStyle().name()); + } + + @Override + public void setStyle(Style style) { + shape.setStyle(com.sovdee.shapes.Shape.Style.valueOf(style.name())); + } + + // ---- Orientation ---- + + @Override + public Quaternion getOrientation() { + Quaterniond q = shape.getOrientation(); + return new Quaternion((float) q.x, (float) q.y, (float) q.z, (float) q.w); + } + + @Override + public void setOrientation(Quaternionf orientation) { + shape.setOrientation(new Quaterniond(orientation.x, orientation.y, orientation.z, orientation.w)); + } + + // ---- Scale ---- + + @Override + public double getScale() { return shape.getScale(); } + + @Override + public void setScale(double scale) { shape.setScale(scale); } + + // ---- Offset ---- + + @Override + public Vector getOffset() { + return VectorConversion.toBukkit(shape.getOffset()); + } + + @Override + public void setOffset(Vector offset) { + shape.setOffset(VectorConversion.toJOML(offset)); + } + + // ---- UUID ---- + + @Override + public UUID getUUID() { return shape.getUUID(); } + + // ---- Particle ---- + + @Override + public Particle getParticle() { return particle.clone(); } + + @Override + public void setParticle(Particle particle) { this.particle = particle; } + + // ---- Ordering ---- + + @Override + @Nullable + public Comparator getOrdering() { + // We store ordering on the library shape via a Vector3d comparator. + // This getter returns a Bukkit Vector comparator for Skript compatibility. + Comparator libOrdering = shape.getOrdering(); + if (libOrdering == null) return null; + return (a, b) -> libOrdering.compare(VectorConversion.toJOML(a), VectorConversion.toJOML(b)); + } + + @Override + public void setOrdering(@Nullable Comparator comparator) { + if (comparator == null) { + shape.setOrdering(null); + } else { + shape.setOrdering((a, b) -> comparator.compare(VectorConversion.toBukkit(a), VectorConversion.toBukkit(b))); + } + } + + // ---- Density ---- + + @Override + public double getParticleDensity() { return shape.getParticleDensity(); } + + @Override + public void setParticleDensity(double particleDensity) { shape.setParticleDensity(particleDensity); } + + @Override + public int getParticleCount() { return shape.getParticleCount(); } + + @Override + public void setParticleCount(int particleCount) { shape.setParticleCount(particleCount); } + + // ---- Update state ---- + + @Override + public boolean needsUpdate() { return shape.needsUpdate(); } + + @Override + public void setNeedsUpdate(boolean needsUpdate) { shape.setNeedsUpdate(needsUpdate); } + + // ---- Animation ---- + + @Override + public long getAnimationDuration() { return animationDuration; } + + @Override + public void setAnimationDuration(long animationDuration) { this.animationDuration = animationDuration; } + + // ---- Clone/Copy ---- + + @Override + @Contract("-> new") + public DrawableShape clone() { + DrawableShape copy = new DrawableShape(shape.clone()); + copy.particle = this.particle.clone(); + if (this.location != null) + copy.location = this.location.clone(); + copy.animationDuration = this.animationDuration; + copy.drawLocalAxes = this.drawLocalAxes; + copy.drawGlobalAxes = this.drawGlobalAxes; + copy.lastOrientation.set(this.lastOrientation); + // Preserve ordering via the library shape's copy + return copy; + } + + @Override + @Contract("_ -> param1") + public Shape copyTo(Shape other) { + if (other instanceof DrawableShape ds) { + shape.copyTo(ds.shape); + ds.particle = this.particle.clone(); + if (this.location != null) + ds.location = this.location.clone(); + ds.animationDuration = this.animationDuration; + ds.drawLocalAxes = this.drawLocalAxes; + ds.drawGlobalAxes = this.drawGlobalAxes; + ds.lastOrientation.set(this.lastOrientation); + } + return other; + } + + // ---- State ---- + + @Override + @Contract("-> new") + public State getState() { + com.sovdee.shapes.Shape.State libState = shape.getState(); + Style style = Style.valueOf(libState.style().name()); + return new State(style, libState.orientationHash(), libState.scale(), libState.offsetHash(), libState.particleDensity()); + } + + @Override + @Contract("_ -> new") + public State getState(Quaternion orientation) { + Quaterniond qd = new Quaterniond(orientation.x, orientation.y, orientation.z, orientation.w); + com.sovdee.shapes.Shape.State libState = shape.getState(qd); + Style style = Style.valueOf(libState.style().name()); + return new State(style, libState.orientationHash(), libState.scale(), libState.offsetHash(), libState.particleDensity()); + } + + @Override + public void setLastState(State state) { + com.sovdee.shapes.Shape.Style libStyle = com.sovdee.shapes.Shape.Style.valueOf(state.style().name()); + shape.setLastState(new com.sovdee.shapes.Shape.State(libStyle, state.orientationHash(), state.scale(), state.offsetHash(), state.particleDensity())); + } + + @Override + public String toString() { + return shape.toString(); + } +} diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicBezierCurve.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicBezierCurve.java new file mode 100644 index 0000000..b03109d --- /dev/null +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicBezierCurve.java @@ -0,0 +1,67 @@ +package com.sovdee.skriptparticles.shapes; + +import com.sovdee.shapes.BezierCurve; +import com.sovdee.shapes.Shape; +import com.sovdee.skriptparticles.util.Point; +import org.bukkit.Location; +import org.joml.Quaterniond; +import org.joml.Vector3d; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * A bezier curve that supports dynamic (entity-following) control points via the plugin's Point type. + */ +public class DynamicBezierCurve extends BezierCurve { + + private final Point start; + private final Point end; + private final List> dynamicControlPoints; + + public DynamicBezierCurve(Point start, Point end, List> controlPoints) { + super(evaluatePoints(start, end, controlPoints)); + this.start = start; + this.end = end; + this.dynamicControlPoints = new ArrayList<>(controlPoints); + } + + private static List evaluatePoints(Point start, Point end, List> controlPoints) { + List result = new ArrayList<>(); + Location origin = start.getLocation(); + result.add(toVector3d(start.getVector(origin))); + for (Point cp : controlPoints) + result.add(toVector3d(cp.getVector(origin))); + result.add(toVector3d(end.getVector(origin))); + return result; + } + + private static Vector3d toVector3d(org.bukkit.util.Vector v) { + return new Vector3d(v.getX(), v.getY(), v.getZ()); + } + + @Override + public Set getPoints(Quaterniond orientation) { + Set points = super.getPoints(orientation); + this.setNeedsUpdate(true); + return points; + } + + @Override + public void generateOutline(Set points) { + // Re-evaluate dynamic control points before generating + this.setControlPoints(evaluatePoints(start, end, dynamicControlPoints)); + super.generateOutline(points); + } + + public Point getStart() { return start; } + public Point getEnd() { return end; } + public List> getDynamicControlPoints() { return dynamicControlPoints; } + + @Override + public Shape clone() { + DynamicBezierCurve curve = new DynamicBezierCurve(start, end, dynamicControlPoints); + return this.copyTo(curve); + } +} diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicCuboid.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicCuboid.java new file mode 100644 index 0000000..6b2cc8a --- /dev/null +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicCuboid.java @@ -0,0 +1,63 @@ +package com.sovdee.skriptparticles.shapes; + +import com.sovdee.shapes.Cuboid; +import com.sovdee.shapes.Shape; +import com.sovdee.skriptparticles.util.DynamicLocation; +import org.bukkit.Location; +import org.joml.Quaterniond; +import org.joml.Vector3d; + +import java.util.Set; + +/** + * A cuboid that tracks dynamic locations (entities) for its corners. + */ +public class DynamicCuboid extends Cuboid { + + private final DynamicLocation negativeCorner; + private final DynamicLocation positiveCorner; + + public DynamicCuboid(DynamicLocation cornerA, DynamicLocation cornerB) { + super(computeLength(cornerA, cornerB), computeWidth(cornerA, cornerB), computeHeight(cornerA, cornerB)); + this.negativeCorner = cornerA.clone(); + this.positiveCorner = cornerB.clone(); + } + + private static double computeLength(DynamicLocation a, DynamicLocation b) { + return Math.max(Math.abs(b.getLocation().getX() - a.getLocation().getX()), 0.001); + } + + private static double computeWidth(DynamicLocation a, DynamicLocation b) { + return Math.max(Math.abs(b.getLocation().getZ() - a.getLocation().getZ()), 0.001); + } + + private static double computeHeight(DynamicLocation a, DynamicLocation b) { + return Math.max(Math.abs(b.getLocation().getY() - a.getLocation().getY()), 0.001); + } + + @Override + public Set getPoints(Quaterniond orientation) { + Set points = super.getPoints(orientation); + this.setNeedsUpdate(true); + return points; + } + + @Override + public void generatePoints(Set points) { + Location neg = negativeCorner.getLocation(); + Location pos = positiveCorner.getLocation(); + this.setLength(Math.abs(pos.getX() - neg.getX())); + this.setWidth(Math.abs(pos.getZ() - neg.getZ())); + this.setHeight(Math.abs(pos.getY() - neg.getY())); + super.generatePoints(points); + } + + public DynamicLocation getNegativeCorner() { return negativeCorner; } + public DynamicLocation getPositiveCorner() { return positiveCorner; } + + @Override + public Shape clone() { + DynamicCuboid cuboid = new DynamicCuboid(negativeCorner, positiveCorner); + return this.copyTo(cuboid); + } +} diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicLine.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicLine.java new file mode 100644 index 0000000..24499ca --- /dev/null +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicLine.java @@ -0,0 +1,56 @@ +package com.sovdee.skriptparticles.shapes; + +import com.sovdee.shapes.Line; +import com.sovdee.shapes.Shape; +import com.sovdee.skriptparticles.util.DynamicLocation; +import org.joml.Quaterniond; +import org.joml.Vector3d; + +import java.util.Set; + +/** + * A line that tracks dynamic locations (entities). + * Recalculates start/end vectors from DynamicLocations before each point generation. + */ +public class DynamicLine extends Line { + + private final DynamicLocation startLocation; + private final DynamicLocation endLocation; + + public DynamicLine(DynamicLocation start, DynamicLocation end) { + super(new Vector3d(0, 0, 0), toRelativeVector(start, end)); + this.startLocation = start.clone(); + this.endLocation = end.clone(); + } + + private static Vector3d toRelativeVector(DynamicLocation start, DynamicLocation end) { + org.bukkit.util.Vector startVec = start.getLocation().toVector(); + org.bukkit.util.Vector endVec = end.getLocation().toVector(); + org.bukkit.util.Vector diff = endVec.subtract(startVec); + return new Vector3d(diff.getX(), diff.getY(), diff.getZ()); + } + + @Override + public Set getPoints(Quaterniond orientation) { + Set points = super.getPoints(orientation); + this.setNeedsUpdate(true); + return points; + } + + @Override + public void generatePoints(Set points) { + Vector3d relativeEnd = toRelativeVector(startLocation, endLocation); + this.setStart(new Vector3d(0, 0, 0)); + this.setEnd(relativeEnd); + super.generatePoints(points); + } + + public DynamicLocation getStartLocation() { return startLocation; } + public DynamicLocation getEndLocation() { return endLocation; } + + @Override + public Shape clone() { + DynamicLine line = new DynamicLine(this.startLocation, this.endLocation); + return this.copyTo(line); + } +} diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicRectangle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicRectangle.java new file mode 100644 index 0000000..a85e7a8 --- /dev/null +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicRectangle.java @@ -0,0 +1,77 @@ +package com.sovdee.skriptparticles.shapes; + +import com.sovdee.shapes.Rectangle; +import com.sovdee.shapes.Shape; +import com.sovdee.skriptparticles.util.DynamicLocation; +import org.bukkit.Location; +import org.joml.Quaterniond; +import org.joml.Vector3d; + +import java.util.Set; + +/** + * A rectangle that tracks dynamic locations (entities) for its corners. + */ +public class DynamicRectangle extends Rectangle { + + private final DynamicLocation negativeCorner; + private final DynamicLocation positiveCorner; + + public DynamicRectangle(DynamicLocation cornerA, DynamicLocation cornerB, Plane plane) { + super(computeLength(cornerA, cornerB, plane), computeWidth(cornerA, cornerB, plane), plane); + this.negativeCorner = cornerA.clone(); + this.positiveCorner = cornerB.clone(); + } + + private static double computeLength(DynamicLocation a, DynamicLocation b, Plane plane) { + Location al = a.getLocation(); + Location bl = b.getLocation(); + return switch (plane) { + case XZ, XY -> Math.max(Math.abs(al.getX() - bl.getX()), 0.001); + case YZ -> Math.max(Math.abs(al.getY() - bl.getY()), 0.001); + }; + } + + private static double computeWidth(DynamicLocation a, DynamicLocation b, Plane plane) { + Location al = a.getLocation(); + Location bl = b.getLocation(); + return switch (plane) { + case XZ, YZ -> Math.max(Math.abs(al.getZ() - bl.getZ()), 0.001); + case XY -> Math.max(Math.abs(al.getY() - bl.getY()), 0.001); + }; + } + + @Override + public Set getPoints(Quaterniond orientation) { + Set points = super.getPoints(orientation); + this.setNeedsUpdate(true); + return points; + } + + @Override + public void generatePoints(Set points) { + Location neg = negativeCorner.getLocation(); + Location pos = positiveCorner.getLocation(); + Plane plane = getPlane(); + double length = switch (plane) { + case XZ, XY -> Math.abs(neg.getX() - pos.getX()); + case YZ -> Math.abs(neg.getY() - pos.getY()); + }; + double width = switch (plane) { + case XZ, YZ -> Math.abs(neg.getZ() - pos.getZ()); + case XY -> Math.abs(neg.getY() - pos.getY()); + }; + this.setLength(length); + this.setWidth(width); + super.generatePoints(points); + } + + public DynamicLocation getNegativeCorner() { return negativeCorner; } + public DynamicLocation getPositiveCorner() { return positiveCorner; } + + @Override + public Shape clone() { + DynamicRectangle rect = new DynamicRectangle(negativeCorner, positiveCorner, getPlane()); + return this.copyTo(rect); + } +} diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/Shape.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/Shape.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/shapes/Shape.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/Shape.java diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/package-info.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/package-info.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/shapes/package-info.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/package-info.java diff --git a/src/main/java/com/sovdee/skriptparticles/util/DynamicLocation.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/util/DynamicLocation.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/util/DynamicLocation.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/util/DynamicLocation.java diff --git a/src/main/java/com/sovdee/skriptparticles/util/MathUtil.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/util/MathUtil.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/util/MathUtil.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/util/MathUtil.java diff --git a/src/main/java/com/sovdee/skriptparticles/util/ParticleUtil.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/util/ParticleUtil.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/util/ParticleUtil.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/util/ParticleUtil.java diff --git a/src/main/java/com/sovdee/skriptparticles/util/Point.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/util/Point.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/util/Point.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/util/Point.java diff --git a/src/main/java/com/sovdee/skriptparticles/util/Quaternion.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/util/Quaternion.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/util/Quaternion.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/util/Quaternion.java diff --git a/src/main/java/com/sovdee/skriptparticles/util/ReflectionUtils.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/util/ReflectionUtils.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/util/ReflectionUtils.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/util/ReflectionUtils.java diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/util/VectorConversion.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/util/VectorConversion.java new file mode 100644 index 0000000..4780668 --- /dev/null +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/util/VectorConversion.java @@ -0,0 +1,37 @@ +package com.sovdee.skriptparticles.util; + +import org.bukkit.util.Vector; +import org.joml.Vector3d; + +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * Utility class for converting between Bukkit Vectors and JOML Vector3d. + */ +public class VectorConversion { + + public static Vector toBukkit(Vector3d v) { + return new Vector(v.x, v.y, v.z); + } + + public static Vector3d toJOML(Vector v) { + return new Vector3d(v.getX(), v.getY(), v.getZ()); + } + + public static Set toBukkit(Set points) { + Set result = new LinkedHashSet<>(); + for (Vector3d v : points) { + result.add(toBukkit(v)); + } + return result; + } + + public static Set toJOML(Set points) { + Set result = new LinkedHashSet<>(); + for (Vector v : points) { + result.add(toJOML(v)); + } + return result; + } +} diff --git a/src/main/java/com/sovdee/skriptparticles/util/package-info.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/util/package-info.java similarity index 100% rename from src/main/java/com/sovdee/skriptparticles/util/package-info.java rename to skript-particle/src/main/java/com/sovdee/skriptparticles/util/package-info.java diff --git a/src/main/resources/lang/english.lang b/skript-particle/src/main/resources/lang/english.lang similarity index 100% rename from src/main/resources/lang/english.lang rename to skript-particle/src/main/resources/lang/english.lang diff --git a/src/main/resources/plugin.yml b/skript-particle/src/main/resources/plugin.yml similarity index 100% rename from src/main/resources/plugin.yml rename to skript-particle/src/main/resources/plugin.yml diff --git a/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java b/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java deleted file mode 100644 index 474790f..0000000 --- a/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java +++ /dev/null @@ -1,201 +0,0 @@ -package com.sovdee.skriptparticles.elements.types; - -import ch.njol.skript.classes.ClassInfo; -import ch.njol.skript.classes.Parser; -import ch.njol.skript.lang.ParseContext; -import ch.njol.skript.registrations.Classes; -import com.sovdee.skriptparticles.shapes.CutoffShape; -import com.sovdee.skriptparticles.shapes.LWHShape; -import com.sovdee.skriptparticles.shapes.PolyShape; -import com.sovdee.skriptparticles.shapes.RadialShape; -import com.sovdee.skriptparticles.shapes.Shape; -import org.jetbrains.annotations.Nullable; - -public class ShapeTypes { - static { - // Shape - Classes.registerClass(new ClassInfo<>(Shape.class, "shape") - .user("shapes?") - .name("Shape") - .description("Represents an abstract particle shape. E.g. circle, line, etc.") - .parser(new Parser<>() { - - @Override - public Shape parse(String input, ParseContext context) { - return null; - } - - @Override - public boolean canParse(ParseContext context) { - return false; - } - - @Override - public String toString(Shape o, int flags) { - return o.toString(); - } - - @Override - public String toVariableNameString(Shape shape) { - return "shape:" + shape.getUUID(); - } - }) - .cloner(Shape::clone) - ); - - // RadialShape - Classes.registerClass(new ClassInfo<>(RadialShape.class, "radialshape") - .user("radial ?shapes?") - .name("Radial Shape") - .description("Represents an abstract particle shape that has a radius. E.g. circle, sphere, etc.") - .parser(new Parser<>() { - - @Override - public RadialShape parse(String input, ParseContext context) { - return null; - } - - @Override - public boolean canParse(ParseContext context) { - return false; - } - - @Override - public String toString(RadialShape o, int flags) { - return o.toString(); - } - - @Override - public String toVariableNameString(RadialShape shape) { - return "shape:" + shape.getUUID(); - } - }) - ); - - // LWHShape - Classes.registerClass(new ClassInfo<>(LWHShape.class, "lwhshape") - .user("lwh ?shapes?") - .name("Length/Width/Height Shape") - .description("Represents an abstract particle shape that has a length, width, and/or height. E.g. cube, cylinder, ellipse, etc.") - .parser(new Parser<>() { - - @Override - public LWHShape parse(String input, ParseContext context) { - return null; - } - - @Override - public boolean canParse(ParseContext context) { - return false; - } - - @Override - public String toString(LWHShape o, int flags) { - return o.toString(); - } - - @Override - public String toVariableNameString(LWHShape shape) { - return "shape:" + shape.getUUID(); - } - }) - - ); - - // CutoffShape - Classes.registerClass(new ClassInfo<>(CutoffShape.class, "cutoffshape") - .user("cutoff ?shapes?") - .name("Cutoff Shape") - .description("Represents an abstract particle shape that has a cutoff angle. E.g. arc, spherical cap, etc.") - .parser(new Parser<>() { - - @Override - public CutoffShape parse(String input, ParseContext context) { - return null; - } - - @Override - public boolean canParse(ParseContext context) { - return false; - } - - @Override - public String toString(CutoffShape o, int flags) { - return o.toString(); - } - - @Override - public String toVariableNameString(CutoffShape shape) { - return "shape:" + shape.getUUID(); - } - }) - ); - - // Polygonal Shape - Classes.registerClass(new ClassInfo<>(PolyShape.class, "polyshape") - .user("poly ?shapes?") - .name("Polygonal/Polyhedral Shape") - .description( - "Represents an abstract particle shape that is a polygon or polyhedron, with a side length and side count.\n" + - "Irregular shapes are included in this category, but do not support changing either side count or side length." - ) - .parser(new Parser<>() { - - @Override - public PolyShape parse(String input, ParseContext context) { - return null; - } - - @Override - public boolean canParse(ParseContext context) { - return false; - } - - @Override - public String toString(PolyShape o, int flags) { - return o.toString(); - } - - @Override - public String toVariableNameString(PolyShape shape) { - return "shape:" + shape.getUUID(); - } - }) - ); - - // Style - Classes.registerClass(new ClassInfo<>(Shape.Style.class, "shapestyle") - .user("shape ?styles?") - .name("Shape Style") - .description("Represents the way the shape is drawn. Outlined is a wireframe representation, Surface is filling in all the surfaces of the shape, and Filled is filling in the entire shape.") - .parser(new Parser<>() { - @Override - public @Nullable Shape.Style parse(String s, ParseContext context) { - s = s.toUpperCase(); - if (s.matches("OUTLINE(D)?") || s.matches("WIREFRAME")) { - return Shape.Style.OUTLINE; - } else if (s.matches("SURFACE") || s.matches("HOLLOW")) { - return Shape.Style.SURFACE; - } else if (s.matches("FILL(ED)?") || s.matches("SOLID")) { - return Shape.Style.FILL; - } - return null; - } - - @Override - public boolean canParse(ParseContext context) { - return true; - } - - @Override - public String toString(Shape.Style style, int i) { - return style.toString(); - } - - @Override - public String toVariableNameString(Shape.Style style) { - return "shapestyle:" + style; - } - })); - } -} diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/AbstractShape.java b/src/main/java/com/sovdee/skriptparticles/shapes/AbstractShape.java deleted file mode 100644 index cea25e9..0000000 --- a/src/main/java/com/sovdee/skriptparticles/shapes/AbstractShape.java +++ /dev/null @@ -1,395 +0,0 @@ -package com.sovdee.skriptparticles.shapes; - -import ch.njol.skript.Skript; -import com.sovdee.skriptparticles.particles.Particle; -import com.sovdee.skriptparticles.particles.ParticleGradient; -import com.sovdee.skriptparticles.util.DynamicLocation; -import com.sovdee.skriptparticles.util.MathUtil; -import com.sovdee.skriptparticles.util.ParticleUtil; -import com.sovdee.skriptparticles.util.Quaternion; -import org.bukkit.entity.Player; -import org.bukkit.scheduler.BukkitRunnable; -import org.bukkit.util.Vector; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.jetbrains.annotations.Contract; -import org.joml.Quaternionf; - -import java.util.Collection; -import java.util.Comparator; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; -import java.util.UUID; -import java.util.function.Consumer; - -public abstract class AbstractShape implements Shape { - - private final UUID uuid; - private Set points; - - private Style style; - private final Quaternion orientation; - private final Quaternion lastOrientation; - private double scale; - private Vector offset; - private @Nullable DynamicLocation location; - private Particle particle; - private @Nullable Comparator ordering; - private double particleDensity = 0.25; // todo: make this configurable - private long animationDuration = 0; - - private State lastState; - private @Nullable DynamicLocation lastLocation; - private boolean needsUpdate = false; - - private boolean drawLocalAxes = false; - private boolean drawGlobalAxes = false; - - public AbstractShape() { - this.style = Style.OUTLINE; - this.points = new LinkedHashSet<>(); - - this.orientation = Quaternion.IDENTITY.clone(); - this.lastOrientation = Quaternion.IDENTITY.clone(); - this.scale = 1; - this.offset = new Vector(0, 0, 0); - - this.particle = (Particle) new Particle(org.bukkit.Particle.FLAME).parent(this).extra(0); - - this.uuid = UUID.randomUUID(); - - this.lastState = getState(); - } - - @Override - public Set getPoints() { - return getPoints(this.orientation); - } - - @Override - public Set getPoints(Quaternion orientation) { - State state = getState(orientation); - if (needsUpdate || !lastState.equals(state) || points.isEmpty()) { - if (ordering != null) - points = new TreeSet<>(ordering); - else - points = new LinkedHashSet<>(); - generatePoints(points); - for (Vector point : points) { - orientation.transform(point); - point.multiply(scale); - point.add(offset); - } - lastState = state; - needsUpdate = false; - } - return points; - } - - @Override - public void setPoints(Set points) { - this.points = points; - } - - @Override - @Contract(pure = true) - public void generateSurface(Set points) { - generateOutline(points); - } - - @Override - @Contract(pure = true) - public void generateFilled(Set points) { - generateSurface(points); - } - - @Override - public void draw(Collection recipients) { - assert location != null; - draw(location, Quaternion.IDENTITY, particle, recipients); - } - - @Override - public void draw(DynamicLocation location, Collection recipients) { - draw(location, Quaternion.IDENTITY, particle, recipients); - } - - @Override - public void draw(DynamicLocation location, Quaternion baseOrientation, Particle particle, Collection recipients) { - // cache the last location and orientation used to draw the shape - if (location.isNull()) { - if (this.location == null) - return; - location = this.location.clone(); - } - - lastLocation = location.clone(); - lastOrientation.set(baseOrientation.clone().mul(orientation)); - - // If the particle doesn't override the shape's particle, use the shape's particle - if (!particle.override()) { - this.particle.parent(this); - particle = this.particle; - // update the gradient if needed - @Nullable ParticleGradient gradient = particle.gradient(); - if (gradient != null && gradient.isLocal()) - gradient.setOrientation(lastOrientation); - } - - particle.receivers(recipients); - Collection toDraw = getPoints(lastOrientation); - if (animationDuration > 0) { - int particleCount = toDraw.size(); - double millisecondsPerPoint = animationDuration / (double) particleCount; - Iterator> batchIterator = MathUtil.batch(toDraw, millisecondsPerPoint).iterator(); - Particle finalParticle = particle; - BukkitRunnable runnable = new BukkitRunnable() { - @Override - public void run() { - if (!batchIterator.hasNext()) { - this.cancel(); - return; - } - List batch = batchIterator.next(); - try { - for (Vector point : batch) { - finalParticle.spawn(point); - } - } catch (IllegalArgumentException e) { - Skript.error("Failed to spawn particle! Error: " + e.getMessage()); - } - } - }; - runnable.runTaskTimerAsynchronously(Skript.getInstance(), 0, 1); - } else { - // no animation needed, draw all particles at once - for (Vector point :toDraw) { - try { - particle.spawn(point); - } catch (IllegalArgumentException e) { - Skript.error("Failed to spawn particle! Error: " + e.getMessage()); - return; - } - } - } - - if (drawLocalAxes) { - ParticleUtil.drawAxes(location.getLocation().add(offset), lastOrientation, recipients); - } - if (drawGlobalAxes) { - ParticleUtil.drawAxes(location.getLocation().add(offset), Quaternion.IDENTITY, recipients); - } - } - - @Override - public void draw(DynamicLocation location, Consumer consumer, Collection recipients) { - consumer.accept(this); - draw(location, this.orientation, this.particle, recipients); - } - - @Override - public Vector getRelativeXAxis(boolean useLastOrientation) { - return (useLastOrientation ? lastOrientation : orientation).transform(new Vector(1, 0, 0)); - } - - @Override - public Vector getRelativeYAxis(boolean useLastOrientation) { - return (useLastOrientation ? lastOrientation : orientation).transform(new Vector(0, 1, 0)); - } - - @Override - public Vector getRelativeZAxis(boolean useLastOrientation) { - return (useLastOrientation ? lastOrientation : orientation).transform(new Vector(0, 0, 1)); - } - - @Override - public void showLocalAxes(boolean show) { - drawLocalAxes = show; - } - - @Override - public boolean showLocalAxes() { - return drawLocalAxes; - } - - @Override - public void showGlobalAxes(boolean show) { - drawGlobalAxes = show; - } - - @Override - public boolean showGlobalAxes() { - return drawGlobalAxes; - } - - @Override - @Nullable - public DynamicLocation getLastLocation() { - return lastLocation; - } - - @Override - public Style getStyle() { - return style; - } - - @Override - public void setStyle(Style style) { - this.style = style; - this.setNeedsUpdate(true); - } - - @Override - public Quaternion getOrientation() { - return new Quaternion(orientation); - } - - @Override - public void setOrientation(Quaternionf orientation) { - this.orientation.set(orientation); - this.setNeedsUpdate(true); - } - - @Override - public double getScale() { - return scale; - } - - @Override - public void setScale(double scale) { - this.scale = scale; - this.setNeedsUpdate(true); - } - - @Override - public Vector getOffset() { - return offset.clone(); - } - - @Override - public void setOffset(Vector offset) { - this.offset = offset; - this.setNeedsUpdate(true); - } - - @Override - @Nullable - public DynamicLocation getLocation() { - if (location == null) - return null; - return location.clone(); - } - - @Override - public void setLocation(DynamicLocation location) { - this.location = location; - } - - @Override - public UUID getUUID() { - return uuid; - } - - @Override - public Particle getParticle() { - return particle.clone(); - } - - @Override - public void setParticle(Particle particle) { - this.particle = particle; - } - - @Override - public @Nullable Comparator getOrdering() { - return ordering; - } - - @Override - public void setOrdering(Comparator comparator) { - ordering = comparator; - this.setNeedsUpdate(true); - } - - @Override - public double getParticleDensity() { - return particleDensity; - } - - - @Override - public void setParticleDensity(double particleDensity) { - this.particleDensity = Math.max(particleDensity, MathUtil.EPSILON); - this.setNeedsUpdate(true); - } - - @Override - public int getParticleCount() { - return getPoints().size(); - } - - @Override - public boolean needsUpdate() { - return needsUpdate; - } - - @Override - public void setNeedsUpdate(boolean needsUpdate) { - this.needsUpdate = needsUpdate; - } - - @Override - public long getAnimationDuration() { - return animationDuration; - } - - @Override - public void setAnimationDuration(long animationDuration) { - this.animationDuration = animationDuration; - } - - @Contract("-> new") - public abstract Shape clone(); - - @Override - @Contract("_ -> param1") - public Shape copyTo(Shape shape) { - shape.setOrientation(this.orientation); - shape.setScale(this.scale); - shape.setOffset(this.offset.clone()); - shape.setParticle(this.particle.clone()); - if (this.location != null) - shape.setLocation(this.location.clone()); - shape.setParticleDensity(this.particleDensity); - shape.setStyle(this.style); - shape.showLocalAxes(this.drawLocalAxes); - shape.showGlobalAxes(this.drawGlobalAxes); - shape.setAnimationDuration(this.animationDuration); - shape.setOrdering(this.ordering); - // ensure that the shape's points are updated, so we don't have to recalculate them unless we change the copy. - shape.setPoints(this.getPoints()); - shape.setNeedsUpdate(this.needsUpdate); - shape.setLastState(this.lastState); - return shape; - } - - @Override - @Contract("-> new") - public State getState() { - return new State(style, orientation.hashCode(), scale, offset.hashCode(), particleDensity); - } - - @Override - @Contract("_ -> new") - public State getState(Quaternion orientation) { - return new State(style, orientation.hashCode(), scale, offset.hashCode(), particleDensity); - } - - @Override - public void setLastState(State state) { - this.lastState = state; - } - -} diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/Arc.java b/src/main/java/com/sovdee/skriptparticles/shapes/Arc.java deleted file mode 100644 index d63df2e..0000000 --- a/src/main/java/com/sovdee/skriptparticles/shapes/Arc.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.sovdee.skriptparticles.shapes; - -import com.sovdee.skriptparticles.util.MathUtil; -import org.bukkit.util.Vector; -import org.jetbrains.annotations.Contract; - -import java.util.Set; - -/** - * A circle with a cutoff angle. This creates an arc or a sector. - * The cutoff angle is the angle between the start and end of the arc. - * It will always be between 0 and 2 * PI. - */ -public class Arc extends Circle implements CutoffShape { - - /** - * @param radius The radius of the arc. Must be greater than 0. - * @param cutoffAngle The cutoff angle of the arc, in radians. Will be clamped to a value between 0 and 2 * PI. - */ - public Arc(double radius, double cutoffAngle) { - super(radius); - this.cutoffAngle = MathUtil.clamp(cutoffAngle, 0, Math.PI * 2); - } - - /** - * @param radius The radius of the arc. Must be greater than 0. - * @param height The height of the arc. Must be non-negative. - * @param cutoffAngle The cutoff angle of the arc, in radians. Will be clamped to a value between 0 and 2 * PI. - */ - public Arc(double radius, double height, double cutoffAngle) { - super(radius, height); - this.cutoffAngle = MathUtil.clamp(cutoffAngle, 0, Math.PI * 2); - } - - @Override - @Contract(pure = true) - public void generateSurface(Set points) { - generateFilled(points); - } - - @Override - public double getCutoffAngle() { - return this.cutoffAngle; - } - - @Override - public void setCutoffAngle(double cutoffAngle) { - this.cutoffAngle = MathUtil.clamp(cutoffAngle, 0, Math.PI * 2); - this.setNeedsUpdate(true); - } - - @Override - @Contract("-> new") - public Shape clone() { - return this.copyTo(new Arc(this.getRadius(), this.getHeight(), cutoffAngle)); - } - - @Override - public String toString() { - return "Arc{" + - "radius=" + this.getRadius() + - ", cutoffAngle=" + cutoffAngle + - ", height=" + this.getHeight() + - '}'; - } -} diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/BezierCurve.java b/src/main/java/com/sovdee/skriptparticles/shapes/BezierCurve.java deleted file mode 100644 index f67150a..0000000 --- a/src/main/java/com/sovdee/skriptparticles/shapes/BezierCurve.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.sovdee.skriptparticles.shapes; - -import com.sovdee.skriptparticles.util.DynamicLocation; -import com.sovdee.skriptparticles.util.Point; -import com.sovdee.skriptparticles.util.Quaternion; -import org.bukkit.Location; -import org.bukkit.util.Vector; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.jetbrains.annotations.Contract; - -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -public class BezierCurve extends AbstractShape { - - private Point start; - private Point end; - private List> controlPoints; - - private boolean isDynamic; - - /** - * Creates a new line shape with the start point at the origin and the end point at the given vector. - * The vector cannot be the origin. - * @param end the end point of the line - * @throws IllegalArgumentException if the end vector is the origin - */ - - public BezierCurve(Point start, Point end, List> controlPoints) { - this.start = start; - if (start.getType() != Vector.class) - this.setLocation(start.getDynamicLocation()); - this.end = end; - this.controlPoints = new ArrayList<>(controlPoints); - isDynamic = true; - } - - public BezierCurve(BezierCurve curve) { - this.start = curve.getStart(); - this.end = curve.getEnd(); - this.controlPoints = new ArrayList<>(curve.getControlPoints()); - isDynamic = curve.isDynamic; - } - - @Override - @Contract(pure = true) - public Set getPoints(Quaternion orientation) { - Set points = super.getPoints(orientation); - if (isDynamic) - // Ensure that the points are always needing to be updated if the start or end location is dynamic - this.setNeedsUpdate(true); - return points; - } - - private List evaluateControlPoints() { - List controlPoints = new ArrayList<>(); - @Nullable Location origin = start.getLocation(); - controlPoints.add(start.getVector(origin)); - for (Point controlPoint : this.controlPoints) - controlPoints.add(controlPoint.getVector(origin)); - controlPoints.add(end.getVector(origin)); - return controlPoints; - } - - @SuppressWarnings("ConstantConditions") - @Override - public void generateOutline(Set points) { - List controlPoints = evaluateControlPoints(); - - int steps = (int) (estimateLength(controlPoints) / getParticleDensity()); - - for (double step = 0; step < steps; step++) { - double t = step / steps; - double nt = 1 - t; - List tempCP = new ArrayList<>(controlPoints); - while (tempCP.size() > 1) { - for (int i = 0; i < tempCP.size() - 1; i++) - tempCP.set(i, tempCP.get(i).clone().multiply(nt).add(tempCP.get(i + 1).clone().multiply(t))); - tempCP.remove(tempCP.size()-1); - } - points.add(tempCP.get(0)); - } - } - - private double estimateLength() { - return estimateLength(evaluateControlPoints()); - } - private double estimateLength(List controlPoints) { - double dist = 0; - for (int i = 0; i < controlPoints.size()-1; i++) { - dist += controlPoints.get(i).distance(controlPoints.get(i+1)); - } - return dist; - } - - @Override - public void setParticleCount(int particleCount) { - particleCount = Math.max(particleCount, 1); - this.setParticleDensity(estimateLength() / particleCount); - this.setNeedsUpdate(true); - } - - public Point getStart() { - return start; - } - - public Point getEnd() { - return end; - } - - public List> getControlPoints() { - return controlPoints; - } - - @Override - public Shape clone() { - return this.copyTo(new BezierCurve(this)); - } -} diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/Cuboid.java b/src/main/java/com/sovdee/skriptparticles/shapes/Cuboid.java deleted file mode 100644 index 5f672de..0000000 --- a/src/main/java/com/sovdee/skriptparticles/shapes/Cuboid.java +++ /dev/null @@ -1,254 +0,0 @@ -package com.sovdee.skriptparticles.shapes; - -import com.sovdee.skriptparticles.util.DynamicLocation; -import com.sovdee.skriptparticles.util.MathUtil; -import com.sovdee.skriptparticles.util.Quaternion; -import org.bukkit.Location; -import org.bukkit.util.Vector; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; - -import java.util.Set; - -/* - * A cuboid shape, defined either by two corners or by length, width, and height. - * Cuboids utilising the {@link DynamicLocation} constructor do not require a location to be drawn at and will - * automatically update their positions when drawn. - */ -public class Cuboid extends AbstractShape implements LWHShape { - - private double halfLength, halfWidth, halfHeight; - private double lengthStep, widthStep, heightStep; - private Vector centerOffset = new Vector(0, 0, 0); - private @Nullable DynamicLocation negativeCorner, positiveCorner; - private boolean isDynamic = false; - - /** - * Creates a cuboid with the given length, width, and height. - * @param length The length of the cuboid. Must be greater than 0. - * @param width The width of the cuboid. Must be greater than 0. - * @param height The height of the cuboid. Must be greater than 0. - */ - public Cuboid(double length, double width, double height) { - super(); - - this.halfWidth = Math.max(width / 2, MathUtil.EPSILON); - this.halfLength = Math.max(length / 2, MathUtil.EPSILON); - this.halfHeight = Math.max(height / 2, MathUtil.EPSILON); - calculateSteps(); - } - - /** - * Creates a cuboid from the given corner vectors. Using asymmetric vectors will result in a cuboid that is offset from the origin of the shape. - *

- * For example, using (0, 0, 0) and (1, 1, 1) and drawing the cuboid at (2, 0, 2) will result - * in the negative corner being drawn at (2, 0, 2) and the positive corner being at (3, 1, 3). - * - * @param cornerA A vector from the origin of the shape to the first corner. - * @param cornerB A vector from the origin of the shape to the second corner. - * @throws IllegalArgumentException If the given vectors are equal. - */ - public Cuboid(Vector cornerA, Vector cornerB) { - super(); - if (cornerA.equals(cornerB)) - throw new IllegalArgumentException("Cuboid corners cannot be equal."); - this.halfLength = Math.abs(cornerB.getX() - cornerA.getX()) / 2; - this.halfWidth = Math.abs(cornerB.getZ() - cornerA.getZ()) / 2; - this.halfHeight = Math.abs(cornerB.getY() - cornerA.getY()) / 2; - centerOffset = cornerB.clone().add(cornerA).multiply(0.5); - calculateSteps(); - } - - /** - * Creates a cuboid from the given corner locations. Unlike the vector constructor, this constructor will not offset the cuboid from the origin of the shape. - * When drawn with the default location, the visual corner locations will be at the given corners. If the cuboid is drawn at a different location, the - * shape's length, width, and height will be dependent on the given corners, but the shape will be drawn at the given location instead. - * - * @param cornerA The first corner of the cuboid. - * @param cornerB The second corner of the cuboid. - * @throws IllegalArgumentException If the given locations are equal. - */ - public Cuboid(DynamicLocation cornerA, DynamicLocation cornerB) { - super(); - if (cornerA.equals(cornerB)) - throw new IllegalArgumentException("Cuboid corners cannot be equal."); - Location cornerALocation = cornerA.getLocation(); - Location cornerBLocation = cornerB.getLocation(); - if (cornerALocation.equals(cornerBLocation)) - throw new IllegalArgumentException("Cuboid corners cannot be equal."); - - if (cornerA.isDynamic() || cornerB.isDynamic()) { - this.negativeCorner = cornerA.clone(); - this.positiveCorner = cornerB.clone(); - isDynamic = true; - } else { - this.halfLength = Math.abs(cornerBLocation.getX() - cornerALocation.getX()) / 2; - this.halfWidth = Math.abs(cornerBLocation.getZ() - cornerALocation.getZ()) / 2; - this.halfHeight = Math.abs(cornerBLocation.getY() - cornerALocation.getY()) / 2; - } - this.setLocation(new DynamicLocation(cornerALocation.clone().add(cornerBLocation.subtract(cornerALocation).toVector().multiply(0.5)))); - calculateSteps(); - } - - /** - * Calculates the step size for each axis based on the given particle density. - * todo: Use config option to toggle between adaptive and fixed step size. - */ - private void calculateSteps() { - widthStep = 2 * halfWidth / Math.round(2 * halfWidth / this.getParticleDensity()); - lengthStep = 2 * halfLength / Math.round(2 * halfLength / this.getParticleDensity()); - heightStep = 2 * halfHeight / Math.round(2 * halfHeight / this.getParticleDensity()); - } - - @SuppressWarnings("ConstantConditions") - @Override - @Contract(pure = true) - public void generateOutline(Set points) { - for (double x = -halfLength; x <= halfLength; x += lengthStep) { - points.add(new Vector(x, -halfHeight, -halfWidth)); - points.add(new Vector(x, -halfHeight, halfWidth)); - points.add(new Vector(x, halfHeight, -halfWidth)); - points.add(new Vector(x, halfHeight, halfWidth)); - } - for (double y = -halfHeight + heightStep; y < halfHeight; y += heightStep) { - points.add(new Vector(-halfLength, y, -halfWidth)); - points.add(new Vector(-halfLength, y, halfWidth)); - points.add(new Vector(halfLength, y, -halfWidth)); - points.add(new Vector(halfLength, y, halfWidth)); - } - for (double z = -halfWidth + widthStep; z < halfWidth; z += widthStep) { - points.add(new Vector(-halfLength, -halfHeight, z)); - points.add(new Vector(-halfLength, halfHeight, z)); - points.add(new Vector(halfLength, -halfHeight, z)); - points.add(new Vector(halfLength, halfHeight, z)); - } - } - - @SuppressWarnings("ConstantConditions") - @Override - @Contract(pure = true) - public void generateSurface(Set points) { - for (double x = -halfLength; x <= halfLength; x += lengthStep) { - for (double z = -halfWidth; z <= halfWidth; z += widthStep) { - points.add(new Vector(x, -halfHeight, z)); - points.add(new Vector(x, halfHeight, z)); - } - } - for (double y = -halfHeight + heightStep; y < halfHeight; y += heightStep) { - for (double z = -halfWidth; z <= halfWidth; z += widthStep) { - points.add(new Vector(-halfLength, y, z)); - points.add(new Vector(halfLength, y, z)); - } - } - for (double x = -halfLength + lengthStep; x < halfLength; x += lengthStep) { - for (double y = -halfHeight + heightStep; y < halfHeight; y += heightStep) { - points.add(new Vector(x, y, -halfWidth)); - points.add(new Vector(x, y, halfWidth)); - } - } - } - - @SuppressWarnings("ConstantConditions") - @Override - @Contract(pure = true) - public void generateFilled(Set points) { - for (double x = -halfLength; x <= halfLength; x += lengthStep) { - for (double y = -halfHeight; y <= halfHeight; y += heightStep) { - for (double z = -halfWidth; z <= halfWidth; z += widthStep) { - points.add(new Vector(x, y, z)); - } - } - } - } - - @Override - @Contract(pure = true) - public void generatePoints(Set points) { - if (isDynamic) { - assert negativeCorner != null; - assert positiveCorner != null; - Location negative = negativeCorner.getLocation(); - Location positive = positiveCorner.getLocation(); - this.halfLength = Math.abs(positive.getX() - negative.getX()) / 2; - this.halfWidth = Math.abs(positive.getZ() - negative.getZ()) / 2; - this.halfHeight = Math.abs(positive.getY() - negative.getY()) / 2; - this.setLocation(new DynamicLocation(negative.clone().add(positive.subtract(negative).toVector().multiply(0.5)))); - } - calculateSteps(); - super.generatePoints(points); - points.forEach(vector -> vector.add(centerOffset)); - } - - // Ensure that the points are always needing to be updated if the start or end location is dynamic - @Override - public Set getPoints(@NonNull @NotNull Quaternion orientation) { - Set points = super.getPoints(orientation); - if (isDynamic) - this.setNeedsUpdate(true); - return points; - } - - @Override - public void setParticleCount(int particleCount) { - particleCount = Math.max(1, particleCount); - this.setParticleDensity(switch (this.getStyle()) { - case OUTLINE -> 8 * (halfLength + halfHeight + halfWidth) / particleCount; - case SURFACE -> - Math.sqrt(8 * (halfLength * halfHeight + halfLength * halfWidth + halfHeight * halfWidth) / particleCount); - case FILL -> Math.cbrt(8 * halfLength * halfHeight * halfWidth / particleCount); - }); - calculateSteps(); - this.setNeedsUpdate(true); - } - - @Override - public double getLength() { - return halfLength * 2; - } - - @Override - public void setLength(double length) { - this.halfLength = Math.max(length / 2, MathUtil.EPSILON); - this.setNeedsUpdate(true); - } - - @Override - public double getWidth() { - return halfWidth * 2; - } - - @Override - public void setWidth(double width) { - this.halfWidth = Math.max(width / 2, MathUtil.EPSILON); - this.setNeedsUpdate(true); - } - - @Override - public double getHeight() { - return halfHeight * 2; - } - - @Override - public void setHeight(double height) { - this.halfHeight = Math.max(height / 2, MathUtil.EPSILON); - this.setNeedsUpdate(true); - } - - @Override - @Contract("-> new") - public Shape clone() { - Cuboid cuboid; - if (isDynamic) { - assert negativeCorner != null; - assert positiveCorner != null; - cuboid = (new Cuboid(negativeCorner, positiveCorner)); - } else { - cuboid = (new Cuboid(getLength(), getWidth(), getHeight())); - } - cuboid.isDynamic = isDynamic; - return this.copyTo(cuboid); - } - -} diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/CutoffShape.java b/src/main/java/com/sovdee/skriptparticles/shapes/CutoffShape.java deleted file mode 100644 index 9291658..0000000 --- a/src/main/java/com/sovdee/skriptparticles/shapes/CutoffShape.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.sovdee.skriptparticles.shapes; - -/** - * Represents a shape that has a cutoff angle, like an arc. - */ -public interface CutoffShape extends Shape { - - /** - * Gets the cutoff angle of the shape, or the angle at which the shape will stop generating particles. - * - * @return The cutoff angle of the shape in radians, between 0 and 2 * PI. - */ - double getCutoffAngle(); - - /** - * Sets the cutoff angle of the shape, or the angle at which the shape will stop generating particles. - * - * @param cutoffAngle The cutoff angle of the shape, in radians. Will be converted to a value between 0 and 2 * PI. - */ - void setCutoffAngle(double cutoffAngle); - -} diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/EllipticalArc.java b/src/main/java/com/sovdee/skriptparticles/shapes/EllipticalArc.java deleted file mode 100644 index 8c5b2ed..0000000 --- a/src/main/java/com/sovdee/skriptparticles/shapes/EllipticalArc.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.sovdee.skriptparticles.shapes; - -import com.sovdee.skriptparticles.util.MathUtil; -import org.bukkit.util.Vector; -import org.jetbrains.annotations.Contract; - -import java.util.Set; - -/** - * A shape that is a section of an ellipse. An arc, but an ellipse instead of a circle. - * The radii must be greater than 0, and the height must be non-negative. - * The cutoff angle will always be between 0 and 2π. - */ -public class EllipticalArc extends Ellipse implements CutoffShape { - - /** - * Creates an elliptical arc with the given x radius, z radius, and cutoff angle. - * The height will be 0. - * - * @param xRadius the x radius. Must be greater than 0. - * @param zRadius the z radius. Must be greater than 0. - * @param cutoffAngle the cutoff angle, in radians. will be clamped to between 0 and 2π. - */ - public EllipticalArc(double xRadius, double zRadius, double cutoffAngle) { - this(xRadius, zRadius, 0, cutoffAngle); - } - - /** - * Creates an elliptical arc with the given x radius, z radius, height, and cutoff angle. - * - * @param xRadius the x radius. Must be greater than 0. - * @param zRadius the z radius. Must be greater than 0. - * @param height the height. Must be non-negative. - * @param cutoffAngle the cutoff angle, in radians. will be clamped to between 0 and 2π. - */ - public EllipticalArc(double xRadius, double zRadius, double height, double cutoffAngle) { - super(xRadius, zRadius, height); - this.cutoffAngle = MathUtil.clamp(cutoffAngle, 0, Math.PI * 2); - } - - @Override - @Contract(pure = true) - public void generateSurface(Set points) { - generateFilled(points); - } - - @Override - public double getCutoffAngle() { - return cutoffAngle; - } - - @Override - public void setCutoffAngle(double cutoffAngle) { - this.cutoffAngle = MathUtil.clamp(cutoffAngle, 0, Math.PI * 2); - this.setNeedsUpdate(true); - } - - @Override - @Contract("-> new") - public Shape clone() { - return this.copyTo(new EllipticalArc(this.getLength(), this.getWidth(), this.getHeight(), cutoffAngle)); - } -} diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/Heart.java b/src/main/java/com/sovdee/skriptparticles/shapes/Heart.java deleted file mode 100644 index 425aaf6..0000000 --- a/src/main/java/com/sovdee/skriptparticles/shapes/Heart.java +++ /dev/null @@ -1,115 +0,0 @@ -package com.sovdee.skriptparticles.shapes; - -import com.sovdee.skriptparticles.util.MathUtil; -import org.bukkit.util.Vector; -import org.jetbrains.annotations.Contract; - -import java.util.LinkedHashSet; -import java.util.Set; - -/** - * Represents a heart shape with a length, width, and eccentricity. - * The length and width must be greater than 0, and the eccentricity must be greater than 1. Values of 3 or so are recommended. - * The eccentricity determines how much the heart is "pointed" at the bottom. - */ -public class Heart extends AbstractShape implements LWHShape { - - private double length; - private double width; - private double eccentricity; - - /** - * Creates a heart with the given length, width, and eccentricity. - * - * @param length the length. Must be greater than 0. - * @param width the width. Must be greater than 0. - * @param eccentricity the eccentricity. Must be greater than 1. - */ - public Heart(double length, double width, double eccentricity) { - super(); - this.length = Math.max(length, MathUtil.EPSILON); - this.width = Math.max(width, MathUtil.EPSILON); - this.eccentricity = Math.max(eccentricity, 1); - } - - @SuppressWarnings("ConstantConditions") - @Override - @Contract(pure = true) - public void generateOutline(Set points) { - points.addAll(MathUtil.calculateHeart(length / 2, width / 2, eccentricity, this.getParticleDensity())); - } - - @SuppressWarnings("ConstantConditions") - @Override - @Contract(pure = true) - public void generateSurface(Set points) { - double particleDensity = this.getParticleDensity(); - for (double w = width, l = length; w > 0 && l > 0; w -= particleDensity * 1.5, l -= particleDensity * 1.5) { - points.addAll(MathUtil.calculateHeart(l / 2, w / 2, eccentricity, particleDensity)); - } - } - - @Override - public void setParticleCount(int particleCount) { - // intentionally empty - } - - @Override - public double getHeight() { - return 0; - } - - @Override - public void setHeight(double height) { - // intentionally empty - } - - @Override - public double getWidth() { - return width; - } - - @Override - public void setWidth(double width) { - this.width = Math.max(width, MathUtil.EPSILON); - this.setNeedsUpdate(true); - } - - @Override - public double getLength() { - return length; - } - - @Override - public void setLength(double length) { - this.length = Math.max(length, MathUtil.EPSILON); - this.setNeedsUpdate(true); - } - - /** - * Gets the eccentricity of this heart. - * The eccentricity determines how much the heart is "pointed" at the bottom. - * - * @return the eccentricity - */ - public double getEccentricity() { - return eccentricity; - } - - /** - * Sets the eccentricity of this heart. - * The eccentricity determines how much the heart is "pointed" at the bottom. - * - * @param eccentricity the eccentricity. Must be greater than 1. - */ - public void setEccentricity(double eccentricity) { - this.eccentricity = Math.max(1, eccentricity); - this.setNeedsUpdate(true); - } - - @Override - @Contract("-> new") - public Shape clone() { - return this.copyTo(new Heart(length, width, eccentricity)); - } -} diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/Helix.java b/src/main/java/com/sovdee/skriptparticles/shapes/Helix.java deleted file mode 100644 index f960591..0000000 --- a/src/main/java/com/sovdee/skriptparticles/shapes/Helix.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.sovdee.skriptparticles.shapes; - -import com.sovdee.skriptparticles.util.MathUtil; -import org.bukkit.util.Vector; -import org.jetbrains.annotations.Contract; - -import java.util.LinkedHashSet; -import java.util.Set; - -/** - * A helix is a spiral that is generated by rotating a line around a central axis. - * The slope of the helix is the rise of the helix per radian of rotation. - * The radius of the helix is the distance from the central point to the line. - * The height of the helix is the distance from the start of the line to the end of the line. - * The direction of the helix is the direction of rotation. - */ -public class Helix extends AbstractShape implements RadialShape, LWHShape { - - private double radius; - private double height; - private double slope; - private int direction = 1; - - /** - * Creates a helix with the given radius, height, and slope. Defaults to a clockwise rotation. - * @param radius the radius of the helix. Must be greater than 0. - * @param height the height of the helix. Must be greater than 0. - * @param slope the slope of the helix. Must be greater than 0. - */ - public Helix(double radius, double height, double slope) { - super(); - this.radius = Math.max(radius, MathUtil.EPSILON); - this.height = Math.max(height, MathUtil.EPSILON); - this.slope = Math.max(slope, MathUtil.EPSILON); - } - - /** - * Creates a helix with the given radius, height, slope, and direction. - * @param radius the radius of the helix. Must be greater than 0. - * @param height the height of the helix. Must be greater than 0. - * @param slope the slope of the helix. Must be greater than 0. - * @param direction the direction of the helix. Must be 1 or -1. - * @throws IllegalArgumentException if the direction is not 1 or -1. - */ - public Helix(double radius, double height, double slope, int direction) { - super(); - this.radius = Math.max(radius, MathUtil.EPSILON); - this.height = Math.max(height, MathUtil.EPSILON); - this.slope = Math.max(slope, MathUtil.EPSILON); - if (direction != 1 && direction != -1) - throw new IllegalArgumentException("Direction must be 1 or -1"); - this.direction = direction; - } - - @SuppressWarnings("ConstantConditions") - @Override - @Contract(pure = true) - public void generateOutline(Set points) { - points.addAll(MathUtil.calculateHelix(radius, height, slope, direction, this.getParticleDensity())); - } - - @SuppressWarnings("ConstantConditions") - @Override - @Contract(pure = true) - public void generateSurface(Set points) { - double particleDensity = this.getParticleDensity(); - for (double r = radius; r > 0; r -= particleDensity) { - points.addAll(MathUtil.calculateHelix(r, height, slope, direction, particleDensity)); - } - } - - @Override - public void setParticleCount(int particleCount) { - particleCount = Math.max(particleCount, 1); - this.setParticleDensity(switch (this.getStyle()) { - case OUTLINE -> (Math.sqrt(slope * slope + radius * radius) * (height / slope) / particleCount); - case FILL, SURFACE -> Math.sqrt(slope * slope + radius * radius * (height / slope) / particleCount); - }); - } - - /** - * The slope of the helix is the rise of the helix per radian of rotation. - * @return the slope of the helix. Always greater than 0. - */ - public double getSlope() { - return slope; - } - - /** - * The slope of the helix is the rise of the helix per radian of rotation. - * @param slope the slope of the helix. Must be greater than 0. - */ - public void setSlope(double slope) { - this.slope = Math.max(slope, MathUtil.EPSILON); - this.setNeedsUpdate(true); - } - - /** - * The direction of the helix is the direction of rotation. - * @return the direction of the helix. 1 for clockwise, -1 for counterclockwise. - */ - public int getDirection() { - return direction; - } - - /** - * The direction of the helix is the direction of rotation. - * @param direction the direction of the helix. 1 for clockwise, -1 for counterclockwise. - * @throws IllegalArgumentException if the direction is not 1 or -1. - */ - public void setDirection(int direction) { - if (direction != 1 && direction != -1) - throw new IllegalArgumentException("Direction must be 1 or -1"); - this.direction = direction; - this.setNeedsUpdate(true); - } - - @Override - public double getLength() { - return height; - } - - @Override - public void setLength(double length) { - height = Math.max(length, MathUtil.EPSILON); - this.setNeedsUpdate(true); - } - - @Override - public double getWidth() { - return 0; - } - - @Override - public void setWidth(double width) { - // intentionally empty - } - - @Override - public double getHeight() { - return height; - } - - @Override - public void setHeight(double height) { - this.height = Math.max(height, MathUtil.EPSILON); - this.setNeedsUpdate(true); - } - - @Override - public double getRadius() { - return radius; - } - - @Override - public void setRadius(double radius) { - this.radius = Math.max(radius, MathUtil.EPSILON); - this.setNeedsUpdate(true); - } - - @Override - @Contract("-> new") - public Shape clone() { - return this.copyTo(new Helix(radius, height, slope, direction)); - } -} diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/IrregularPolygon.java b/src/main/java/com/sovdee/skriptparticles/shapes/IrregularPolygon.java deleted file mode 100644 index a9224e0..0000000 --- a/src/main/java/com/sovdee/skriptparticles/shapes/IrregularPolygon.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.sovdee.skriptparticles.shapes; - -import com.sovdee.skriptparticles.util.MathUtil; -import org.bukkit.util.Vector; -import org.jetbrains.annotations.Contract; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -/** - * A polygon with an arbitrary number of vertices. The polygon is assumed to be parallel to the xz plane. - * The height of the polygon is the distance from the lowest vertex to the highest vertex, or the given height from the lowest vertex. - */ -public class IrregularPolygon extends AbstractShape implements LWHShape { - - private final List vertices; - private double height; - - /** - * Creates a polygon with the given vertices. The polygon is assumed to be parallel to the xz plane. - * The height of the polygon is the distance from the lowest vertex to the highest vertex. - * @param vertices the vertices of the polygon. Must be greater than 2. - * @throws IllegalArgumentException if the vertices are less than 3. - */ - public IrregularPolygon(Collection vertices) { - super(); - if (vertices.size() < 3) - throw new IllegalArgumentException("A polygon must have at least 3 vertices."); - setBounds(vertices); - this.vertices = flattenVertices(vertices); - } - - /** - * Creates a polygon with the given vertices and height. The polygon is assumed to be parallel to the xz plane. - * @param vertices the vertices of the polygon. Must be greater than 2. - * @param height the height of the polygon. Must be non-negative. - * @throws IllegalArgumentException if the vertices are less than 3. - */ - public IrregularPolygon(Collection vertices, double height) { - this(vertices); - this.height = Math.max(height, 0); - } - - /** - * Flattens the vertices to the xz plane. - * @param vertices the vertices to flatten. Does not modify the original vertices. - * @return the flattened vertices. - */ - @Contract(pure = true, value = "_ -> new") - private List flattenVertices(Collection vertices) { - List flattened = new ArrayList<>(); - for (Vector v : vertices) { - flattened.add(v.clone().setY(0)); - } - return flattened; - } - - /** - * Sets the height of the polygon to the distance from the lowest vertex to the highest vertex. - * @param vertices the vertices of the polygon. - */ - private void setBounds(Collection vertices) { - double low = 9999999; - double high = -9999999; - for (Vector v : vertices) { - if (v.getY() < low) low = v.getY(); - if (v.getY() > high) high = v.getY(); - } - this.height = high - low; - } - - @SuppressWarnings("ConstantConditions") - @Override - @Contract(pure = true) - public void generateOutline(Set points) { - double particleDensity = this.getParticleDensity(); - points.addAll(MathUtil.connectPoints(vertices, particleDensity)); - points.addAll(MathUtil.calculateLine(vertices.get(0), vertices.get(vertices.size() - 1), particleDensity)); - if (height != 0) { - Set upperPoints = new LinkedHashSet<>(); - for (Vector v : points) { - upperPoints.add(new Vector(v.getX(), height, v.getZ())); - } - points.addAll(upperPoints); - for (Vector v : vertices) { - points.addAll(MathUtil.calculateLine(v, new Vector(v.getX(), height, v.getZ()), particleDensity)); - } - } - } - - @Override - public void setParticleCount(int particleCount) { - particleCount = Math.max(particleCount, 1); - double perimeter = 0; - for (int i = 0; i < vertices.size() - 1; i++) { - perimeter += vertices.get(i).distance(vertices.get(i + 1)); - } - perimeter += vertices.get(0).distance(vertices.get(vertices.size() - 1)); - perimeter *= 2; - perimeter += vertices.size() * height; - this.setParticleDensity(perimeter / particleCount); - this.setNeedsUpdate(true); - } - - @Override - public double getLength() { - return 0; - } - - @Override - public void setLength(double length) { - // intentionally left blank - } - - @Override - public double getWidth() { - return 0; - } - - @Override - public void setWidth(double width) { - // intentionally left blank - } - - @Override - public double getHeight() { - return height; - } - - @Override - public void setHeight(double height) { - this.height = Math.max(height, 0); - this.setNeedsUpdate(true); - } - - @Override - @Contract("-> new") - public Shape clone() { - return this.copyTo(new IrregularPolygon(vertices, height)); - } -} diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/LWHShape.java b/src/main/java/com/sovdee/skriptparticles/shapes/LWHShape.java deleted file mode 100644 index ce4b957..0000000 --- a/src/main/java/com/sovdee/skriptparticles/shapes/LWHShape.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.sovdee.skriptparticles.shapes; - -/** - * Represents a shape that has a length, width, and/or height. - * Neither the length, width, nor height may be negative. - */ -public interface LWHShape extends Shape { - - /** - * @return The length of the shape. - */ - double getLength(); - - /** - * Sets the length of the shape. - * @param length The length of the shape. Must be non-negative. - */ - void setLength(double length); - - /** - * @return The width of the shape. - */ - double getWidth(); - - /** - * Sets the width of the shape. - * @param width The width of the shape. Must be non-negative. - */ - void setWidth(double width); - - /** - * @return The height of the shape. - */ - double getHeight(); - - /** - * Sets the height of the shape. - * @param height The height of the shape. Must be non-negative. - */ - void setHeight(double height); -} diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/Line.java b/src/main/java/com/sovdee/skriptparticles/shapes/Line.java deleted file mode 100644 index 7d4e327..0000000 --- a/src/main/java/com/sovdee/skriptparticles/shapes/Line.java +++ /dev/null @@ -1,206 +0,0 @@ -package com.sovdee.skriptparticles.shapes; - -import com.sovdee.skriptparticles.util.DynamicLocation; -import com.sovdee.skriptparticles.util.MathUtil; -import com.sovdee.skriptparticles.util.Quaternion; -import org.bukkit.util.Vector; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.jetbrains.annotations.Contract; - -import java.util.Set; - -/** - * A line shape. This shape is defined by two points, a start and an end. - * These points can be relative ({@link Vector}) or absolute ({@link DynamicLocation}). - */ -public class Line extends AbstractShape implements LWHShape { - - private Vector start; - private Vector end; - - private @Nullable DynamicLocation startLocation; - private @Nullable DynamicLocation endLocation; - private boolean isDynamic = false; - - /** - * Creates a new line shape with the start point at the origin and the end point at the given vector. - * The vector cannot be the origin. - * @param end the end point of the line - * @throws IllegalArgumentException if the end vector is the origin - */ - public Line(Vector end) { - this(new Vector(0, 0, 0), end); - } - - /** - * Creates a new line shape with the start and end points at the given vectors. - * The vectors cannot be the same. - * @param start the start point of the line - * @param end the end point of the line - * @throws IllegalArgumentException if the start and end vectors are the same - */ - public Line(Vector start, Vector end) { - super(); - if (start.equals(end)) - throw new IllegalArgumentException("Start and end locations cannot be the same."); - this.start = start; - this.end = end; - } - - /** - * Creates a new line shape with the start and end points at the given locations. - * The locations cannot be the same. - * @param start the start point of the line - * @param end the end point of the line - */ - public Line(DynamicLocation start, DynamicLocation end) { - super(); - if (start.equals(end)) - throw new IllegalArgumentException("Start and end locations cannot be the same."); - if (start.isDynamic() || end.isDynamic()) { - this.startLocation = start.clone(); - this.endLocation = end.clone(); - this.isDynamic = true; - } - this.start = new Vector(0, 0, 0); - this.end = end.getLocation().toVector().subtract(start.getLocation().toVector()); - if (this.end.equals(this.start)) - throw new IllegalArgumentException("Start and end locations cannot be the same."); - - this.setLocation(start.clone()); - } - - - @Override - @Contract(pure = true) - public Set getPoints(Quaternion orientation) { - Set points = super.getPoints(orientation); - if (isDynamic) - // Ensure that the points are always needing to be updated if the start or end location is dynamic - this.setNeedsUpdate(true); - return points; - } - - @Override - @Contract(pure = true) - public void generatePoints(Set points) { - if (isDynamic) { - assert startLocation != null; - assert endLocation != null; - this.start = new Vector(0, 0, 0); - this.end = endLocation.getLocation().toVector().subtract(startLocation.getLocation().toVector()); - } - super.generatePoints(points); - } - - @SuppressWarnings("ConstantConditions") - @Override - @Contract(pure = true) - public void generateOutline(Set points) { - points.addAll(MathUtil.calculateLine(start, end, this.getParticleDensity())); - } - - /** - * Gets the start point of the line as a vector. May change between calls if the start point is dynamic. - * @return the start point of the line - */ - public Vector getStart() { - if (isDynamic) { - assert startLocation != null; - return startLocation.getLocation().toVector(); - } - return start.clone(); - } - - /** - * Sets the start point of the line as a vector. - * @param start the start point of the line. Must not be identical to the end point. - * @throws IllegalArgumentException if the start and end points are identical. - */ - public void setStart(Vector start) { - if (start.equals(end)) - throw new IllegalArgumentException("Start and end points must not be identical"); - this.start = start.clone(); - } - - /** - * Gets the end point of the line as a vector. May change between calls if the end point is dynamic. - * @return the end point of the line - */ - public Vector getEnd() { - if (isDynamic) { - assert endLocation != null; - return endLocation.getLocation().toVector(); - } - return end.clone(); - } - - /** - * Sets the end point of the line as a vector. - * @param end the end point of the line. Must not be identical to the start point. - * @throws IllegalArgumentException if the start and end points are identical. - */ - public void setEnd(Vector end) { - if (end.equals(start)) - throw new IllegalArgumentException("Start and end points must not be identical"); - this.end = end.clone(); - } - - @Override - public void setParticleCount(int particleCount) { - particleCount = Math.max(particleCount, 1); - this.setParticleDensity(end.clone().subtract(start).length() / particleCount); - this.setNeedsUpdate(true); - } - - @Override - public double getLength() { - return start.clone().subtract(end).length(); - } - - @Override - public void setLength(double length) { - length = Math.max(length, MathUtil.EPSILON); - Vector direction = end.clone().subtract(start).normalize(); - end = start.clone().add(direction.multiply(length)); - } - - @Override - public double getWidth() { - return 0; - } - - @Override - public void setWidth(double width) { - // intentionally left blank - } - - @Override - public double getHeight() { - return 0; - } - - @Override - public void setHeight(double height) { - // intentionally left blank - } - - @Override - @Contract("-> new") - public Shape clone() { - Line line; - if (isDynamic) { - assert this.startLocation != null; - assert this.endLocation != null; - line = (new Line(this.startLocation, this.endLocation)); - } else { - line = (new Line(this.start, this.end)); - } - line.isDynamic = this.isDynamic; - return this.copyTo(line); - } - - public String toString() { - return "Line from " + this.start + " to " + this.end; - } -} diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/PolyShape.java b/src/main/java/com/sovdee/skriptparticles/shapes/PolyShape.java deleted file mode 100644 index 0b9df43..0000000 --- a/src/main/java/com/sovdee/skriptparticles/shapes/PolyShape.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.sovdee.skriptparticles.shapes; - -/** - * Represents a shape that has a number of sides and a side length. - * The number of sides must be greater than 2. - * The side length must be greater than 0. - */ -public interface PolyShape extends Shape { - - /** - * @return The number of sides of the shape. - */ - int getSides(); - - /** - * Sets the number of sides of the shape. - * @param sides The number of sides of the shape. Must be greater than 2. - */ - void setSides(int sides); - - /** - * @return The side length of the shape. - */ - double getSideLength(); - - /** - * Sets the side length of the shape. - * @param sideLength The side length of the shape. Must be greater than 0. - */ - void setSideLength(double sideLength); -} diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/RadialShape.java b/src/main/java/com/sovdee/skriptparticles/shapes/RadialShape.java deleted file mode 100644 index 06bc449..0000000 --- a/src/main/java/com/sovdee/skriptparticles/shapes/RadialShape.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.sovdee.skriptparticles.shapes; - -/** - * Represents a shape that has a radius. - * The radius must be greater than 0. - */ -public interface RadialShape extends Shape { - - /** - * Gets the radius of the shape. - * @return The radius of the shape. - */ - double getRadius(); - - /** - * Sets the radius of the shape. - * @param radius The radius of the shape. Must be greater than 0. - */ - void setRadius(double radius); - -} diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/Rectangle.java b/src/main/java/com/sovdee/skriptparticles/shapes/Rectangle.java deleted file mode 100644 index 53403e5..0000000 --- a/src/main/java/com/sovdee/skriptparticles/shapes/Rectangle.java +++ /dev/null @@ -1,302 +0,0 @@ -package com.sovdee.skriptparticles.shapes; - -import com.sovdee.skriptparticles.util.DynamicLocation; -import com.sovdee.skriptparticles.util.MathUtil; -import com.sovdee.skriptparticles.util.Quaternion; -import org.bukkit.Location; -import org.bukkit.util.Vector; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.jetbrains.annotations.Contract; - -import java.util.LinkedHashSet; -import java.util.Set; - - -/** - * A rectangle shape, defined by a plane and a length and width. - * Can be defined by two corners, which can be relative ({@link Vector}) or absolute ({@link DynamicLocation}). - */ -public class Rectangle extends AbstractShape implements LWHShape { - - private Plane plane; - private double halfLength; - private double halfWidth; - private double lengthStep = 1.0; - private double widthStep = 1.0; - private Vector centerOffset = new Vector(0, 0, 0); - private @Nullable DynamicLocation negativeCorner; - private @Nullable DynamicLocation positiveCorner; - private boolean isDynamic = false; - - /** - * Creates a new rectangle shape with the given plane and length and width. - * @param length the length of the rectangle. Must be greater than 0. - * @param width the width of the rectangle. Must be greater than 0. - * @param plane the plane of the rectangle. - */ - public Rectangle(double length, double width, Plane plane) { - super(); - this.plane = plane; - this.halfLength = Math.max(length / 2, MathUtil.EPSILON); - this.halfWidth = Math.max(width / 2, MathUtil.EPSILON); - calculateSteps(); - } - - /** - * Creates a new rectangle shape from the given corners and plane. - * The corners may not be the same. - * @param cornerA the first corner of the rectangle. - * @param cornerB the second corner of the rectangle. - * @param plane the plane of the rectangle. - * @throws IllegalArgumentException if the corners are the same - */ - public Rectangle(Vector cornerA, Vector cornerB, Plane plane) { - super(); - if (cornerA.equals(cornerB)) - throw new IllegalArgumentException("Corners cannot be the same."); - this.plane = plane; - setLengthWidth(cornerA, cornerB); - centerOffset = cornerB.clone().add(cornerA).multiply(0.5); - switch (plane) { - case XZ -> centerOffset.setY(0); - case XY -> centerOffset.setZ(0); - case YZ -> centerOffset.setX(0); - } - calculateSteps(); - } - - /** - * Creates a new rectangle shape from the given corners and plane. - * The corners may not be the same. - * @param cornerA the first corner of the rectangle. - * @param cornerB the second corner of the rectangle. - * @param plane the plane of the rectangle. - * @throws IllegalArgumentException if the corners are the same - */ - public Rectangle(DynamicLocation cornerA, DynamicLocation cornerB, Plane plane) { - super(); - if (cornerA.equals(cornerB)) - throw new IllegalArgumentException("Corners cannot be the same."); - - this.plane = plane; - Location cornerALocation = cornerA.getLocation(); - Location cornerBLocation = cornerB.getLocation(); - if (cornerA.equals(cornerB)) - throw new IllegalArgumentException("Corners cannot be the same."); - - if (cornerA.isDynamic() || cornerB.isDynamic()) { - this.negativeCorner = cornerA.clone(); - this.positiveCorner = cornerB.clone(); - isDynamic = true; - } else { - setLengthWidth(cornerALocation, cornerBLocation); - } - // get center of rectangle - Vector offset = cornerBLocation.toVector().subtract(cornerALocation.toVector()).multiply(0.5); - switch (plane) { - case XZ -> offset.setY(0); - case XY -> offset.setZ(0); - case YZ -> offset.setX(0); - } - this.setLocation(new DynamicLocation(cornerALocation.clone().add(offset))); - calculateSteps(); - } - - /** - * Sets the length and width of the rectangle based on the given corners. - * @param cornerA the first corner - * @param cornerB the second corner - */ - private void setLengthWidth(Location cornerA, Location cornerB) { - setLengthWidth(cornerA.toVector(), cornerB.toVector()); - } - - /** - * Sets the length and width of the rectangle based on the given corners. - * @param cornerA the first corner - * @param cornerB the second corner - */ - private void setLengthWidth(Vector cornerA, Vector cornerB) { - double length = switch (plane) { - case XZ, XY -> Math.abs(cornerA.getX() - cornerB.getX()); - case YZ -> Math.abs(cornerA.getY() - cornerB.getY()); - }; - double width = switch (plane) { - case XZ, YZ -> Math.abs(cornerA.getZ() - cornerB.getZ()); - case XY -> Math.abs(cornerA.getY() - cornerB.getY()); - }; - this.halfWidth = Math.abs(width) / 2; - this.halfLength = Math.abs(length) / 2; - } - - /** - * Creates a {@link Vector} in the plane of the rectangle from the given length and width. - * @param length X or Y coordinate, depending on the plane. - * @param width Z or Y coordinate, depending on the plane. - * @return a vector in the plane of the rectangle. - */ - @Contract(pure = true, value = "_, _ -> new") - private Vector vectorFromLengthWidth(double length, double width) { - return switch (plane) { - case XZ -> new Vector(length, 0, width); - case XY -> new Vector(length, width, 0); - case YZ -> new Vector(0, length, width); - }; - } - - /** - * Calculates the nearest factor to particleDensity as step size for the x and z plane. - * Used to ensure the shape has a uniform density of particles. - */ - private void calculateSteps() { - double particleDensity = this.getParticleDensity(); - lengthStep = 2 * halfWidth / Math.round(2 * halfWidth / particleDensity); - widthStep = 2 * halfLength / Math.round(2 * halfLength / particleDensity); - } - - @SuppressWarnings("ConstantConditions") - @Override - @Contract(pure = true) - public void generateOutline(Set points) { - for (double l = -halfLength + widthStep; l < halfLength; l += widthStep) { - points.add(vectorFromLengthWidth(l, -halfWidth)); - points.add(vectorFromLengthWidth(l, halfWidth)); - } - for (double w = -halfWidth; w <= halfWidth; w += lengthStep) { - points.add(vectorFromLengthWidth(-halfLength, w)); - points.add(vectorFromLengthWidth(halfLength, w)); - } - } - - @SuppressWarnings("ConstantConditions") - @Override - @Contract(pure = true) - public void generateSurface(Set points) { - for (double w = -halfWidth; w <= halfWidth; w += lengthStep) { - for (double l = -halfLength; l <= halfLength; l += widthStep) { - points.add(vectorFromLengthWidth(l, w)); - } - } - } - - @Override - @Contract(pure = true) - public void generatePoints(Set points) { - if (isDynamic) { - assert positiveCorner != null; - assert negativeCorner != null; - Location pos = positiveCorner.getLocation(); - Location neg = negativeCorner.getLocation(); - setLengthWidth(neg, pos); - // get center of rectangle - Vector offset = pos.toVector().subtract(neg.toVector()).multiply(0.5); - switch (plane) { - case XZ -> offset.setY(0); - case XY -> offset.setZ(0); - case YZ -> offset.setX(0); - } - this.setLocation(new DynamicLocation(neg.clone().add(offset))); - } - calculateSteps(); - super.generatePoints(points); - points.forEach(vector -> vector.add(centerOffset)); - } - - @Override - public Set getPoints(Quaternion orientation) { - Set points = super.getPoints(orientation); - if (isDynamic) - // Ensure that the points are always needing to be updated if the start or end location is dynamic - this.setNeedsUpdate(true); - return points; - } - - @Override - public void setParticleCount(int particleCount) { - particleCount = Math.max(particleCount, 1); - switch (this.getStyle()) { - case FILL, SURFACE -> this.setParticleDensity(Math.sqrt(4 * halfWidth * halfLength / particleCount)); - case OUTLINE -> this.setParticleDensity(4 * (halfWidth + halfLength) / particleCount); - } - this.setNeedsUpdate(true); - } - - @Override - public double getLength() { - return halfLength * 2; - } - - @Override - public void setLength(double length) { - this.halfLength = Math.max(length / 2, MathUtil.EPSILON); - this.setNeedsUpdate(true); - } - - @Override - public double getWidth() { - return halfWidth * 2; - } - - @Override - public void setWidth(double width) { - this.halfWidth = Math.max(width / 2, MathUtil.EPSILON); - this.setNeedsUpdate(true); - } - - @Override - public double getHeight() { - return 0; - } - - @Override - public void setHeight(double height) { - // intentionally left blank - } - - /** - * Gets the plane of the rectangle. - * @return the plane of the rectangle. - */ - public Plane getPlane() { - return plane; - } - - /** - * Sets the plane of the rectangle. Ensures the shape will be updated on the next call to {@link Shape#generatePoints(Set)}. - * @param plane the plane of the rectangle. - */ - public void setPlane(Plane plane) { - this.plane = plane; - this.setNeedsUpdate(true); - } - - @Override - @Contract("-> new") - public Shape clone() { - Rectangle rectangle; - if (isDynamic) { - assert negativeCorner != null; - assert positiveCorner != null; - rectangle = (new Rectangle(negativeCorner, positiveCorner, plane)); - } else { - rectangle = (new Rectangle(this.getLength(), this.getWidth(), plane)); - } - rectangle.isDynamic = this.isDynamic; - return this.copyTo(rectangle); - } - - @Override - public String toString() { - String axis = this.plane.toString().toLowerCase(); - if (isDynamic) - return axis + " rectangle from " + negativeCorner + " to " + positiveCorner; - return axis + " rectangle with length " + this.getLength() + " and width " + this.getWidth(); - } - - /** - * Represents the plane of a rectangle. - */ - public enum Plane { - XZ, XY, YZ - } -} diff --git a/src/main/java/com/sovdee/skriptparticles/shapes/RegularPolyhedron.java b/src/main/java/com/sovdee/skriptparticles/shapes/RegularPolyhedron.java deleted file mode 100644 index 9f7f0bd..0000000 --- a/src/main/java/com/sovdee/skriptparticles/shapes/RegularPolyhedron.java +++ /dev/null @@ -1,261 +0,0 @@ -package com.sovdee.skriptparticles.shapes; - -import com.sovdee.skriptparticles.util.MathUtil; -import com.sovdee.skriptparticles.util.Quaternion; -import org.bukkit.util.Vector; -import org.jetbrains.annotations.Contract; - -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Set; - -/** - * A regular polyhedron shape. This shape is defined by a center point and a radius, as well as the number of faces. - * The four regular polyhedra are stored as static constant arrays of rotations, each representing a face of the polyhedron. - */ -public class RegularPolyhedron extends AbstractShape implements RadialShape, PolyShape { - - private static final Quaternion[] TETRAHEDRON_FACES = { - new Quaternion(1.0, 0.0, 0.0, 0), - new Quaternion(-0.5, -0.0, -0.288675134594813, 0.816496580927726), - new Quaternion(0.5, 0.0, -0.288675134594813, 0.816496580927726), - new Quaternion(0.0, 0.0, 0.5773502691896258, 0.816496580927726) - }; - private static final Quaternion[] OCTAHEDRON_FACES = { - new Quaternion(0.0, 0.0, 0.45970084338098305, 0.8880738339771153), - new Quaternion(0.3250575836718681, 0.6279630301995544, 0.32505758367186816, 0.6279630301995545), - new Quaternion(0.45970084338098305, 0.8880738339771153, 0, 0), - new Quaternion(0.32505758367186816, 0.6279630301995545, -0.3250575836718681, -0.6279630301995544), - new Quaternion(0.0, 0.0, 0.8880738339771153, -0.45970084338098316), - new Quaternion(0.6279630301995544, -0.3250575836718682, 0.6279630301995545, -0.3250575836718682), - new Quaternion(0.8880738339771153, -0.45970084338098316, 0, 0), - new Quaternion(0.6279630301995545, -0.3250575836718682, -0.6279630301995544, 0.3250575836718682) - }; - private static final Quaternion[] ICOSAHEDRON_FACES = { - new Quaternion(1.0, 0.0, 0.0, 0), - new Quaternion(0.0, 1.0, 0.0, 0), - new Quaternion(0.3090169943749475, 0.0, 0.17841104488654494, 0.9341723589627158), - new Quaternion(-0.5, 0.8090169943749475, 0.288675134594813, 0.110264089708268), - new Quaternion(0, 0.8090169943749475, 0.5773502691896256, -0.110264089708268), - new Quaternion(0.3090169943749475, 0.0, -0.1784110448865451, -0.9341723589627157), - new Quaternion(0.5, -0.8090169943749475, 0.288675134594813, 0.110264089708268), - new Quaternion(0, 0.8090169943749475, -0.5773502691896261, 0.110264089708268), - new Quaternion(0.0, 0.0, 0.35682208977309, -0.9341723589627157), - new Quaternion(-0.5, -0.8090169943749475, -0.288675134594813, -0.110264089708268), - new Quaternion(0.5, 0.8090169943749475, -0.288675134594813, -0.110264089708268), - new Quaternion(-0.8090169943749475, -0.0, -0.46708617948135794, 0.35682208977309), - new Quaternion(0.3090169943749475, 0.5, -0.7557613140761709, -0.288675134594813), - new Quaternion(0.8090169943749475, 0.0, -0.46708617948135794, 0.35682208977309), - new Quaternion(-0.5, -0.5, -0.6454972243679027, 0.288675134594813), - new Quaternion(0.0, 0.0, 0.9341723589627157, 0.35682208977309), - new Quaternion(-0.8090169943749475, 0.5, 0.110264089708268, -0.288675134594813) - }; - private static final Quaternion[] DODECAHEDRON_FACES = { - new Quaternion(0.0, 0.3090169943749475, 0.0, 0.9510565162951536), - new Quaternion(-0.3090169943749475, 0, 0.9510565162951536, 0), - new Quaternion(0.0, 0.0, 0.8506508083520399, 0.5257311121191337), - new Quaternion(0.0, 0.0, 0.5257311121191337, -0.8506508083520399), - new Quaternion(0.5, 0.3090169943749475, 0.6881909602355868, 0.42532540417602), - new Quaternion(0.3090169943749475, -0.5, 0.42532540417602, -0.6881909602355868), - new Quaternion(0.8090169943749475, 0.5, 0.2628655560595668, 0.1624598481164532), - new Quaternion(0.5, -0.8090169943749475, 0.1624598481164532, -0.2628655560595668), - new Quaternion(0.8090169943749475, 0.5, -0.2628655560595668, -0.1624598481164532), - new Quaternion(0.5, -0.8090169943749475, -0.1624598481164532, 0.2628655560595668), - new Quaternion(0.5, 0.3090169943749475, -0.6881909602355868, -0.42532540417602), - new Quaternion(0.3090169943749475, -0.5, -0.42532540417602, 0.6881909602355868) - }; - private double radius; - private int faces; - - /** - * Generates a regular polyhedron with the given radius and number of faces. - * - * @param radius the radius of the polyhedron. Must be greater than 0. - * @param faces the number of faces of the polyhedron. Must be 4, 8, 12, or 20. - */ - public RegularPolyhedron(double radius, int faces) { - super(); - this.radius = Math.max(radius, MathUtil.EPSILON); - this.faces = switch (faces) { - case 4, 8, 12, 20 -> faces; - default -> 4; - }; - } - - - @SuppressWarnings("ConstantConditions") - @Override - @Contract(pure = true) - public void generateOutline(Set points) { - points.addAll(switch (faces) { - case 4 -> generatePolyhedron(TETRAHEDRON_FACES, radius); - case 8 -> generatePolyhedron(OCTAHEDRON_FACES, radius); - case 20 -> generatePolyhedron(ICOSAHEDRON_FACES, radius); - case 12 -> generatePolyhedron(DODECAHEDRON_FACES, radius); - default -> new HashSet<>(); - }); - } - - @SuppressWarnings("ConstantConditions") - @Override - public void generateFilled(Set points) { - double step = radius / Math.round(radius / this.getParticleDensity()); - switch (faces) { - case 4: - for (double i = radius; i > 0; i -= step) { - points.addAll(generatePolyhedron(TETRAHEDRON_FACES, i)); - } - break; - case 8: - for (double i = radius; i > 0; i -= step) { - points.addAll(generatePolyhedron(OCTAHEDRON_FACES, i)); - } - break; - case 12: - for (double i = radius; i > 0; i -= step) { - points.addAll(generatePolyhedron(DODECAHEDRON_FACES, i)); - } - break; - case 20: - for (double i = radius; i > 0; i -= step) { - points.addAll(generatePolyhedron(ICOSAHEDRON_FACES, i)); - } - break; - } - } - - /** - * Generates a polyhedron from the given set of face rotations and a radius. - * - * @param rotations the rotations of the faces of the polyhedron - * @param radius the radius of the polyhedron - * @return a set of vectors representing the polyhedron - */ - @Contract(pure = true, value = "_, _ -> new") - private Set generatePolyhedron(Quaternion[] rotations, double radius) { - Set points = new LinkedHashSet<>(); - int sides = this.faces == 12 ? 5 : 3; - // todo: get rid of magic numbers - double sideLength = switch (faces) { - case 4 -> radius / 0.6123724356957945; - case 8 -> radius / 0.7071067811865; - case 12 -> radius / 1.401258538; - case 20 -> radius / 0.9510565162951535; - default -> 0.0; - }; - double inscribedRadius = switch (this.faces) { - case 4 -> sideLength / 4.89897948556; - case 8 -> sideLength * 0.408248290; - case 12 -> sideLength * 1.113516364; - case 20 -> sideLength * 0.7557613141; - default -> 1; - }; - Vector offset = new Vector(0, inscribedRadius, 0); - double faceRadius = sideLength / (2 * Math.sin(Math.PI / sides)); - Style style = this.getStyle(); - for (Quaternion rotation : rotations) { - Set facePoints = new LinkedHashSet<>(switch (style) { - case OUTLINE -> generateFaceOutline(sides, faceRadius); - case FILL, SURFACE -> generateFaceSurface(sides, faceRadius); - }); - facePoints.forEach(point -> rotation.transform(point.add(offset))); - points.addAll(facePoints); - } - return points; - } - - /** - * Generates the outline of a face of the polyhedron. The face is a regular polygon with the given number of sides. - * @param sides the number of sides of the face - * @param radius the radius of the face - * @return a set of vectors representing the outline of the face - */ - @Contract(pure = true, value = "_, _ -> new") - private Set generateFaceOutline(int sides, double radius) { - return new LinkedHashSet<>(MathUtil.calculateRegularPolygon(radius, 2 * Math.PI / sides, this.getParticleDensity(), true)); - } - - /** - * Generates the surface of a face of the polyhedron. The face is a regular polygon with the given number of sides. - * @param sides the number of sides of the face - * @param radius the radius of the face - * @return a set of vectors representing the surface of the face - */ - @Contract(pure = true, value = "_, _ -> new") - private Set generateFaceSurface(int sides, double radius) { - HashSet facePoints = new LinkedHashSet<>(); - double particleDensity = this.getParticleDensity(); - double apothem = radius * Math.cos(Math.PI / sides); - double radiusStep = radius / Math.round(apothem / particleDensity); - for (double subRadius = radius; subRadius > 0; subRadius -= radiusStep) { - facePoints.addAll(MathUtil.calculateRegularPolygon(subRadius, 2 * Math.PI / sides, particleDensity, false)); - } - facePoints.add(new Vector(0, 0, 0)); - return facePoints; - } - - @Override - public void setParticleCount(int particleCount) { - // intentionally left blank - } - - @Override - @Contract("-> new") - public Shape clone() { - return this.copyTo(new RegularPolyhedron(radius, faces)); - } - - @Override - public int getSides() { - return faces; - } - - @Override - public void setSides(int sides) { - switch (sides) { - case 4, 8, 12, 20 -> this.faces = sides; - default -> { - // intentionally does not throw an exception - return; - } - } - this.setNeedsUpdate(true); - } - - @Override - public double getSideLength() { - return switch (faces) { - case 4 -> radius / 0.6123724356957945; - case 8 -> radius / 0.7071067811865; - case 12 -> radius / 1.401258538; - case 20 -> radius / 0.9510565162951535; - default -> 0.0; - }; - } - - @Override - public void setSideLength(double sideLength) { - sideLength = Math.max(sideLength, MathUtil.EPSILON); - switch (faces) { - case 4 -> this.radius = sideLength * 0.6123724356957945; - case 8 -> this.radius = sideLength * 0.7071067811865; - case 12 -> this.radius = sideLength * 1.401258538; - case 20 -> this.radius = sideLength * 0.9510565162951535; - default -> { - return; - } - } - this.setNeedsUpdate(true); - } - - @Override - public double getRadius() { - return radius; - } - - @Override - public void setRadius(double radius) { - this.radius = Math.max(radius, MathUtil.EPSILON); - this.setNeedsUpdate(true); - } -} From 942ee2331159fb6eac15f775b384bb67ce1b2be6 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Sat, 7 Feb 2026 19:40:43 -0800 Subject: [PATCH 2/6] Introduce drawContext/drawable and further remove shape logic from skript-particle --- .../java/com/sovdee/shapes/AbstractShape.java | 28 +- .../java/com/sovdee/shapes/BezierCurve.java | 32 +- .../main/java/com/sovdee/shapes/Cuboid.java | 33 +- .../java/com/sovdee/shapes/DrawContext.java | 8 + .../src/main/java/com/sovdee/shapes/Line.java | 71 ++- .../java/com/sovdee/shapes/Rectangle.java | 30 +- .../main/java/com/sovdee/shapes/Shape.java | 25 ++ .../elements/effects/EffRotateShape.java | 14 +- .../elements/effects/EffSetOrdering.java | 14 +- .../elements/effects/EffToggleAxes.java | 8 +- .../elements/expressions/ExprDrawnShapes.java | 2 +- .../elements/expressions/ExprShapeCopy.java | 2 +- .../expressions/constructors/ExprArc.java | 11 +- .../constructors/ExprBezierCurve.java | 37 +- .../expressions/constructors/ExprCircle.java | 19 +- .../expressions/constructors/ExprCuboid.java | 27 +- .../expressions/constructors/ExprEllipse.java | 16 +- .../constructors/ExprEllipsoid.java | 10 +- .../constructors/ExprEllipticalArc.java | 10 +- .../expressions/constructors/ExprHeart.java | 11 +- .../expressions/constructors/ExprHelix.java | 10 +- .../constructors/ExprIrregularPolygon.java | 15 +- .../expressions/constructors/ExprLine.java | 30 +- .../constructors/ExprRectangle.java | 33 +- .../constructors/ExprRegularPolygon.java | 10 +- .../constructors/ExprRegularPolyhedron.java | 12 +- .../expressions/constructors/ExprSphere.java | 11 +- .../constructors/ExprSphericalCap.java | 11 +- .../expressions/constructors/ExprStar.java | 12 +- .../properties/ExprHelixWindingRate.java | 9 +- .../properties/ExprShapeCutoffAngle.java | 9 +- .../expressions/properties/ExprShapeLWH.java | 9 +- .../properties/ExprShapeLocations.java | 5 +- .../properties/ExprShapeNormal.java | 11 +- .../properties/ExprShapeOffset.java | 10 +- .../properties/ExprShapeOrientation.java | 11 +- .../properties/ExprShapeParticle.java | 9 +- .../properties/ExprShapeParticleDensity.java | 2 +- .../properties/ExprShapePoints.java | 5 +- .../properties/ExprShapeRadius.java | 9 +- .../properties/ExprShapeRelativeAxis.java | 9 +- .../properties/ExprShapeScale.java | 2 +- .../properties/ExprShapeSideLength.java | 9 +- .../properties/ExprShapeSides.java | 9 +- .../properties/ExprShapeStyle.java | 2 +- .../properties/ExprStarPoints.java | 9 +- .../expressions/properties/ExprStarRadii.java | 9 +- .../sections/DrawShapeEffectSection.java | 55 +-- .../elements/sections/EffSecDrawShape.java | 3 +- .../sections/EffSecDrawShapeAnimation.java | 5 +- .../elements/types/ShapeTypes.java | 6 +- .../skriptparticles/particles/Particle.java | 14 +- .../skriptparticles/shapes/DrawData.java | 100 +++++ .../skriptparticles/shapes/DrawManager.java | 120 +++++ .../skriptparticles/shapes/DrawableShape.java | 422 ------------------ .../shapes/DynamicBezierCurve.java | 67 --- .../skriptparticles/shapes/DynamicCuboid.java | 63 --- .../skriptparticles/shapes/DynamicLine.java | 56 --- .../shapes/DynamicRectangle.java | 77 ---- .../sovdee/skriptparticles/shapes/Shape.java | 402 ----------------- 60 files changed, 722 insertions(+), 1358 deletions(-) create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/DrawContext.java create mode 100644 skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawData.java create mode 100644 skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawManager.java delete mode 100644 skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawableShape.java delete mode 100644 skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicBezierCurve.java delete mode 100644 skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicCuboid.java delete mode 100644 skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicLine.java delete mode 100644 skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicRectangle.java delete mode 100644 skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/Shape.java diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/AbstractShape.java b/shapes-lib/src/main/java/com/sovdee/shapes/AbstractShape.java index c648e05..8d4c3d5 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/AbstractShape.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/AbstractShape.java @@ -23,6 +23,9 @@ public abstract class AbstractShape implements Shape { private Comparator ordering; private double particleDensity = 0.25; + private DrawContext drawContext; + private boolean dynamic = false; + private State lastState; private boolean needsUpdate = false; @@ -48,7 +51,7 @@ public Set getPoints() { @Override public Set getPoints(Quaterniond orientation) { State state = getState(orientation); - if (needsUpdate || !lastState.equals(state) || points.isEmpty()) { + if (dynamic || needsUpdate || !lastState.equals(state) || points.isEmpty()) { if (ordering != null) points = new TreeSet<>(ordering); else @@ -189,6 +192,26 @@ public void setNeedsUpdate(boolean needsUpdate) { this.needsUpdate = needsUpdate; } + @Override + public DrawContext getDrawContext() { + return drawContext; + } + + @Override + public void setDrawContext(DrawContext drawContext) { + this.drawContext = drawContext; + } + + @Override + public boolean isDynamic() { + return dynamic; + } + + @Override + public void setDynamic(boolean dynamic) { + this.dynamic = dynamic; + } + public abstract Shape clone(); @Override @@ -202,6 +225,9 @@ public Shape copyTo(Shape shape) { shape.setPoints(this.getPoints()); shape.setNeedsUpdate(this.needsUpdate); shape.setLastState(this.lastState); + shape.setDynamic(this.dynamic); + if (this.drawContext != null) + shape.setDrawContext(this.drawContext.copy()); return shape; } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/BezierCurve.java b/shapes-lib/src/main/java/com/sovdee/shapes/BezierCurve.java index 7319f62..a712ccd 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/BezierCurve.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/BezierCurve.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.function.Supplier; /** * A bezier curve defined by control points as Vector3d. @@ -13,6 +14,7 @@ public class BezierCurve extends AbstractShape { private List controlPoints; + private Supplier> controlPointsSupplier; /** * Creates a bezier curve from the given control points. @@ -29,6 +31,18 @@ public BezierCurve(List controlPoints) { this.controlPoints.add(new Vector3d(cp)); } + public BezierCurve(Supplier> controlPointsSupplier) { + super(); + this.controlPointsSupplier = controlPointsSupplier; + List pts = controlPointsSupplier.get(); + if (pts.size() < 2) + throw new IllegalArgumentException("A bezier curve must have at least 2 control points."); + this.controlPoints = new ArrayList<>(); + for (Vector3d cp : pts) + this.controlPoints.add(new Vector3d(cp)); + setDynamic(true); + } + public BezierCurve(BezierCurve curve) { super(); this.controlPoints = new ArrayList<>(); @@ -38,6 +52,12 @@ public BezierCurve(BezierCurve curve) { @Override public void generateOutline(Set points) { + if (controlPointsSupplier != null) { + List pts = controlPointsSupplier.get(); + this.controlPoints = new ArrayList<>(); + for (Vector3d cp : pts) + this.controlPoints.add(new Vector3d(cp)); + } int steps = (int) (estimateLength() / getParticleDensity()); for (double step = 0; step < steps; step++) { @@ -81,8 +101,18 @@ public void setControlPoints(List controlPoints) { this.setNeedsUpdate(true); } + public Supplier> getControlPointsSupplier() { + return controlPointsSupplier; + } + @Override public Shape clone() { - return this.copyTo(new BezierCurve(this)); + BezierCurve clone; + if (controlPointsSupplier != null) { + clone = new BezierCurve(controlPointsSupplier); + } else { + clone = new BezierCurve(this); + } + return this.copyTo(clone); } } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Cuboid.java b/shapes-lib/src/main/java/com/sovdee/shapes/Cuboid.java index 9f89855..b88d4ed 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/Cuboid.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/Cuboid.java @@ -4,6 +4,7 @@ import org.joml.Vector3d; import java.util.Set; +import java.util.function.Supplier; /** * A cuboid shape, defined either by dimensions or by two corner vectors. @@ -14,6 +15,8 @@ public class Cuboid extends AbstractShape implements LWHShape { private double halfLength, halfWidth, halfHeight; private double lengthStep, widthStep, heightStep; private Vector3d centerOffset = new Vector3d(0, 0, 0); + private Supplier cornerASupplier; + private Supplier cornerBSupplier; public Cuboid(double length, double width, double height) { super(); @@ -34,6 +37,19 @@ public Cuboid(Vector3d cornerA, Vector3d cornerB) { calculateSteps(); } + public Cuboid(Supplier cornerA, Supplier cornerB) { + super(); + this.cornerASupplier = cornerA; + this.cornerBSupplier = cornerB; + Vector3d a = cornerA.get(); + Vector3d b = cornerB.get(); + this.halfLength = Math.max(Math.abs(b.x - a.x) / 2, MathUtil.EPSILON); + this.halfWidth = Math.max(Math.abs(b.z - a.z) / 2, MathUtil.EPSILON); + this.halfHeight = Math.max(Math.abs(b.y - a.y) / 2, MathUtil.EPSILON); + calculateSteps(); + setDynamic(true); + } + private void calculateSteps() { widthStep = 2 * halfWidth / Math.round(2 * halfWidth / this.getParticleDensity()); lengthStep = 2 * halfLength / Math.round(2 * halfLength / this.getParticleDensity()); @@ -97,6 +113,13 @@ public void generateFilled(Set points) { @Override public void generatePoints(Set points) { + if (cornerASupplier != null && cornerBSupplier != null) { + Vector3d a = cornerASupplier.get(); + Vector3d b = cornerBSupplier.get(); + this.halfLength = Math.max(Math.abs(b.x - a.x) / 2, MathUtil.EPSILON); + this.halfWidth = Math.max(Math.abs(b.z - a.z) / 2, MathUtil.EPSILON); + this.halfHeight = Math.max(Math.abs(b.y - a.y) / 2, MathUtil.EPSILON); + } calculateSteps(); super.generatePoints(points); points.forEach(vector -> vector.add(centerOffset)); @@ -142,9 +165,17 @@ public void setHeight(double height) { this.setNeedsUpdate(true); } + public Supplier getCornerASupplier() { return cornerASupplier; } + public Supplier getCornerBSupplier() { return cornerBSupplier; } + @Override public Shape clone() { - Cuboid cuboid = new Cuboid(getLength(), getWidth(), getHeight()); + Cuboid cuboid; + if (cornerASupplier != null && cornerBSupplier != null) { + cuboid = new Cuboid(cornerASupplier, cornerBSupplier); + } else { + cuboid = new Cuboid(getLength(), getWidth(), getHeight()); + } cuboid.centerOffset = new Vector3d(this.centerOffset); return this.copyTo(cuboid); } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/DrawContext.java b/shapes-lib/src/main/java/com/sovdee/shapes/DrawContext.java new file mode 100644 index 0000000..8321e3b --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/DrawContext.java @@ -0,0 +1,8 @@ +package com.sovdee.shapes; + +/** + * Client-provided rendering metadata. Implementations are opaque to the library. + */ +public interface DrawContext { + DrawContext copy(); +} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Line.java b/shapes-lib/src/main/java/com/sovdee/shapes/Line.java index dda5661..4fe7efe 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/Line.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/Line.java @@ -4,15 +4,16 @@ import org.joml.Vector3d; import java.util.Set; +import java.util.function.Supplier; /** * A line shape defined by two vector endpoints. - * For dynamic (entity-following) lines, use the plugin-side DynamicLine wrapper. + * Supports both static endpoints and dynamic suppliers for entity-following. */ public class Line extends AbstractShape implements LWHShape { - private Vector3d start; - private Vector3d end; + private Supplier startSupplier; + private Supplier endSupplier; public Line(Vector3d end) { this(new Vector3d(0, 0, 0), end); @@ -22,54 +23,82 @@ public Line(Vector3d start, Vector3d end) { super(); if (start.equals(end)) throw new IllegalArgumentException("Start and end locations cannot be the same."); - this.start = new Vector3d(start); - this.end = new Vector3d(end); + final Vector3d s = new Vector3d(start); + final Vector3d e = new Vector3d(end); + this.startSupplier = () -> new Vector3d(s); + this.endSupplier = () -> new Vector3d(e); + } + + public Line(Supplier start, Supplier end) { + super(); + this.startSupplier = start; + this.endSupplier = end; + setDynamic(true); } @Override public void generateOutline(Set points) { - points.addAll(MathUtil.calculateLine(start, end, this.getParticleDensity())); + points.addAll(MathUtil.calculateLine(getStart(), getEnd(), this.getParticleDensity())); } public Vector3d getStart() { - return new Vector3d(start); + return startSupplier.get(); } public void setStart(Vector3d start) { - if (start.equals(end)) - throw new IllegalArgumentException("Start and end points must not be identical"); - this.start = new Vector3d(start); + final Vector3d s = new Vector3d(start); + this.startSupplier = () -> new Vector3d(s); this.setNeedsUpdate(true); } public Vector3d getEnd() { - return new Vector3d(end); + return endSupplier.get(); } public void setEnd(Vector3d end) { - if (end.equals(start)) - throw new IllegalArgumentException("Start and end points must not be identical"); - this.end = new Vector3d(end); + final Vector3d e = new Vector3d(end); + this.endSupplier = () -> new Vector3d(e); this.setNeedsUpdate(true); } + public Supplier getStartSupplier() { + return startSupplier; + } + + public void setStartSupplier(Supplier startSupplier) { + this.startSupplier = startSupplier; + } + + public Supplier getEndSupplier() { + return endSupplier; + } + + public void setEndSupplier(Supplier endSupplier) { + this.endSupplier = endSupplier; + } + @Override public void setParticleCount(int particleCount) { particleCount = Math.max(particleCount, 1); + Vector3d start = getStart(); + Vector3d end = getEnd(); this.setParticleDensity(new Vector3d(end).sub(start).length() / particleCount); this.setNeedsUpdate(true); } @Override public double getLength() { - return new Vector3d(start).sub(end).length(); + return new Vector3d(getStart()).sub(getEnd()).length(); } @Override public void setLength(double length) { length = Math.max(length, MathUtil.EPSILON); + Vector3d start = getStart(); + Vector3d end = getEnd(); Vector3d direction = new Vector3d(end).sub(start).normalize(); - end = new Vector3d(start).add(direction.mul(length)); + Vector3d newEnd = new Vector3d(start).add(direction.mul(length)); + setEnd(newEnd); this.setNeedsUpdate(true); } @@ -87,10 +116,16 @@ public void setHeight(double height) { } @Override public Shape clone() { - return this.copyTo(new Line(new Vector3d(this.start), new Vector3d(this.end))); + Line clone; + if (isDynamic()) { + clone = new Line(this.startSupplier, this.endSupplier); + } else { + clone = new Line(getStart(), getEnd()); + } + return this.copyTo(clone); } public String toString() { - return "Line from " + this.start + " to " + this.end; + return "Line from " + getStart() + " to " + getEnd(); } } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Rectangle.java b/shapes-lib/src/main/java/com/sovdee/shapes/Rectangle.java index 9b685bd..ccfae5f 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/Rectangle.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/Rectangle.java @@ -4,6 +4,7 @@ import org.joml.Vector3d; import java.util.Set; +import java.util.function.Supplier; /** * A rectangle shape, defined by a plane and dimensions. @@ -17,6 +18,8 @@ public class Rectangle extends AbstractShape implements LWHShape { private double lengthStep = 1.0; private double widthStep = 1.0; private Vector3d centerOffset = new Vector3d(0, 0, 0); + private Supplier cornerASupplier; + private Supplier cornerBSupplier; public Rectangle(double length, double width, Plane plane) { super(); @@ -41,6 +44,18 @@ public Rectangle(Vector3d cornerA, Vector3d cornerB, Plane plane) { calculateSteps(); } + public Rectangle(Supplier cornerA, Supplier cornerB, Plane plane) { + super(); + this.plane = plane; + this.cornerASupplier = cornerA; + this.cornerBSupplier = cornerB; + Vector3d a = cornerA.get(); + Vector3d b = cornerB.get(); + setLengthWidth(a, b); + calculateSteps(); + setDynamic(true); + } + private void setLengthWidth(Vector3d cornerA, Vector3d cornerB) { double length = switch (plane) { case XZ, XY -> Math.abs(cornerA.x - cornerB.x); @@ -91,6 +106,11 @@ public void generateSurface(Set points) { @Override public void generatePoints(Set points) { + if (cornerASupplier != null && cornerBSupplier != null) { + Vector3d a = cornerASupplier.get(); + Vector3d b = cornerBSupplier.get(); + setLengthWidth(a, b); + } calculateSteps(); super.generatePoints(points); points.forEach(vector -> vector.add(centerOffset)); @@ -137,9 +157,17 @@ public void setPlane(Plane plane) { this.setNeedsUpdate(true); } + public Supplier getCornerASupplier() { return cornerASupplier; } + public Supplier getCornerBSupplier() { return cornerBSupplier; } + @Override public Shape clone() { - Rectangle rectangle = new Rectangle(this.getLength(), this.getWidth(), plane); + Rectangle rectangle; + if (cornerASupplier != null && cornerBSupplier != null) { + rectangle = new Rectangle(cornerASupplier, cornerBSupplier, plane); + } else { + rectangle = new Rectangle(this.getLength(), this.getWidth(), plane); + } rectangle.centerOffset = new Vector3d(this.centerOffset); return this.copyTo(rectangle); } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Shape.java b/shapes-lib/src/main/java/com/sovdee/shapes/Shape.java index 2c274a8..3f40a7a 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/Shape.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/Shape.java @@ -173,6 +173,31 @@ default void generatePoints(Set points) { */ void setParticleCount(int particleCount); + /** + * @return the draw context attached to this shape, or null if none. + */ + DrawContext getDrawContext(); + + /** + * Sets the draw context for this shape. + * + * @param drawContext The draw context to attach. + */ + void setDrawContext(DrawContext drawContext); + + /** + * @return whether the shape is dynamic (always regenerates points). + */ + boolean isDynamic(); + + /** + * Sets whether the shape is dynamic. + * Dynamic shapes always regenerate points, bypassing state caching. + * + * @param dynamic Whether the shape is dynamic. + */ + void setDynamic(boolean dynamic); + /** * @return whether the shape needs a point update. */ diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffRotateShape.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffRotateShape.java index 3614ace..ca35a31 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffRotateShape.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffRotateShape.java @@ -10,10 +10,11 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.util.Kleenean; import com.sovdee.skriptparticles.elements.sections.DrawShapeEffectSection.DrawEvent; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; import org.bukkit.event.Event; import org.bukkit.util.Vector; import org.jetbrains.annotations.Nullable; +import org.joml.Quaterniond; import org.joml.Quaternionf; @Name("Rotate Shape") @@ -81,7 +82,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override protected void execute(Event event) { - Quaternionf rotation; + Quaterniond rotation; if (isAxisAngle) { Number angle = this.angle.getSingle(event); if (angle == null) return; @@ -105,10 +106,11 @@ protected void execute(Event event) { axis = vectorAxis.getSingle(event); if (axis == null) return; } - rotation = new Quaternionf().rotationAxis((float) angle.doubleValue(), (float) axis.getX(), (float) axis.getY(), (float) axis.getZ()); + rotation = new Quaterniond().rotationAxis(angle.doubleValue(), axis.getX(), axis.getY(), axis.getZ()); } else { - rotation = this.rotation.getSingle(event); - if (rotation == null) return; + Quaternionf rotationF = this.rotation.getSingle(event); + if (rotationF == null) return; + rotation = new Quaterniond(rotationF.x, rotationF.y, rotationF.z, rotationF.w); } Shape[] shapes; @@ -119,7 +121,7 @@ protected void execute(Event event) { } for (Shape shape : shapes) { - Quaternionf orientation = shape.getOrientation(); + Quaterniond orientation = shape.getOrientation(); if (relative) { shape.setOrientation(orientation.mul(rotation)); } else { diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffSetOrdering.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffSetOrdering.java index ada4414..962a650 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffSetOrdering.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffSetOrdering.java @@ -9,10 +9,10 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; import org.bukkit.event.Event; -import org.bukkit.util.Vector; import org.checkerframework.checker.nullness.qual.Nullable; +import org.joml.Vector3d; import java.util.Comparator; @@ -44,15 +44,15 @@ public boolean init(Expression[] expressions, int matchedPattern, Kleenean is @Override protected void execute(Event event) { - @Nullable Comparator order = switch (this.order) { + @Nullable Comparator order = switch (this.order) { case 1 -> (o1, o2) -> { - double value1 = o1.getX() + o1.getY() + o1.getZ(); - double value2 = o2.getX() + o2.getY() + o2.getZ(); + double value1 = o1.x + o1.y + o1.z; + double value2 = o2.x + o2.y + o2.z; return Double.compare(value1, value2); }; case 2 -> (o1, o2) -> { - double value1 = o1.getX() + o1.getY() + o1.getZ(); - double value2 = o2.getX() + o2.getY() + o2.getZ(); + double value1 = o1.x + o1.y + o1.z; + double value2 = o2.x + o2.y + o2.z; return -1 * Double.compare(value1, value2); }; default -> null; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffToggleAxes.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffToggleAxes.java index a7af564..9e701db 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffToggleAxes.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffToggleAxes.java @@ -9,7 +9,8 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; +import com.sovdee.skriptparticles.shapes.DrawData; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -50,10 +51,11 @@ public boolean init(Expression[] expressions, int i, Kleenean kleenean, Skrip protected void execute(Event event) { Shape[] shapes = shape.getArray(event); for (Shape shape : shapes) { + DrawData dd = DrawData.of(shape); if (globalFlag) - shape.showGlobalAxes(showFlag); + dd.showGlobalAxes(showFlag); if (localFlag) - shape.showLocalAxes(showFlag); + dd.showLocalAxes(showFlag); } } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprDrawnShapes.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprDrawnShapes.java index 30de270..e9874f6 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprDrawnShapes.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprDrawnShapes.java @@ -7,7 +7,7 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.EventValueExpression; import ch.njol.skript.lang.ExpressionType; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprShapeCopy.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprShapeCopy.java index 9861c9e..db30d40 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprShapeCopy.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprShapeCopy.java @@ -10,7 +10,7 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; import org.bukkit.event.Event; import org.checkerframework.checker.nullness.qual.NonNull; import org.jetbrains.annotations.Nullable; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprArc.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprArc.java index 07acc16..9985de0 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprArc.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprArc.java @@ -11,8 +11,10 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Arc; +import com.sovdee.shapes.Shape; +import com.sovdee.shapes.Shape.Style; +import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -93,9 +95,10 @@ protected Shape[] get(Event event) { height = Math.max(height.doubleValue(), 0); angle = MathUtil.clamp(angle.doubleValue(), 0, 2 * Math.PI); - DrawableShape shape = new DrawableShape(new com.sovdee.shapes.Arc(radius.doubleValue(), height.doubleValue(), angle.doubleValue())); + Arc shape = new Arc(radius.doubleValue(), height.doubleValue(), angle.doubleValue()); if (isSector) - shape.setStyle(Shape.Style.FILL); + shape.setStyle(Style.FILL); + shape.setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprBezierCurve.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprBezierCurve.java index 698474f..f53286a 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprBezierCurve.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprBezierCurve.java @@ -10,13 +10,16 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.DynamicBezierCurve; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.BezierCurve; +import com.sovdee.shapes.Shape; +import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.Point; +import com.sovdee.skriptparticles.util.VectorConversion; +import org.bukkit.Location; import org.bukkit.event.Event; import org.checkerframework.checker.nullness.qual.NonNull; import org.jetbrains.annotations.Nullable; +import org.joml.Vector3d; import java.util.ArrayList; import java.util.List; @@ -51,16 +54,32 @@ public boolean init(Expression[] exprs, int matchedPattern, @NonNull Kleenean @Override protected Shape @Nullable [] get(@NonNull Event event) { - @Nullable Point start = Point.of(this.start.getSingle(event)); - @Nullable Point end = Point.of(this.end.getSingle(event)); - if (start == null || end == null) + @Nullable Point startPt = Point.of(this.start.getSingle(event)); + @Nullable Point endPt = Point.of(this.end.getSingle(event)); + if (startPt == null || endPt == null) return null; - List> controlPoints = new ArrayList<>(); + List> controlPts = new ArrayList<>(); for (Object value : this.controlPoints.getArray(event)) { - controlPoints.add(Point.of(value)); + controlPts.add(Point.of(value)); } - return new Shape[]{new DrawableShape(new DynamicBezierCurve(start, end, controlPoints))}; + // Use Supplier-based BezierCurve for dynamic control points + BezierCurve curve = getBezierCurve(startPt, controlPts, endPt); + return new Shape[]{curve}; + } + + private static @NonNull BezierCurve getBezierCurve(@NonNull Point startPt, List> controlPts, @NonNull Point endPt) { + BezierCurve curve = new BezierCurve(() -> { + Location origin = startPt.getLocation(); + List result = new ArrayList<>(); + result.add(VectorConversion.toJOML(startPt.getVector(origin))); + for (Point cp : controlPts) + result.add(VectorConversion.toJOML(cp.getVector(origin))); + result.add(VectorConversion.toJOML(endPt.getVector(origin))); + return result; + }); + curve.setDrawContext(new DrawData()); + return curve; } @Override diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCircle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCircle.java index 23e1a8b..3bc2b3b 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCircle.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCircle.java @@ -11,8 +11,10 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Circle; +import com.sovdee.shapes.Shape; +import com.sovdee.shapes.Shape.Style; +import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; import org.checkerframework.checker.nullness.qual.NonNull; @@ -38,7 +40,7 @@ public class ExprCircle extends SimpleExpression { private Expression radius; private Expression height; - private Shape.Style style; + private Style style; private boolean isCylinder; @Override @@ -61,12 +63,12 @@ public boolean init(Expression[] exprs, int matchedPattern, @NonNull Kleenean } style = switch (parseResult.mark) { - case 0 -> Shape.Style.SURFACE; - case 1 -> Shape.Style.OUTLINE; - default -> Shape.Style.FILL; + case 0 -> Style.SURFACE; + case 1 -> Style.OUTLINE; + default -> Style.FILL; }; } else { - style = parseResult.hasTag("disc") ? Shape.Style.SURFACE : Shape.Style.OUTLINE; + style = parseResult.hasTag("disc") ? Style.SURFACE : Style.OUTLINE; } return true; } @@ -82,8 +84,9 @@ protected Shape[] get(@NonNull Event event) { radius = Math.max(radius.doubleValue(), MathUtil.EPSILON); height = Math.max(height.doubleValue(), 0); - DrawableShape shape = new DrawableShape(new com.sovdee.shapes.Circle(radius.doubleValue(), height.doubleValue())); + Circle shape = new Circle(radius.doubleValue(), height.doubleValue()); shape.setStyle(style); + shape.setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCuboid.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCuboid.java index 095cf1c..c8e2e79 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCuboid.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCuboid.java @@ -10,10 +10,10 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.DynamicCuboid; -import com.sovdee.skriptparticles.shapes.Shape; -import com.sovdee.skriptparticles.shapes.Shape.Style; +import com.sovdee.shapes.Cuboid; +import com.sovdee.shapes.Shape; +import com.sovdee.shapes.Shape.Style; +import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.DynamicLocation; import com.sovdee.skriptparticles.util.MathUtil; import com.sovdee.skriptparticles.util.VectorConversion; @@ -80,7 +80,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override @Nullable protected Shape[] get(Event event) { - DrawableShape shape; + Shape shape; // from width, length, height if (matchedPattern == 0) { if (width == null || length == null || height == null) return null; @@ -91,7 +91,7 @@ protected Shape[] get(Event event) { width = Math.max(width.doubleValue(), MathUtil.EPSILON); length = Math.max(length.doubleValue(), MathUtil.EPSILON); height = Math.max(height.doubleValue(), MathUtil.EPSILON); - shape = new DrawableShape(new com.sovdee.shapes.Cuboid(length.doubleValue(), width.doubleValue(), height.doubleValue())); + shape = new Cuboid(length.doubleValue(), width.doubleValue(), height.doubleValue()); // from location/entity/vector to location/entity/vector } else { if (corner1 == null || corner2 == null) return null; @@ -101,18 +101,23 @@ protected Shape[] get(Event event) { // vector check if (corner1 instanceof Vector && corner2 instanceof Vector) { - shape = new DrawableShape(new com.sovdee.shapes.Cuboid(VectorConversion.toJOML((Vector) corner1), VectorConversion.toJOML((Vector) corner2))); + shape = new Cuboid(VectorConversion.toJOML((Vector) corner1), VectorConversion.toJOML((Vector) corner2)); } else if (corner1 instanceof Vector || corner2 instanceof Vector) { return null; } else { - corner1 = DynamicLocation.fromLocationEntity(corner1); - corner2 = DynamicLocation.fromLocationEntity(corner2); - if (corner1 == null || corner2 == null) + DynamicLocation dl1 = DynamicLocation.fromLocationEntity(corner1); + DynamicLocation dl2 = DynamicLocation.fromLocationEntity(corner2); + if (dl1 == null || dl2 == null) return null; - shape = new DrawableShape(new DynamicCuboid((DynamicLocation) corner1, (DynamicLocation) corner2)); + // Use Supplier-based Cuboid for dynamic corners + shape = new Cuboid( + () -> VectorConversion.toJOML(dl1.getLocation().toVector()), + () -> VectorConversion.toJOML(dl2.getLocation().toVector()) + ); } } shape.setStyle(style); + shape.setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipse.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipse.java index f304e1f..39d754c 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipse.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipse.java @@ -11,9 +11,10 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.Shape; -import com.sovdee.skriptparticles.shapes.Shape.Style; +import com.sovdee.shapes.Ellipse; +import com.sovdee.shapes.Shape; +import com.sovdee.shapes.Shape.Style; +import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -55,9 +56,9 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye if (matchedPattern == 1) { height = (Expression) exprs[2]; style = switch (parseResult.mark) { - case 0 -> Shape.Style.SURFACE; - case 1 -> Shape.Style.OUTLINE; - default -> Shape.Style.FILL; + case 0 -> Style.SURFACE; + case 1 -> Style.OUTLINE; + default -> Style.FILL; }; } @@ -95,8 +96,9 @@ protected Shape[] get(Event event) { zRadius = Math.max(zRadius.doubleValue(), MathUtil.EPSILON); height = Math.max(height.doubleValue(), 0); - DrawableShape shape = new DrawableShape(new com.sovdee.shapes.Ellipse(xRadius.doubleValue(), zRadius.doubleValue(), height.doubleValue())); + Ellipse shape = new Ellipse(xRadius.doubleValue(), zRadius.doubleValue(), height.doubleValue()); shape.setStyle(style); + shape.setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipsoid.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipsoid.java index c3da3d9..14c64e7 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipsoid.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipsoid.java @@ -11,9 +11,10 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.Shape; -import com.sovdee.skriptparticles.shapes.Shape.Style; +import com.sovdee.shapes.Ellipsoid; +import com.sovdee.shapes.Shape; +import com.sovdee.shapes.Shape.Style; +import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -86,8 +87,9 @@ protected Shape[] get(Event event) { yRadius = Math.max(yRadius.doubleValue(), MathUtil.EPSILON); zRadius = Math.max(zRadius.doubleValue(), MathUtil.EPSILON); - DrawableShape shape = new DrawableShape(new com.sovdee.shapes.Ellipsoid(xRadius.doubleValue(), yRadius.doubleValue(), zRadius.doubleValue())); + Ellipsoid shape = new Ellipsoid(xRadius.doubleValue(), yRadius.doubleValue(), zRadius.doubleValue()); shape.setStyle(style); + shape.setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipticalArc.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipticalArc.java index 3cfc14e..4318d1c 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipticalArc.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipticalArc.java @@ -11,9 +11,10 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.Shape; -import com.sovdee.skriptparticles.shapes.Shape.Style; +import com.sovdee.shapes.EllipticalArc; +import com.sovdee.shapes.Shape; +import com.sovdee.shapes.Shape.Style; +import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -107,8 +108,9 @@ protected Shape[] get(Event event) { angle = Math.toRadians(angle.doubleValue()); angle = MathUtil.clamp(angle.doubleValue(), 0, Math.PI * 2); - DrawableShape shape = new DrawableShape(new com.sovdee.shapes.EllipticalArc(xRadius.doubleValue(), zRadius.doubleValue(), height.doubleValue(), angle.doubleValue())); + EllipticalArc shape = new EllipticalArc(xRadius.doubleValue(), zRadius.doubleValue(), height.doubleValue(), angle.doubleValue()); shape.setStyle(style); + shape.setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHeart.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHeart.java index 7bc0781..bbbdb69 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHeart.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHeart.java @@ -11,8 +11,10 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Heart; +import com.sovdee.shapes.Shape; +import com.sovdee.shapes.Shape.Style; +import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -82,10 +84,11 @@ protected Shape[] get(Event event) { length = Math.max(length.doubleValue(), MathUtil.EPSILON); eccentricity = Math.max(eccentricity.doubleValue(), 1); - DrawableShape shape = new DrawableShape(new com.sovdee.shapes.Heart(width.doubleValue(), length.doubleValue(), eccentricity.doubleValue())); + Heart shape = new Heart(width.doubleValue(), length.doubleValue(), eccentricity.doubleValue()); if (isSolid) { - shape.setStyle(Shape.Style.SURFACE); + shape.setStyle(Style.SURFACE); } + shape.setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHelix.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHelix.java index 43d26a2..5f01c46 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHelix.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHelix.java @@ -11,9 +11,10 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.Shape; -import com.sovdee.skriptparticles.shapes.Shape.Style; +import com.sovdee.shapes.Helix; +import com.sovdee.shapes.Shape; +import com.sovdee.shapes.Shape.Style; +import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; import org.checkerframework.checker.nullness.qual.Nullable; @@ -87,8 +88,9 @@ protected Shape[] get(Event event) { height = Math.max(height.doubleValue(), MathUtil.EPSILON); double slope = 1.0 / Math.max(windingRate.doubleValue(), MathUtil.EPSILON); int direction = isClockwise ? 1 : -1; - DrawableShape shape = new DrawableShape(new com.sovdee.shapes.Helix(radius.doubleValue(), height.doubleValue(), slope / (2 * Math.PI), direction)); + Helix shape = new Helix(radius.doubleValue(), height.doubleValue(), slope / (2 * Math.PI), direction); shape.setStyle(style); + shape.setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprIrregularPolygon.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprIrregularPolygon.java index 670cc79..2f4b531 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprIrregularPolygon.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprIrregularPolygon.java @@ -11,8 +11,9 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.IrregularPolygon; +import com.sovdee.shapes.Shape; +import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.DynamicLocation; import com.sovdee.skriptparticles.util.VectorConversion; import org.bukkit.Location; @@ -80,15 +81,15 @@ protected Shape[] get(Event event) { } } Number height = this.height != null ? this.height.getSingle(event) : null; - com.sovdee.shapes.IrregularPolygon libPolygon; + IrregularPolygon shape; if (height != null) { - libPolygon = new com.sovdee.shapes.IrregularPolygon(vertices, height.doubleValue()); + shape = new IrregularPolygon(vertices, height.doubleValue()); } else { - libPolygon = new com.sovdee.shapes.IrregularPolygon(vertices); + shape = new IrregularPolygon(vertices); } - DrawableShape shape = new DrawableShape(libPolygon); + shape.setDrawContext(new DrawData()); if (locationOffset != null) { - shape.setLocation(new DynamicLocation((Location) points[0])); + DrawData.of(shape).setLocation(new DynamicLocation((Location) points[0])); } return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprLine.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprLine.java index 9764cf7..887ab95 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprLine.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprLine.java @@ -11,9 +11,9 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.DynamicLine; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Line; +import com.sovdee.shapes.Shape; +import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.DynamicLocation; import com.sovdee.skriptparticles.util.MathUtil; import com.sovdee.skriptparticles.util.VectorConversion; @@ -88,6 +88,11 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye return true; } + private Shape attachDrawData(Shape shape) { + shape.setDrawContext(new DrawData()); + return shape; + } + @Override @Nullable protected Shape[] get(Event event) { @@ -99,7 +104,7 @@ protected Shape[] get(Event event) { for (Object startObject : start) { for (Object endObject : end) { if (startObject instanceof Vector startVector && endObject instanceof Vector endVector) { - lines.add(new DrawableShape(new com.sovdee.shapes.Line(VectorConversion.toJOML(startVector), VectorConversion.toJOML(endVector)))); + lines.add(attachDrawData(new Line(VectorConversion.toJOML(startVector), VectorConversion.toJOML(endVector)))); continue; } else if (startObject instanceof Vector || endObject instanceof Vector) { continue; @@ -109,7 +114,11 @@ protected Shape[] get(Event event) { if (endPoint == null || startPoint == null) { continue; } - lines.add(new DrawableShape(new DynamicLine(startPoint, endPoint))); + // Use Supplier-based Line for dynamic endpoints + lines.add(attachDrawData(new Line( + () -> VectorConversion.toJOML(startPoint.getLocation().toVector()), + () -> VectorConversion.toJOML(endPoint.getLocation().toVector()) + ))); } } } @@ -122,13 +131,13 @@ protected Shape[] get(Event event) { if (length != null) v.multiply(Math.max(length.doubleValue(), MathUtil.EPSILON)); - lines.add(new DrawableShape(new com.sovdee.shapes.Line(VectorConversion.toJOML(v)))); + lines.add(attachDrawData(new Line(VectorConversion.toJOML(v)))); } case 2 -> { Object[] points = this.points.getArray(event); if (points instanceof Vector[] vectors) { for (int i = 0; i < vectors.length - 1; i++) { - lines.add(new DrawableShape(new com.sovdee.shapes.Line(VectorConversion.toJOML(vectors[i]), VectorConversion.toJOML(vectors[i + 1])))); + lines.add(attachDrawData(new Line(VectorConversion.toJOML(vectors[i]), VectorConversion.toJOML(vectors[i + 1])))); } } else { List locations = new ArrayList<>(); @@ -141,7 +150,12 @@ protected Shape[] get(Event event) { locations.add(location); } for (int i = 0; i < locations.size() - 1; i++) { - lines.add(new DrawableShape(new DynamicLine(locations.get(i), locations.get(i + 1)))); + DynamicLocation startLoc = locations.get(i); + DynamicLocation endLoc = locations.get(i + 1); + lines.add(attachDrawData(new Line( + () -> VectorConversion.toJOML(startLoc.getLocation().toVector()), + () -> VectorConversion.toJOML(endLoc.getLocation().toVector()) + ))); } } } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRectangle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRectangle.java index b236b23..c0437ee 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRectangle.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRectangle.java @@ -10,10 +10,11 @@ import ch.njol.skript.lang.SkriptParser; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; +import com.sovdee.shapes.Rectangle; import com.sovdee.shapes.Rectangle.Plane; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.DynamicRectangle; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; +import com.sovdee.shapes.Shape.Style; +import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.DynamicLocation; import com.sovdee.skriptparticles.util.MathUtil; import com.sovdee.skriptparticles.util.VectorConversion; @@ -50,7 +51,7 @@ public class ExprRectangle extends SimpleExpression { private Expression lengthExpr; private Expression widthExpr; - private Shape.Style style; + private Style style; private Expression corner1Expr; private Expression corner2Expr; private int matchedPattern; @@ -58,7 +59,7 @@ public class ExprRectangle extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { - style = parseResult.hasTag("solid") ? Shape.Style.SURFACE : Shape.Style.OUTLINE; + style = parseResult.hasTag("solid") ? Style.SURFACE : Style.OUTLINE; this.matchedPattern = matchedPattern; if (matchedPattern == 0) { lengthExpr = (Expression) exprs[0]; @@ -75,7 +76,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override protected @Nullable Shape[] get(Event event) { - DrawableShape shape; + Shape shape; if (matchedPattern == 0) { if (lengthExpr == null || widthExpr == null) return null; Number length = lengthExpr.getSingle(event); @@ -83,7 +84,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye if (length == null || width == null) return null; length = Math.max(length.doubleValue(), MathUtil.EPSILON); width = Math.max(width.doubleValue(), MathUtil.EPSILON); - shape = new DrawableShape(new com.sovdee.shapes.Rectangle(length.doubleValue(), width.doubleValue(), plane)); + shape = new Rectangle(length.doubleValue(), width.doubleValue(), plane); } else { if (corner1Expr == null || corner2Expr == null) return null; Object corner1 = corner1Expr.getSingle(event); @@ -91,18 +92,24 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye if (corner1 == null || corner2 == null) return null; if (corner1 instanceof Vector && corner2 instanceof Vector) { - shape = new DrawableShape(new com.sovdee.shapes.Rectangle(VectorConversion.toJOML((Vector) corner1), VectorConversion.toJOML((Vector) corner2), plane)); + shape = new Rectangle(VectorConversion.toJOML((Vector) corner1), VectorConversion.toJOML((Vector) corner2), plane); } else if (corner1 instanceof Vector || corner2 instanceof Vector) { return null; } else { - corner1 = DynamicLocation.fromLocationEntity(corner1); - corner2 = DynamicLocation.fromLocationEntity(corner2); - if (corner1 == null || corner2 == null) + DynamicLocation dl1 = DynamicLocation.fromLocationEntity(corner1); + DynamicLocation dl2 = DynamicLocation.fromLocationEntity(corner2); + if (dl1 == null || dl2 == null) return null; - shape = new DrawableShape(new DynamicRectangle((DynamicLocation) corner1, (DynamicLocation) corner2, plane)); + // Use Supplier-based Rectangle for dynamic corners + shape = new Rectangle( + () -> VectorConversion.toJOML(dl1.getLocation().toVector()), + () -> VectorConversion.toJOML(dl2.getLocation().toVector()), + plane + ); } } shape.setStyle(style); + shape.setDrawContext(new DrawData()); return new Shape[]{shape}; } @@ -118,7 +125,7 @@ public Class getReturnType() { @Override public String toString(@Nullable Event event, boolean debug) { - return (style == Shape.Style.SURFACE ? "solid " : "") + + return (style == Style.SURFACE ? "solid " : "") + switch (plane) { case XZ -> " xz "; case XY -> " xy "; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolygon.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolygon.java index f6d3808..af46763 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolygon.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolygon.java @@ -11,9 +11,10 @@ import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.skript.lang.util.SimpleLiteral; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.Shape; -import com.sovdee.skriptparticles.shapes.Shape.Style; +import com.sovdee.shapes.RegularPolygon; +import com.sovdee.shapes.Shape; +import com.sovdee.shapes.Shape.Style; +import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -110,8 +111,9 @@ protected Shape[] get(Event event) { sides = Math.max(sides.intValue(), 3); radius = Math.max(radius.doubleValue(), MathUtil.EPSILON); - DrawableShape shape = new DrawableShape(new com.sovdee.shapes.RegularPolygon(sides.intValue(), radius.doubleValue())); + RegularPolygon shape = new RegularPolygon(sides.intValue(), radius.doubleValue()); shape.setStyle(style); + shape.setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolyhedron.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolyhedron.java index d3b986a..396056f 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolyhedron.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolyhedron.java @@ -10,9 +10,10 @@ import ch.njol.skript.lang.SkriptParser; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.Shape; -import com.sovdee.skriptparticles.shapes.Shape.Style; +import com.sovdee.shapes.RegularPolyhedron; +import com.sovdee.shapes.Shape; +import com.sovdee.shapes.Shape.Style; +import com.sovdee.skriptparticles.shapes.DrawData; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -42,7 +43,7 @@ public class ExprRegularPolyhedron extends SimpleExpression { public boolean init(Expression[] expressions, int i, Kleenean kleenean, SkriptParser.ParseResult parseResult) { radius = (Expression) expressions[0]; faces = parseResult.hasTag("tetra") ? 4 : parseResult.hasTag("octa") ? 8 : parseResult.hasTag("dodeca") ? 12 : 20; - style = parseResult.hasTag("hollow") ? Shape.Style.SURFACE : parseResult.hasTag("solid") ? Shape.Style.FILL : Shape.Style.OUTLINE; + style = parseResult.hasTag("hollow") ? Style.SURFACE : parseResult.hasTag("solid") ? Style.FILL : Style.OUTLINE; if (radius instanceof Literal literal && literal.getSingle().doubleValue() <= 0) { Skript.error("The radius of the polyhedron must be greater than 0. (radius: " + @@ -57,8 +58,9 @@ public boolean init(Expression[] expressions, int i, Kleenean kleenean, Skrip protected @Nullable Shape[] get(Event event) { if (radius.getSingle(event) == null) return new Shape[0]; - DrawableShape shape = new DrawableShape(new com.sovdee.shapes.RegularPolyhedron(radius.getSingle(event).doubleValue(), faces)); + RegularPolyhedron shape = new RegularPolyhedron(radius.getSingle(event).doubleValue(), faces); shape.setStyle(style); + shape.setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphere.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphere.java index aeedef8..bea1b6d 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphere.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphere.java @@ -11,8 +11,10 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; +import com.sovdee.shapes.Shape.Style; +import com.sovdee.shapes.Sphere; +import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; import org.jetbrains.annotations.NotNull; @@ -56,8 +58,9 @@ protected Shape[] get(@NotNull Event event) { radius = Math.max(radius.doubleValue(), MathUtil.EPSILON); - DrawableShape shape = new DrawableShape(new com.sovdee.shapes.Sphere(radius.doubleValue())); - if (isSolid) shape.setStyle(Shape.Style.FILL); + Sphere shape = new Sphere(radius.doubleValue()); + if (isSolid) shape.setStyle(Style.FILL); + shape.setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphericalCap.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphericalCap.java index 3d69302..5687bcc 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphericalCap.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphericalCap.java @@ -11,8 +11,10 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; +import com.sovdee.shapes.Shape.Style; +import com.sovdee.shapes.SphericalCap; +import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -75,9 +77,10 @@ protected Shape[] get(Event event) { if (!isRadians) angle = Math.toRadians(angle.doubleValue()); - DrawableShape shape = new DrawableShape(new com.sovdee.shapes.SphericalCap(radius.doubleValue(), angle.doubleValue())); + SphericalCap shape = new SphericalCap(radius.doubleValue(), angle.doubleValue()); if (isSector) - shape.setStyle(Shape.Style.FILL); + shape.setStyle(Style.FILL); + shape.setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprStar.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprStar.java index 906793c..5ef3713 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprStar.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprStar.java @@ -10,8 +10,10 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; +import com.sovdee.shapes.Shape.Style; +import com.sovdee.shapes.Star; +import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -75,10 +77,10 @@ protected Shape[] get(Event event) { innerRadius = Math.max(innerRadius.doubleValue(), MathUtil.EPSILON); outerRadius = Math.max(outerRadius.doubleValue(), MathUtil.EPSILON); - DrawableShape shape = new DrawableShape(new com.sovdee.shapes.Star(innerRadius.doubleValue(), outerRadius.doubleValue(), angle)); + Star shape = new Star(innerRadius.doubleValue(), outerRadius.doubleValue(), angle); if (isSolid) - shape.setStyle(Shape.Style.SURFACE); - + shape.setStyle(Style.SURFACE); + shape.setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprHelixWindingRate.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprHelixWindingRate.java index 7418580..807568c 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprHelixWindingRate.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprHelixWindingRate.java @@ -7,8 +7,7 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; import com.sovdee.shapes.Helix; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -33,7 +32,7 @@ public class ExprHelixWindingRate extends SimplePropertyExpression[] exprs, int matchedPattern, Kleenean isDelaye @Override @Nullable public Number convert(Shape shape) { - if (!(shape instanceof DrawableShape ds && ds.getShape() instanceof LWHShape lwhShape)) + if (!(shape instanceof LWHShape lwhShape)) return null; return switch (lwh) { case 0 -> lwhShape.getLength(); @@ -75,7 +74,7 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { value = -value; case ADD: for (Shape shape : getExpr().getArray(event)) { - if (shape instanceof DrawableShape ds && ds.getShape() instanceof LWHShape lwhShape) { + if (shape instanceof LWHShape lwhShape) { switch (lwh) { case 0 -> lwhShape.setLength(lwhShape.getLength() + value); case 1 -> lwhShape.setWidth(lwhShape.getWidth() + value); @@ -88,7 +87,7 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { case RESET: case SET: for (Shape shape : getExpr().getArray(event)) { - if (shape instanceof DrawableShape ds && ds.getShape() instanceof LWHShape lwhShape) { + if (shape instanceof LWHShape lwhShape) { switch (lwh) { case 0 -> lwhShape.setLength(value); case 1 -> lwhShape.setWidth(value); diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLocations.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLocations.java index 62e3636..607cdfb 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLocations.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLocations.java @@ -11,7 +11,8 @@ import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.skript.util.Direction; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; +import com.sovdee.skriptparticles.util.VectorConversion; import org.bukkit.Location; import org.bukkit.event.Event; import org.checkerframework.checker.nullness.qual.NonNull; @@ -61,7 +62,7 @@ protected Location[] get(Event event) { ArrayList locations = new ArrayList<>(); for (Shape shape : shapes) { - locations.addAll(shape.getPoints().stream().map(point -> center.clone().add(point)).toList()); + locations.addAll(VectorConversion.toBukkit(shape.getPoints()).stream().map(point -> center.clone().add(point)).toList()); } return locations.toArray(new Location[0]); } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeNormal.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeNormal.java index c74ad03..90d3de9 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeNormal.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeNormal.java @@ -7,8 +7,10 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.PropertyExpression; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; import com.sovdee.skriptparticles.util.Quaternion; +import com.sovdee.skriptparticles.util.VectorConversion; +import org.joml.Quaterniond; import org.bukkit.event.Event; import org.bukkit.util.Vector; import org.jetbrains.annotations.Nullable; @@ -33,7 +35,7 @@ public class ExprShapeNormal extends SimplePropertyExpression { @Override public Vector convert(Shape shape) { - return shape.getRelativeYAxis(false); + return VectorConversion.toBukkit(shape.getRelativeYAxis(false)); } @Override @@ -50,14 +52,15 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { switch (mode) { case SET: if (delta == null || delta.length == 0) return; + Quaternion q = new Quaternion().rotationTo((Vector) delta[0]); for (Shape shape : getExpr().getArray(event)) { - shape.setOrientation(new Quaternion().rotationTo((Vector) delta[0])); + shape.setOrientation(new Quaterniond(q.x, q.y, q.z, q.w)); } break; case RESET: case DELETE: for (Shape shape : getExpr().getArray(event)) { - shape.setOrientation(Quaternion.IDENTITY); + shape.setOrientation(new Quaterniond()); } break; case ADD: diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOffset.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOffset.java index 5a4ec56..f61c64f 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOffset.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOffset.java @@ -6,9 +6,11 @@ import ch.njol.skript.doc.Name; import ch.njol.skript.expressions.base.PropertyExpression; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; +import com.sovdee.skriptparticles.util.VectorConversion; import org.bukkit.event.Event; import org.bukkit.util.Vector; +import org.joml.Vector3d; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,7 +32,7 @@ public class ExprShapeOffset extends SimplePropertyExpression { @Override public @Nullable Vector convert(Shape shape) { - return shape.getOffset(); + return VectorConversion.toBukkit(shape.getOffset()); } @Override @@ -49,13 +51,13 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { if (delta == null || delta.length == 0) return; for (Shape shape : getExpr().getArray(event)) { - shape.setOffset(((Vector) delta[0])); + shape.setOffset(VectorConversion.toJOML((Vector) delta[0])); } break; case RESET: case DELETE: for (Shape shape : getExpr().getArray(event)) { - shape.setOffset(new Vector(0, 0, 0)); + shape.setOffset(new Vector3d(0, 0, 0)); } break; case ADD: diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOrientation.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOrientation.java index efe5ed6..42a50bb 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOrientation.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOrientation.java @@ -7,8 +7,9 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.PropertyExpression; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; import com.sovdee.skriptparticles.util.Quaternion; +import org.joml.Quaterniond; import org.bukkit.event.Event; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -35,7 +36,8 @@ public class ExprShapeOrientation extends SimplePropertyExpression @Override @Nullable public Particle convert(Shape shape) { - return shape.getParticle(); + return DrawData.of(shape).getParticle(); } @Override @@ -58,12 +59,12 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { case SET: if (delta == null || delta.length == 0) return; for (Shape shape : getExpr().getArray(event)) - shape.setParticle((Particle) delta[0]); + DrawData.of(shape).setParticle((Particle) delta[0]); break; case RESET: case DELETE: for (Shape shape : getExpr().getArray(event)) - shape.setParticle(ParticleUtil.getDefaultParticle()); + DrawData.of(shape).setParticle(ParticleUtil.getDefaultParticle()); break; case ADD: case REMOVE: diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticleDensity.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticleDensity.java index c5bb4f2..7da9ddd 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticleDensity.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticleDensity.java @@ -10,7 +10,7 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapePoints.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapePoints.java index c91e3ea..8f2d1c9 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapePoints.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapePoints.java @@ -8,7 +8,8 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; +import com.sovdee.skriptparticles.util.VectorConversion; import org.bukkit.event.Event; import org.bukkit.util.Vector; import org.jetbrains.annotations.Nullable; @@ -44,7 +45,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye protected Vector[] get(Event event, Shape[] source) { List points = new ArrayList<>(); for (Shape shape : source) { - points.addAll(shape.getPoints()); + points.addAll(VectorConversion.toBukkit(shape.getPoints())); } return points.toArray(new Vector[0]); } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRadius.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRadius.java index e1adbc7..c68dde2 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRadius.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRadius.java @@ -8,8 +8,7 @@ import ch.njol.skript.expressions.base.PropertyExpression; import ch.njol.skript.expressions.base.SimplePropertyExpression; import com.sovdee.shapes.RadialShape; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -34,7 +33,7 @@ public class ExprShapeRadius extends SimplePropertyExpression { @Override @Nullable public Number convert(Shape shape) { - if (shape instanceof DrawableShape ds && ds.getShape() instanceof RadialShape rs) + if (shape instanceof RadialShape rs) return rs.getRadius(); return null; } @@ -60,7 +59,7 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { deltaValue = -deltaValue; case ADD: for (Shape shape : shapes) { - if (shape instanceof DrawableShape ds && ds.getShape() instanceof RadialShape rs) + if (shape instanceof RadialShape rs) rs.setRadius(Math.max(0.001, rs.getRadius() + deltaValue)); } break; @@ -69,7 +68,7 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { case SET: deltaValue = Math.max(0.001, deltaValue); for (Shape shape : shapes) { - if (shape instanceof DrawableShape ds && ds.getShape() instanceof RadialShape rs) + if (shape instanceof RadialShape rs) rs.setRadius(deltaValue); } break; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRelativeAxis.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRelativeAxis.java index c856f67..c709680 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRelativeAxis.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRelativeAxis.java @@ -9,7 +9,8 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser; import ch.njol.util.Kleenean; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; +import com.sovdee.skriptparticles.util.VectorConversion; import org.bukkit.util.Vector; import org.jetbrains.annotations.Nullable; @@ -44,9 +45,9 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override public @Nullable Vector convert(Shape shape) { return switch (axis) { - case 0 -> shape.getRelativeXAxis(false); - case 1 -> shape.getRelativeYAxis(false); - case 2 -> shape.getRelativeZAxis(false); + case 0 -> VectorConversion.toBukkit(shape.getRelativeXAxis(false)); + case 1 -> VectorConversion.toBukkit(shape.getRelativeYAxis(false)); + case 2 -> VectorConversion.toBukkit(shape.getRelativeZAxis(false)); default -> null; }; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeScale.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeScale.java index 5ccc8e5..53e3221 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeScale.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeScale.java @@ -7,7 +7,7 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.PropertyExpression; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSideLength.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSideLength.java index d297db6..3a379b5 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSideLength.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSideLength.java @@ -7,8 +7,7 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; import com.sovdee.shapes.PolyShape; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -34,7 +33,7 @@ public class ExprShapeSideLength extends SimplePropertyExpression @Override @Nullable public Number convert(Shape shape) { - if (shape instanceof DrawableShape ds && ds.getShape() instanceof PolyShape ps) + if (shape instanceof PolyShape ps) return ps.getSideLength(); return null; } @@ -57,7 +56,7 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { change = -change; case ADD: for (Shape shape : getExpr().getArray(event)) { - if (shape instanceof DrawableShape ds && ds.getShape() instanceof PolyShape ps) + if (shape instanceof PolyShape ps) ps.setSideLength(Math.max(0.001, ps.getSideLength() + change)); } break; @@ -66,7 +65,7 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { case SET: change = Math.max(0.001, change); for (Shape shape : getExpr().getArray(event)) { - if (shape instanceof DrawableShape ds && ds.getShape() instanceof PolyShape ps) + if (shape instanceof PolyShape ps) ps.setSideLength(change); } break; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSides.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSides.java index 99a95f3..fd1524d 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSides.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSides.java @@ -7,8 +7,7 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; import com.sovdee.shapes.PolyShape; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -34,7 +33,7 @@ public class ExprShapeSides extends SimplePropertyExpression { @Override @Nullable public Integer convert(Shape shape) { - if (shape instanceof DrawableShape ds && ds.getShape() instanceof PolyShape ps) + if (shape instanceof PolyShape ps) return ps.getSides(); return null; } @@ -57,7 +56,7 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { change = -change; case ADD: for (Shape shape : getExpr().getArray(event)) { - if (shape instanceof DrawableShape ds && ds.getShape() instanceof PolyShape ps) + if (shape instanceof PolyShape ps) ps.setSides(Math.max(3, ps.getSides() + change)); } break; @@ -66,7 +65,7 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { case SET: change = Math.max(3, change); for (Shape shape : getExpr().getArray(event)) { - if (shape instanceof DrawableShape ds && ds.getShape() instanceof PolyShape ps) + if (shape instanceof PolyShape ps) ps.setSides(change); } break; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeStyle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeStyle.java index e4f27ec..b67d5b5 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeStyle.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeStyle.java @@ -7,7 +7,7 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.PropertyExpression; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarPoints.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarPoints.java index 0bdb6ed..81ae6a2 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarPoints.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarPoints.java @@ -7,8 +7,7 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; import com.sovdee.shapes.Star; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -31,7 +30,7 @@ public class ExprStarPoints extends SimplePropertyExpression { @Override public @Nullable Number convert(Shape shape) { - if (shape instanceof DrawableShape ds && ds.getShape() instanceof Star star) + if (shape instanceof Star star) return star.getStarPoints(); return null; } @@ -56,7 +55,7 @@ public void change(Event event, Object @Nullable [] delta, Changer.ChangeMode mo deltaValue = -deltaValue; case ADD: for (Shape shape : shapes) { - if (shape instanceof DrawableShape ds && ds.getShape() instanceof Star star) { + if (shape instanceof Star star) { star.setStarPoints(Math.max(star.getStarPoints() + deltaValue, 2)); } } @@ -64,7 +63,7 @@ public void change(Event event, Object @Nullable [] delta, Changer.ChangeMode mo case SET: deltaValue = Math.max(deltaValue, 2); for (Shape shape : shapes) { - if (shape instanceof DrawableShape ds && ds.getShape() instanceof Star star) { + if (shape instanceof Star star) { star.setStarPoints(deltaValue); } } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarRadii.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarRadii.java index 1e7abb1..d555dab 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarRadii.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarRadii.java @@ -9,10 +9,9 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.util.Kleenean; +import com.sovdee.shapes.Shape; import com.sovdee.shapes.Star; import com.sovdee.shapes.util.MathUtil; -import com.sovdee.skriptparticles.shapes.DrawableShape; -import com.sovdee.skriptparticles.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -44,7 +43,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override public @Nullable Number convert(Shape shape) { - if (shape instanceof DrawableShape ds && ds.getShape() instanceof Star star) { + if (shape instanceof Star star) { return (isInner ? star.getInnerRadius() : star.getOuterRadius()); } return null; @@ -70,7 +69,7 @@ public void change(Event event, Object @Nullable [] delta, Changer.ChangeMode mo deltaValue = -deltaValue; case ADD: for (Shape shape : shapes) { - if (shape instanceof DrawableShape ds && ds.getShape() instanceof Star star) { + if (shape instanceof Star star) { if (isInner) { star.setInnerRadius(Math.max(star.getInnerRadius() + deltaValue, MathUtil.EPSILON)); } else { @@ -82,7 +81,7 @@ public void change(Event event, Object @Nullable [] delta, Changer.ChangeMode mo case SET: deltaValue = Math.max(deltaValue, MathUtil.EPSILON); for (Shape shape : shapes) { - if (shape instanceof DrawableShape ds && ds.getShape() instanceof Star star) { + if (shape instanceof Star star) { if (isInner) { star.setInnerRadius(deltaValue); } else { diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/DrawShapeEffectSection.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/DrawShapeEffectSection.java index 75fd27d..1a58185 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/DrawShapeEffectSection.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/DrawShapeEffectSection.java @@ -15,8 +15,9 @@ import ch.njol.skript.util.Timespan.TimePeriod; import ch.njol.skript.variables.Variables; import ch.njol.util.Kleenean; +import com.sovdee.shapes.Shape; import com.sovdee.skriptparticles.SkriptParticle; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.skriptparticles.shapes.DrawManager; import com.sovdee.skriptparticles.util.DynamicLocation; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -55,23 +56,8 @@ public abstract class DrawShapeEffectSection extends EffectSection { protected boolean useShapeLocation; protected boolean sync; - /** - * Called just after the constructor. Handles checking for delays in the section body and setting the sync tag, then - * passes execution to {@link #init(Expression[], int, Kleenean, ParseResult, boolean)}. - * - * @param expressions all %expr%s included in the matching pattern in the order they appear in the pattern. If an optional value was left out it will still be included in this list - * holding the default value of the desired type which usually depends on the event. - * @param matchedPattern The index of the pattern which matched - * @param isDelayed Whether this expression is used after a delay or not (i.e. if the event has already passed when this expression will be called) - * @param parseResult Additional information about the match. - * @param sectionNode The section node that represents this section. - * @param list A list of {@link TriggerItem}s that belong to this section. This list is modifiable. - * @return Whether this expression was initialised successfully. An error should be printed prior to returning false to specify the cause. - * @see ParserInstance#isCurrentEvent(Class...) - */ @Override public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult, @Nullable SectionNode sectionNode, @Nullable List list) { - // handle delays in the section body if (hasSection()) { AtomicBoolean delayed = new AtomicBoolean(false); Runnable afterLoading = () -> delayed.set(!getParser().getHasDelayBefore().isFalse()); @@ -85,22 +71,6 @@ public boolean init(Expression[] expressions, int matchedPattern, Kleenean is return init(expressions, matchedPattern, isDelayed, parseResult, hasSection()); } - /** - * Called just after the constructor. By default, this method does sets the following fields: - * - {@link #shapes} to the first expression - * - {@link #directions} to the second expression - * - {@link #locations} to the third expression - * - {@link #players} to the fourth expression - * - {@link #sync} to whether the sync tag was present - * - * @param expressions all %expr%s included in the matching pattern in the order they appear in the pattern. If an optional value was left out it will still be included in this list - * holding the default value of the desired type which usually depends on the event. - * @param matchedPattern The index of the pattern which matched - * @param isDelayed Whether this expression is used after a delay or not (i.e. if the event has already passed when this expression will be called) - * @param parseResult Additional information about the match. - * @param hasSection Whether this section had a valid section body. - * @return Whether this expression was initialised successfully. An error should be printed prior to returning false to specify the cause - */ public boolean init(@Nullable Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult, boolean hasSection) { shapes = (Expression) expressions[0]; @@ -123,7 +93,7 @@ public boolean init(@Nullable Expression[] expressions, int matchedPattern, K protected TriggerItem walk(Event event) { debug(event, true); - Delay.addDelayedEvent(event); // Mark this event as delayed + Delay.addDelayedEvent(event); Collection recipients = new ArrayList<>(); if (players != null) { @@ -144,7 +114,6 @@ protected TriggerItem walk(Event event) { consumer = null; } - // Figure out what locations to draw at, or what entities to follow List locations = new ArrayList<>(); @Nullable Direction direction = null; if (!useShapeLocation) { @@ -159,15 +128,12 @@ protected TriggerItem walk(Event event) { } } } else { - // blank value means use shape location locations.add(new DynamicLocation()); } if (sync) { executeSync(event, locations, consumer, recipients); } else { - // Clone shapes and run Consumer before going async - // We can't guarantee that the consumer will be thread-safe, so we need do this before going async List preppedShapes = new ArrayList<>(); Shape preppedShape; for (Shape shape : shapes.getArray(event)) { @@ -184,13 +150,6 @@ protected TriggerItem walk(Event event) { return getNext(); } - /** - * Sets up the async task to draw the shapes at the given locations for the given recipients. - * Should be called from {@link #walk(Event)} if {@link #sync} is false, and will return the next trigger item to run. - * @param locations the locations to draw the shapes at - * @param shapes the shapes to draw - * @param recipients the players to draw the shapes for - */ protected void setupAsync(Event event, Collection locations, Collection shapes, Collection recipients) { BukkitRunnable runnable = new BukkitRunnable() { @Override @@ -202,14 +161,14 @@ public void run() { } protected void executeSync(Event event, Collection locations, @Nullable Consumer consumer, Collection recipients) { - Shape shapeCopy; try { for (DynamicLocation dynamicLocation : locations) { for (Shape shape : shapes.getArray(event)) { + Shape clone = shape.clone(); if (consumer != null) { - shape.clone().draw(dynamicLocation, consumer, recipients); + DrawManager.drawWithConsumer(clone, dynamicLocation, consumer, recipients); } else { - shape.clone().draw(dynamicLocation, recipients); + DrawManager.draw(clone, dynamicLocation, recipients); } } } @@ -226,7 +185,7 @@ protected void executeAsync(Collection locations, Collection locations, Co Timespan duration = this.duration.getOptionalSingle(event).orElse(new Timespan(TimePeriod.TICK, 0)); long milliseconds = duration.getAs(TimePeriod.MILLISECOND); for (Shape shape : shapes) { - shape.setAnimationDuration(milliseconds); + DrawData.of(shape).setAnimationDuration(milliseconds); } super.setupAsync(event, locations, shapes, recipients); } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java index 1ff19cb..2a9e0d2 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java @@ -4,12 +4,12 @@ import ch.njol.skript.classes.Parser; import ch.njol.skript.lang.ParseContext; import ch.njol.skript.registrations.Classes; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; import org.jetbrains.annotations.Nullable; public class ShapeTypes { static { - // Shape + // Shape — register the library shape directly Classes.registerClass(new ClassInfo<>(Shape.class, "shape") .user("shapes?") .name("Shape") @@ -39,7 +39,7 @@ public String toVariableNameString(Shape shape) { .cloner(Shape::clone) ); - // Style + // Style — use library Style directly (no duplicate enum) Classes.registerClass(new ClassInfo<>(Shape.Style.class, "shapestyle") .user("shape ?styles?") .name("Shape Style") diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/particles/Particle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/particles/Particle.java index dfe6dd3..c460aff 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/particles/Particle.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/particles/Particle.java @@ -1,6 +1,8 @@ package com.sovdee.skriptparticles.particles; -import com.sovdee.skriptparticles.shapes.Shape; +import com.sovdee.shapes.Shape; +import com.sovdee.skriptparticles.shapes.DrawData; +import com.sovdee.skriptparticles.util.VectorConversion; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.entity.Player; @@ -47,17 +49,19 @@ public Particle(org.bukkit.Particle particle, ParticleMotion motion) { } public void spawn(Vector delta) { - if (parent == null || parent.getLastLocation() == null) return; + if (parent == null) return; + DrawData dd = DrawData.of(parent); + if (dd.getLastLocation() == null) return; if (motion != null) { - Vector motionVector = motion.getMotionVector(parent.getRelativeYAxis(true), delta); + Vector yAxis = dd.getLastOrientation().transform(new Vector(0, 1, 0)); + Vector motionVector = motion.getMotionVector(yAxis, delta); this.offset(motionVector.getX(), motionVector.getY(), motionVector.getZ()); this.count(0); } if (gradient != null) { color(gradient.calculateColour(delta)); } - location(parent.getLastLocation().getLocation().add(delta)); - // note that the values we change here do persist, so we may need to reset them after spawning if it causes issues + location(dd.getLastLocation().getLocation().add(delta)); super.spawn(); } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawData.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawData.java new file mode 100644 index 0000000..37cca49 --- /dev/null +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawData.java @@ -0,0 +1,100 @@ +package com.sovdee.skriptparticles.shapes; + +import com.sovdee.shapes.DrawContext; +import com.sovdee.skriptparticles.particles.Particle; +import com.sovdee.skriptparticles.util.DynamicLocation; +import com.sovdee.skriptparticles.util.Quaternion; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * Plugin-side rendering metadata attached to library shapes via {@link DrawContext}. + * Holds particle, location, animation, and debug axis state. + */ +public class DrawData implements DrawContext { + + private Particle particle; + private @Nullable DynamicLocation location; + private @Nullable DynamicLocation lastLocation; + private final Quaternion lastOrientation; + private long animationDuration = 0; + private boolean drawLocalAxes = false; + private boolean drawGlobalAxes = false; + + public DrawData() { + this.particle = (Particle) new Particle(org.bukkit.Particle.FLAME).extra(0); + this.lastOrientation = Quaternion.IDENTITY.clone(); + } + + /** + * Gets the DrawData attached to a shape, creating and attaching one if missing. + */ + public static DrawData of(com.sovdee.shapes.Shape shape) { + DrawContext ctx = shape.getDrawContext(); + if (ctx instanceof DrawData dd) return dd; + DrawData dd = new DrawData(); + shape.setDrawContext(dd); + return dd; + } + + // ---- Particle ---- + + public Particle getParticle() { return particle.clone(); } + + public Particle getParticleRaw() { return particle; } + + public void setParticle(Particle particle) { this.particle = particle; } + + // ---- Location ---- + + @Nullable + public DynamicLocation getLocation() { + if (location == null) return null; + return location.clone(); + } + + public void setLocation(DynamicLocation location) { this.location = location; } + + @Nullable + public DynamicLocation getLastLocation() { return lastLocation; } + + public void setLastLocation(@Nullable DynamicLocation lastLocation) { this.lastLocation = lastLocation; } + + // ---- Orientation ---- + + public Quaternion getLastOrientation() { return lastOrientation; } + + public void setLastOrientation(Quaternion orientation) { this.lastOrientation.set(orientation); } + + // ---- Animation ---- + + public long getAnimationDuration() { return animationDuration; } + + public void setAnimationDuration(long animationDuration) { this.animationDuration = animationDuration; } + + // ---- Axes ---- + + public boolean showLocalAxes() { return drawLocalAxes; } + + public void showLocalAxes(boolean show) { this.drawLocalAxes = show; } + + public boolean showGlobalAxes() { return drawGlobalAxes; } + + public void showGlobalAxes(boolean show) { this.drawGlobalAxes = show; } + + // ---- DrawContext ---- + + @Override + public DrawData copy() { + DrawData copy = new DrawData(); + copy.particle = this.particle.clone(); + if (this.location != null) + copy.location = this.location.clone(); + if (this.lastLocation != null) + copy.lastLocation = this.lastLocation.clone(); + copy.lastOrientation.set(this.lastOrientation); + copy.animationDuration = this.animationDuration; + copy.drawLocalAxes = this.drawLocalAxes; + copy.drawGlobalAxes = this.drawGlobalAxes; + return copy; + } +} diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawManager.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawManager.java new file mode 100644 index 0000000..56e26ef --- /dev/null +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawManager.java @@ -0,0 +1,120 @@ +package com.sovdee.skriptparticles.shapes; + +import ch.njol.skript.Skript; +import com.sovdee.shapes.Shape; +import com.sovdee.skriptparticles.particles.Particle; +import com.sovdee.skriptparticles.particles.ParticleGradient; +import com.sovdee.skriptparticles.util.DynamicLocation; +import com.sovdee.skriptparticles.util.MathUtil; +import com.sovdee.skriptparticles.util.ParticleUtil; +import com.sovdee.skriptparticles.util.Quaternion; +import com.sovdee.skriptparticles.util.VectorConversion; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.util.Vector; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.joml.Quaterniond; +import org.joml.Vector3d; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; + +/** + * Static draw methods for rendering library shapes with plugin DrawData. + */ +public class DrawManager { + + public static void draw(Shape shape, Collection recipients) { + DrawData dd = DrawData.of(shape); + DynamicLocation location = dd.getLocation(); + if (location == null) return; + draw(shape, location, Quaternion.IDENTITY, dd.getParticleRaw(), recipients); + } + + public static void draw(Shape shape, DynamicLocation location, Collection recipients) { + draw(shape, location, Quaternion.IDENTITY, DrawData.of(shape).getParticleRaw(), recipients); + } + + public static void drawWithConsumer(Shape shape, DynamicLocation location, Consumer consumer, Collection recipients) { + consumer.accept(shape); + DrawData dd = DrawData.of(shape); + Quaterniond shapeOrientation = shape.getOrientation(); + Quaternion shapeOrientationQ = new Quaternion((float) shapeOrientation.x, (float) shapeOrientation.y, (float) shapeOrientation.z, (float) shapeOrientation.w); + draw(shape, location, shapeOrientationQ, dd.getParticleRaw(), recipients); + } + + public static void draw(Shape shape, DynamicLocation location, Quaternion baseOrientation, Particle particle, Collection recipients) { + DrawData dd = DrawData.of(shape); + + if (location.isNull()) { + DynamicLocation shapeLocation = dd.getLocation(); + if (shapeLocation == null) return; + location = shapeLocation.clone(); + } + + dd.setLastLocation(location.clone()); + Quaterniond shapeOrientation = shape.getOrientation(); + Quaternion shapeOrientationQ = new Quaternion((float) shapeOrientation.x, (float) shapeOrientation.y, (float) shapeOrientation.z, (float) shapeOrientation.w); + dd.getLastOrientation().set(baseOrientation.clone().mul(shapeOrientationQ)); + + if (!particle.override()) { + dd.getParticleRaw().parent(shape); + particle = dd.getParticleRaw(); + @Nullable ParticleGradient gradient = particle.gradient(); + if (gradient != null && gradient.isLocal()) + gradient.setOrientation(dd.getLastOrientation()); + } + + particle.receivers(recipients); + + // Get points from library shape using the last orientation + Quaterniond lastOrientationD = new Quaterniond(dd.getLastOrientation().x, dd.getLastOrientation().y, dd.getLastOrientation().z, dd.getLastOrientation().w); + Set jomlPoints = shape.getPoints(lastOrientationD); + Collection toDraw = VectorConversion.toBukkit(jomlPoints); + + long animationDuration = dd.getAnimationDuration(); + if (animationDuration > 0) { + int particleCount = toDraw.size(); + double millisecondsPerPoint = animationDuration / (double) particleCount; + Iterator> batchIterator = MathUtil.batch(toDraw, millisecondsPerPoint).iterator(); + Particle finalParticle = particle; + BukkitRunnable runnable = new BukkitRunnable() { + @Override + public void run() { + if (!batchIterator.hasNext()) { + this.cancel(); + return; + } + List batch = batchIterator.next(); + try { + for (Vector point : batch) { + finalParticle.spawn(point); + } + } catch (IllegalArgumentException e) { + Skript.error("Failed to spawn particle! Error: " + e.getMessage()); + } + } + }; + runnable.runTaskTimerAsynchronously(Skript.getInstance(), 0, 1); + } else { + for (Vector point : toDraw) { + try { + particle.spawn(point); + } catch (IllegalArgumentException e) { + Skript.error("Failed to spawn particle! Error: " + e.getMessage()); + return; + } + } + } + + if (dd.showLocalAxes()) { + ParticleUtil.drawAxes(location.getLocation().add(VectorConversion.toBukkit(shape.getOffset())), dd.getLastOrientation(), recipients); + } + if (dd.showGlobalAxes()) { + ParticleUtil.drawAxes(location.getLocation().add(VectorConversion.toBukkit(shape.getOffset())), Quaternion.IDENTITY, recipients); + } + } +} diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawableShape.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawableShape.java deleted file mode 100644 index be90260..0000000 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawableShape.java +++ /dev/null @@ -1,422 +0,0 @@ -package com.sovdee.skriptparticles.shapes; - -import ch.njol.skript.Skript; -import com.sovdee.shapes.AbstractShape; -import com.sovdee.skriptparticles.particles.Particle; -import com.sovdee.skriptparticles.particles.ParticleGradient; -import com.sovdee.skriptparticles.util.DynamicLocation; -import com.sovdee.skriptparticles.util.MathUtil; -import com.sovdee.skriptparticles.util.ParticleUtil; -import com.sovdee.skriptparticles.util.Quaternion; -import com.sovdee.skriptparticles.util.VectorConversion; -import org.bukkit.entity.Player; -import org.bukkit.scheduler.BukkitRunnable; -import org.bukkit.util.Vector; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.jetbrains.annotations.Contract; -import org.joml.Quaterniond; -import org.joml.Quaternionf; -import org.joml.Vector3d; - -import java.util.Collection; -import java.util.Comparator; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; -import java.util.function.Consumer; - -/** - * Wraps a library {@link com.sovdee.shapes.Shape} with rendering metadata (particle, location, animation, etc.) - * This is the type that Skript elements operate on. - */ -public class DrawableShape implements Shape { - - private final com.sovdee.shapes.Shape shape; - - private Particle particle; - private @Nullable DynamicLocation location; - private @Nullable DynamicLocation lastLocation; - private final Quaternion lastOrientation; - private long animationDuration = 0; - private boolean drawLocalAxes = false; - private boolean drawGlobalAxes = false; - - public DrawableShape(com.sovdee.shapes.Shape shape) { - this.shape = shape; - this.particle = (Particle) new Particle(org.bukkit.Particle.FLAME).parent(this).extra(0); - this.lastOrientation = Quaternion.IDENTITY.clone(); - } - - /** - * Gets the underlying library shape. - */ - public com.sovdee.shapes.Shape getShape() { - return shape; - } - - // ---- Delegated geometric methods ---- - - @Override - public Set getPoints() { - return VectorConversion.toBukkit(shape.getPoints()); - } - - @Override - public void setPoints(Set points) { - shape.setPoints(VectorConversion.toJOML(points)); - } - - @Override - public Set getPoints(Quaternion orientation) { - Quaterniond qd = new Quaterniond(orientation.x, orientation.y, orientation.z, orientation.w); - return VectorConversion.toBukkit(shape.getPoints(qd)); - } - - @Override - public void generatePoints(Set points) { - Set jomlPoints = new LinkedHashSet<>(); - shape.generatePoints(jomlPoints); - points.addAll(VectorConversion.toBukkit(jomlPoints)); - } - - @Override - public void generateOutline(Set points) { - Set jomlPoints = new LinkedHashSet<>(); - shape.generateOutline(jomlPoints); - points.addAll(VectorConversion.toBukkit(jomlPoints)); - } - - @Override - public void generateSurface(Set points) { - Set jomlPoints = new LinkedHashSet<>(); - shape.generateSurface(jomlPoints); - points.addAll(VectorConversion.toBukkit(jomlPoints)); - } - - @Override - public void generateFilled(Set points) { - Set jomlPoints = new LinkedHashSet<>(); - shape.generateFilled(jomlPoints); - points.addAll(VectorConversion.toBukkit(jomlPoints)); - } - - // ---- Draw methods ---- - - @Override - public void draw(Collection recipients) { - assert location != null; - draw(location, Quaternion.IDENTITY, particle, recipients); - } - - @Override - public void draw(DynamicLocation location, Collection recipients) { - draw(location, Quaternion.IDENTITY, particle, recipients); - } - - @Override - public void draw(DynamicLocation location, Quaternion baseOrientation, Particle particle, Collection recipients) { - if (location.isNull()) { - if (this.location == null) - return; - location = this.location.clone(); - } - - lastLocation = location.clone(); - Quaterniond shapeOrientation = shape.getOrientation(); - Quaternion shapeOrientationQ = new Quaternion((float) shapeOrientation.x, (float) shapeOrientation.y, (float) shapeOrientation.z, (float) shapeOrientation.w); - lastOrientation.set(baseOrientation.clone().mul(shapeOrientationQ)); - - if (!particle.override()) { - this.particle.parent(this); - particle = this.particle; - @Nullable ParticleGradient gradient = particle.gradient(); - if (gradient != null && gradient.isLocal()) - gradient.setOrientation(lastOrientation); - } - - particle.receivers(recipients); - - // Get points from library using the last orientation - Quaterniond lastOrientationD = new Quaterniond(lastOrientation.x, lastOrientation.y, lastOrientation.z, lastOrientation.w); - Collection toDraw = VectorConversion.toBukkit(shape.getPoints(lastOrientationD)); - - if (animationDuration > 0) { - int particleCount = toDraw.size(); - double millisecondsPerPoint = animationDuration / (double) particleCount; - Iterator> batchIterator = MathUtil.batch(toDraw, millisecondsPerPoint).iterator(); - Particle finalParticle = particle; - BukkitRunnable runnable = new BukkitRunnable() { - @Override - public void run() { - if (!batchIterator.hasNext()) { - this.cancel(); - return; - } - List batch = batchIterator.next(); - try { - for (Vector point : batch) { - finalParticle.spawn(point); - } - } catch (IllegalArgumentException e) { - Skript.error("Failed to spawn particle! Error: " + e.getMessage()); - } - } - }; - runnable.runTaskTimerAsynchronously(Skript.getInstance(), 0, 1); - } else { - for (Vector point : toDraw) { - try { - particle.spawn(point); - } catch (IllegalArgumentException e) { - Skript.error("Failed to spawn particle! Error: " + e.getMessage()); - return; - } - } - } - - if (drawLocalAxes) { - ParticleUtil.drawAxes(location.getLocation().add(VectorConversion.toBukkit(shape.getOffset())), lastOrientation, recipients); - } - if (drawGlobalAxes) { - ParticleUtil.drawAxes(location.getLocation().add(VectorConversion.toBukkit(shape.getOffset())), Quaternion.IDENTITY, recipients); - } - } - - @Override - public void draw(DynamicLocation location, Consumer consumer, Collection recipients) { - consumer.accept(this); - Quaterniond shapeOrientation = shape.getOrientation(); - Quaternion shapeOrientationQ = new Quaternion((float) shapeOrientation.x, (float) shapeOrientation.y, (float) shapeOrientation.z, (float) shapeOrientation.w); - draw(location, shapeOrientationQ, this.particle, recipients); - } - - // ---- Axis methods ---- - - @Override - public Vector getRelativeXAxis(boolean useLastOrientation) { - if (useLastOrientation) { - return lastOrientation.transform(new Vector(1, 0, 0)); - } - return VectorConversion.toBukkit(shape.getRelativeXAxis(false)); - } - - @Override - public Vector getRelativeYAxis(boolean useLastOrientation) { - if (useLastOrientation) { - return lastOrientation.transform(new Vector(0, 1, 0)); - } - return VectorConversion.toBukkit(shape.getRelativeYAxis(false)); - } - - @Override - public Vector getRelativeZAxis(boolean useLastOrientation) { - if (useLastOrientation) { - return lastOrientation.transform(new Vector(0, 0, 1)); - } - return VectorConversion.toBukkit(shape.getRelativeZAxis(false)); - } - - // ---- Debug axes ---- - - @Override - public void showLocalAxes(boolean show) { drawLocalAxes = show; } - - @Override - public boolean showLocalAxes() { return drawLocalAxes; } - - @Override - public void showGlobalAxes(boolean show) { drawGlobalAxes = show; } - - @Override - public boolean showGlobalAxes() { return drawGlobalAxes; } - - // ---- Location ---- - - @Override - @Nullable - public DynamicLocation getLastLocation() { return lastLocation; } - - @Override - @Nullable - public DynamicLocation getLocation() { - if (location == null) return null; - return location.clone(); - } - - @Override - public void setLocation(DynamicLocation location) { this.location = location; } - - // ---- Style ---- - - @Override - public Style getStyle() { - return Style.valueOf(shape.getStyle().name()); - } - - @Override - public void setStyle(Style style) { - shape.setStyle(com.sovdee.shapes.Shape.Style.valueOf(style.name())); - } - - // ---- Orientation ---- - - @Override - public Quaternion getOrientation() { - Quaterniond q = shape.getOrientation(); - return new Quaternion((float) q.x, (float) q.y, (float) q.z, (float) q.w); - } - - @Override - public void setOrientation(Quaternionf orientation) { - shape.setOrientation(new Quaterniond(orientation.x, orientation.y, orientation.z, orientation.w)); - } - - // ---- Scale ---- - - @Override - public double getScale() { return shape.getScale(); } - - @Override - public void setScale(double scale) { shape.setScale(scale); } - - // ---- Offset ---- - - @Override - public Vector getOffset() { - return VectorConversion.toBukkit(shape.getOffset()); - } - - @Override - public void setOffset(Vector offset) { - shape.setOffset(VectorConversion.toJOML(offset)); - } - - // ---- UUID ---- - - @Override - public UUID getUUID() { return shape.getUUID(); } - - // ---- Particle ---- - - @Override - public Particle getParticle() { return particle.clone(); } - - @Override - public void setParticle(Particle particle) { this.particle = particle; } - - // ---- Ordering ---- - - @Override - @Nullable - public Comparator getOrdering() { - // We store ordering on the library shape via a Vector3d comparator. - // This getter returns a Bukkit Vector comparator for Skript compatibility. - Comparator libOrdering = shape.getOrdering(); - if (libOrdering == null) return null; - return (a, b) -> libOrdering.compare(VectorConversion.toJOML(a), VectorConversion.toJOML(b)); - } - - @Override - public void setOrdering(@Nullable Comparator comparator) { - if (comparator == null) { - shape.setOrdering(null); - } else { - shape.setOrdering((a, b) -> comparator.compare(VectorConversion.toBukkit(a), VectorConversion.toBukkit(b))); - } - } - - // ---- Density ---- - - @Override - public double getParticleDensity() { return shape.getParticleDensity(); } - - @Override - public void setParticleDensity(double particleDensity) { shape.setParticleDensity(particleDensity); } - - @Override - public int getParticleCount() { return shape.getParticleCount(); } - - @Override - public void setParticleCount(int particleCount) { shape.setParticleCount(particleCount); } - - // ---- Update state ---- - - @Override - public boolean needsUpdate() { return shape.needsUpdate(); } - - @Override - public void setNeedsUpdate(boolean needsUpdate) { shape.setNeedsUpdate(needsUpdate); } - - // ---- Animation ---- - - @Override - public long getAnimationDuration() { return animationDuration; } - - @Override - public void setAnimationDuration(long animationDuration) { this.animationDuration = animationDuration; } - - // ---- Clone/Copy ---- - - @Override - @Contract("-> new") - public DrawableShape clone() { - DrawableShape copy = new DrawableShape(shape.clone()); - copy.particle = this.particle.clone(); - if (this.location != null) - copy.location = this.location.clone(); - copy.animationDuration = this.animationDuration; - copy.drawLocalAxes = this.drawLocalAxes; - copy.drawGlobalAxes = this.drawGlobalAxes; - copy.lastOrientation.set(this.lastOrientation); - // Preserve ordering via the library shape's copy - return copy; - } - - @Override - @Contract("_ -> param1") - public Shape copyTo(Shape other) { - if (other instanceof DrawableShape ds) { - shape.copyTo(ds.shape); - ds.particle = this.particle.clone(); - if (this.location != null) - ds.location = this.location.clone(); - ds.animationDuration = this.animationDuration; - ds.drawLocalAxes = this.drawLocalAxes; - ds.drawGlobalAxes = this.drawGlobalAxes; - ds.lastOrientation.set(this.lastOrientation); - } - return other; - } - - // ---- State ---- - - @Override - @Contract("-> new") - public State getState() { - com.sovdee.shapes.Shape.State libState = shape.getState(); - Style style = Style.valueOf(libState.style().name()); - return new State(style, libState.orientationHash(), libState.scale(), libState.offsetHash(), libState.particleDensity()); - } - - @Override - @Contract("_ -> new") - public State getState(Quaternion orientation) { - Quaterniond qd = new Quaterniond(orientation.x, orientation.y, orientation.z, orientation.w); - com.sovdee.shapes.Shape.State libState = shape.getState(qd); - Style style = Style.valueOf(libState.style().name()); - return new State(style, libState.orientationHash(), libState.scale(), libState.offsetHash(), libState.particleDensity()); - } - - @Override - public void setLastState(State state) { - com.sovdee.shapes.Shape.Style libStyle = com.sovdee.shapes.Shape.Style.valueOf(state.style().name()); - shape.setLastState(new com.sovdee.shapes.Shape.State(libStyle, state.orientationHash(), state.scale(), state.offsetHash(), state.particleDensity())); - } - - @Override - public String toString() { - return shape.toString(); - } -} diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicBezierCurve.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicBezierCurve.java deleted file mode 100644 index b03109d..0000000 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicBezierCurve.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.sovdee.skriptparticles.shapes; - -import com.sovdee.shapes.BezierCurve; -import com.sovdee.shapes.Shape; -import com.sovdee.skriptparticles.util.Point; -import org.bukkit.Location; -import org.joml.Quaterniond; -import org.joml.Vector3d; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -/** - * A bezier curve that supports dynamic (entity-following) control points via the plugin's Point type. - */ -public class DynamicBezierCurve extends BezierCurve { - - private final Point start; - private final Point end; - private final List> dynamicControlPoints; - - public DynamicBezierCurve(Point start, Point end, List> controlPoints) { - super(evaluatePoints(start, end, controlPoints)); - this.start = start; - this.end = end; - this.dynamicControlPoints = new ArrayList<>(controlPoints); - } - - private static List evaluatePoints(Point start, Point end, List> controlPoints) { - List result = new ArrayList<>(); - Location origin = start.getLocation(); - result.add(toVector3d(start.getVector(origin))); - for (Point cp : controlPoints) - result.add(toVector3d(cp.getVector(origin))); - result.add(toVector3d(end.getVector(origin))); - return result; - } - - private static Vector3d toVector3d(org.bukkit.util.Vector v) { - return new Vector3d(v.getX(), v.getY(), v.getZ()); - } - - @Override - public Set getPoints(Quaterniond orientation) { - Set points = super.getPoints(orientation); - this.setNeedsUpdate(true); - return points; - } - - @Override - public void generateOutline(Set points) { - // Re-evaluate dynamic control points before generating - this.setControlPoints(evaluatePoints(start, end, dynamicControlPoints)); - super.generateOutline(points); - } - - public Point getStart() { return start; } - public Point getEnd() { return end; } - public List> getDynamicControlPoints() { return dynamicControlPoints; } - - @Override - public Shape clone() { - DynamicBezierCurve curve = new DynamicBezierCurve(start, end, dynamicControlPoints); - return this.copyTo(curve); - } -} diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicCuboid.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicCuboid.java deleted file mode 100644 index 6b2cc8a..0000000 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicCuboid.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.sovdee.skriptparticles.shapes; - -import com.sovdee.shapes.Cuboid; -import com.sovdee.shapes.Shape; -import com.sovdee.skriptparticles.util.DynamicLocation; -import org.bukkit.Location; -import org.joml.Quaterniond; -import org.joml.Vector3d; - -import java.util.Set; - -/** - * A cuboid that tracks dynamic locations (entities) for its corners. - */ -public class DynamicCuboid extends Cuboid { - - private final DynamicLocation negativeCorner; - private final DynamicLocation positiveCorner; - - public DynamicCuboid(DynamicLocation cornerA, DynamicLocation cornerB) { - super(computeLength(cornerA, cornerB), computeWidth(cornerA, cornerB), computeHeight(cornerA, cornerB)); - this.negativeCorner = cornerA.clone(); - this.positiveCorner = cornerB.clone(); - } - - private static double computeLength(DynamicLocation a, DynamicLocation b) { - return Math.max(Math.abs(b.getLocation().getX() - a.getLocation().getX()), 0.001); - } - - private static double computeWidth(DynamicLocation a, DynamicLocation b) { - return Math.max(Math.abs(b.getLocation().getZ() - a.getLocation().getZ()), 0.001); - } - - private static double computeHeight(DynamicLocation a, DynamicLocation b) { - return Math.max(Math.abs(b.getLocation().getY() - a.getLocation().getY()), 0.001); - } - - @Override - public Set getPoints(Quaterniond orientation) { - Set points = super.getPoints(orientation); - this.setNeedsUpdate(true); - return points; - } - - @Override - public void generatePoints(Set points) { - Location neg = negativeCorner.getLocation(); - Location pos = positiveCorner.getLocation(); - this.setLength(Math.abs(pos.getX() - neg.getX())); - this.setWidth(Math.abs(pos.getZ() - neg.getZ())); - this.setHeight(Math.abs(pos.getY() - neg.getY())); - super.generatePoints(points); - } - - public DynamicLocation getNegativeCorner() { return negativeCorner; } - public DynamicLocation getPositiveCorner() { return positiveCorner; } - - @Override - public Shape clone() { - DynamicCuboid cuboid = new DynamicCuboid(negativeCorner, positiveCorner); - return this.copyTo(cuboid); - } -} diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicLine.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicLine.java deleted file mode 100644 index 24499ca..0000000 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicLine.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.sovdee.skriptparticles.shapes; - -import com.sovdee.shapes.Line; -import com.sovdee.shapes.Shape; -import com.sovdee.skriptparticles.util.DynamicLocation; -import org.joml.Quaterniond; -import org.joml.Vector3d; - -import java.util.Set; - -/** - * A line that tracks dynamic locations (entities). - * Recalculates start/end vectors from DynamicLocations before each point generation. - */ -public class DynamicLine extends Line { - - private final DynamicLocation startLocation; - private final DynamicLocation endLocation; - - public DynamicLine(DynamicLocation start, DynamicLocation end) { - super(new Vector3d(0, 0, 0), toRelativeVector(start, end)); - this.startLocation = start.clone(); - this.endLocation = end.clone(); - } - - private static Vector3d toRelativeVector(DynamicLocation start, DynamicLocation end) { - org.bukkit.util.Vector startVec = start.getLocation().toVector(); - org.bukkit.util.Vector endVec = end.getLocation().toVector(); - org.bukkit.util.Vector diff = endVec.subtract(startVec); - return new Vector3d(diff.getX(), diff.getY(), diff.getZ()); - } - - @Override - public Set getPoints(Quaterniond orientation) { - Set points = super.getPoints(orientation); - this.setNeedsUpdate(true); - return points; - } - - @Override - public void generatePoints(Set points) { - Vector3d relativeEnd = toRelativeVector(startLocation, endLocation); - this.setStart(new Vector3d(0, 0, 0)); - this.setEnd(relativeEnd); - super.generatePoints(points); - } - - public DynamicLocation getStartLocation() { return startLocation; } - public DynamicLocation getEndLocation() { return endLocation; } - - @Override - public Shape clone() { - DynamicLine line = new DynamicLine(this.startLocation, this.endLocation); - return this.copyTo(line); - } -} diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicRectangle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicRectangle.java deleted file mode 100644 index a85e7a8..0000000 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DynamicRectangle.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.sovdee.skriptparticles.shapes; - -import com.sovdee.shapes.Rectangle; -import com.sovdee.shapes.Shape; -import com.sovdee.skriptparticles.util.DynamicLocation; -import org.bukkit.Location; -import org.joml.Quaterniond; -import org.joml.Vector3d; - -import java.util.Set; - -/** - * A rectangle that tracks dynamic locations (entities) for its corners. - */ -public class DynamicRectangle extends Rectangle { - - private final DynamicLocation negativeCorner; - private final DynamicLocation positiveCorner; - - public DynamicRectangle(DynamicLocation cornerA, DynamicLocation cornerB, Plane plane) { - super(computeLength(cornerA, cornerB, plane), computeWidth(cornerA, cornerB, plane), plane); - this.negativeCorner = cornerA.clone(); - this.positiveCorner = cornerB.clone(); - } - - private static double computeLength(DynamicLocation a, DynamicLocation b, Plane plane) { - Location al = a.getLocation(); - Location bl = b.getLocation(); - return switch (plane) { - case XZ, XY -> Math.max(Math.abs(al.getX() - bl.getX()), 0.001); - case YZ -> Math.max(Math.abs(al.getY() - bl.getY()), 0.001); - }; - } - - private static double computeWidth(DynamicLocation a, DynamicLocation b, Plane plane) { - Location al = a.getLocation(); - Location bl = b.getLocation(); - return switch (plane) { - case XZ, YZ -> Math.max(Math.abs(al.getZ() - bl.getZ()), 0.001); - case XY -> Math.max(Math.abs(al.getY() - bl.getY()), 0.001); - }; - } - - @Override - public Set getPoints(Quaterniond orientation) { - Set points = super.getPoints(orientation); - this.setNeedsUpdate(true); - return points; - } - - @Override - public void generatePoints(Set points) { - Location neg = negativeCorner.getLocation(); - Location pos = positiveCorner.getLocation(); - Plane plane = getPlane(); - double length = switch (plane) { - case XZ, XY -> Math.abs(neg.getX() - pos.getX()); - case YZ -> Math.abs(neg.getY() - pos.getY()); - }; - double width = switch (plane) { - case XZ, YZ -> Math.abs(neg.getZ() - pos.getZ()); - case XY -> Math.abs(neg.getY() - pos.getY()); - }; - this.setLength(length); - this.setWidth(width); - super.generatePoints(points); - } - - public DynamicLocation getNegativeCorner() { return negativeCorner; } - public DynamicLocation getPositiveCorner() { return positiveCorner; } - - @Override - public Shape clone() { - DynamicRectangle rect = new DynamicRectangle(negativeCorner, positiveCorner, getPlane()); - return this.copyTo(rect); - } -} diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/Shape.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/Shape.java deleted file mode 100644 index 68d426b..0000000 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/Shape.java +++ /dev/null @@ -1,402 +0,0 @@ -package com.sovdee.skriptparticles.shapes; - -import com.sovdee.skriptparticles.particles.Particle; -import com.sovdee.skriptparticles.util.DynamicLocation; -import com.sovdee.skriptparticles.util.Quaternion; -import org.bukkit.entity.Player; -import org.bukkit.util.Vector; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.checkerframework.checker.nullness.qual.RequiresNonNull; -import org.jetbrains.annotations.Contract; -import org.joml.Quaternionf; - -import java.util.Collection; -import java.util.Comparator; -import java.util.Set; -import java.util.UUID; -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -/** - * Represents a shape that can be drawn with particles. - * Shapes should have a style, which determines how they are drawn. - * They should also have an orientation, a scale, and an offset which are applied when drawing. - */ -public interface Shape extends Cloneable { - /** - * Gets the points for the shape. - * Attempts to use cached points if possible, by checking if the shape has been modified. Updates the cached points if necessary. - * Uses the shape's orientation to rotate the shape. - * - * @return A set of points that make up the shape. - */ - Set getPoints(); - - /** - * Sets the points for the shape. - * - * @param points The points to use. - */ - void setPoints(Set points); - - /** - * Gets the points for the shape. - * Attempts to use cached points if possible, by checking if the shape has been modified. Updates the cached points if necessary. - * Uses the given orientation to rotate the shape. - * - * @param orientation The orientation of the shape. - * @return A set of points that make up the shape. - */ - Set getPoints(Quaternion orientation); - - /** - * Generates the points for the shape. Depends on the set style. - * No orientation, offset, or scale is applied. - */ - @Contract(pure = true) - default void generatePoints(Set points) { - getStyle().generatePoints(this, points); - } - - /** - * Generates the points for the shape, if the style is set to OUTLINE. - * - * @param points A set that will be filled with the points that make up the shape. - */ - @Contract(pure = true) - void generateOutline(Set points); - - /** - * Generates the points for the shape, if the style is set to SURFACE. - * Default implementation is to return the same as generateOutline(). - * - * @param points A set that will be filled with the points that make up the shape. - */ - @Contract(pure = true) - void generateSurface(Set points); - - /** - * Generates the points for the shape, if the style is set to FILL. - * Default implementation is to return the same as generateSurface(). - * - * @param points A set that will be filled with the points that make up the shape. - */ - @Contract(pure = true) - void generateFilled(Set points); - - - /** - * Draws a shape using the default location, orientation, and particle. - * Caches the last orientation used to draw the shape, so that it can be updated if the orientation changes. - * - * @param recipients The players to draw the shape for. - */ - @RequiresNonNull("location") - void draw(Collection recipients); - - /** - * Draws a shape at a location, using the default orientation and particle. - * Caches the last orientation used to draw the shape, so that it can be updated if the orientation changes. - * - * @param location The location to draw the shape at. - * @param recipients The players to draw the shape for. - */ - void draw(DynamicLocation location, Collection recipients); - - /** - * Draws a shape at a location, given a starting orientation and a particle to use. - * Caches the last orientation used to draw the shape, so that it can be updated if the orientation changes. - * - * @param location The location to draw the shape at. - * @param baseOrientation The base orientation to draw the shape with. This is applied before the shape's own orientation. - * @param particle The particle to draw the shape with. - * @param recipients The players to draw the shape for. - */ - void draw(DynamicLocation location, Quaternion baseOrientation, Particle particle, Collection recipients); - - /** - * Draws a shape at a location, using the default orientation and particle. - * The provided consumer is run before the shape is drawn, allowing for the shape to be modified before drawing. - * This method should not be called by a complex shape, but only by the user via EffSecDrawShape. - * - * @param location The location to draw the shape at. - * @param consumer A consumer that is run before the shape is drawn. - * @param recipients The players to draw the shape for. - */ - void draw(DynamicLocation location, Consumer consumer, Collection recipients); - - /** - * @return the relative X axis of the shape, either using its default orientation or the last orientation used to draw the shape. - */ - Vector getRelativeXAxis(boolean useLastOrientation); - - /** - * @return the relative Y axis of the shape, either using its default orientation or the last orientation used to draw the shape. - */ - Vector getRelativeYAxis(boolean useLastOrientation); - - /** - * @return the relative Z axis of the shape, either using its default orientation or the last orientation used to draw the shape. - */ - Vector getRelativeZAxis(boolean useLastOrientation); - - /** - * Sets whether the shape will draw its local axes. - * - * @param show Whether the shape should draw its local axes. - */ - void showLocalAxes(boolean show); - - /** - * @return whether the shape will draw its local axes. - */ - boolean showLocalAxes(); - - /** - * Sets whether the shape will draw its global axes. - * - * @param show Whether the shape should draw its global axes. - */ - void showGlobalAxes(boolean show); - - /** - * @return whether the shape will draw its global axes. - */ - boolean showGlobalAxes(); - - /** - * @return the last location the shape was drawn at. - */ - @Nullable - DynamicLocation getLastLocation(); - - /** - * @return the style of the shape. - */ - Style getStyle(); - - /** - * Sets the style of the shape. Ensures that the shape will be updated upon the next getPoints() call. - * - * @param style The style to use. - */ - void setStyle(Style style); - - /** - * @return a clone of the orientation of the shape. - */ - Quaternion getOrientation(); - - /** - * Sets the orientation of the shape. Ensures that the shape will be updated upon the next getPoints() call. - * - * @param orientation The orientation to use. - */ - void setOrientation(Quaternionf orientation); - - /** - * @return the scale of the shape. - */ - double getScale(); - - /** - * Sets the scale of the shape. Ensures that the shape will be updated upon the next getPoints() call. - * - * @param scale The scale to use. - */ - void setScale(double scale); - - /** - * @return the offset of the shape. - */ - Vector getOffset(); - - /** - * Sets the offset of the shape. Ensures that the shape will be updated upon the next getPoints() call. - * - * @param offset The offset vector to use. - */ - void setOffset(Vector offset); - - /** - * @return the default location of the shape. - */ - @Nullable - DynamicLocation getLocation(); - - /** - * Sets the default location of the shape. This is used as a fallback if the shape is drawn without a given location. - * - * @param location The location to use. - */ - void setLocation(DynamicLocation location); - - /** - * @return the UUID of the shape. Used for uniqueness during serialization. - */ - UUID getUUID(); - - /** - * @return a clone of the particle of the shape. - */ - Particle getParticle(); - - /** - * Sets the particle of the shape. - * - * @param particle The particle to use. - */ - void setParticle(Particle particle); - - /** - * @return The comparator used to order the particles for drawing - */ - @Nullable Comparator getOrdering(); - - /** - * Sets the comparator to be used to order the particles for drawing. - * Using a null value means the particles will be drawn in the order they are calculated. - * - * @param comparator the Comparator to use. - */ - void setOrdering(@Nullable Comparator comparator); - - - /** - * @return the particle density of the shape. - */ - double getParticleDensity(); - - /** - * Sets the particle density of the shape. Ensures that the shape will be updated upon the next getPoints() call. - * - * @param particleDensity The particle density to use, in meters per particle. Must be greater than 0. - */ - void setParticleDensity(double particleDensity); - - /** - * @return the number of points that the shape has. - */ - int getParticleCount(); - - /** - * Sets the number of points that the shape should have. Will not always be accurate, but should be close. - * Ensures that the shape will be updated upon the next getPoints() call. - * - * @param particleCount The number of points to use, ideally. Must be greater than 0. - */ - void setParticleCount(int particleCount); - - /** - * @return whether the shape needs an update. - */ - boolean needsUpdate(); - - /** - * Marks the shape as needing an update or not. - * A value of true ensures that the shape will be updated upon the next getPoints() call. - * - * @param needsUpdate Whether the shape needs an update. - */ - void setNeedsUpdate(boolean needsUpdate); - - /** - * @return the duration of time it takes to draw the whole shape, in milliseconds. Default is 0. - */ - long getAnimationDuration(); - - /** - * Sets the duration of time it takes to draw the whole shape, in milliseconds. - * A value of 0 causes all points to be drawn at once. - * - * @param animationDuration how long it takes to draw the whole shape, in nanoseconds. Points will be drawn in 10ms intervals, so values below 10ms will be treated as 10ms. - */ - void setAnimationDuration(long animationDuration); - - /** - * @return a deep copy of the shape. - */ - @Contract("-> new") - Shape clone(); - - /** - * Used for deeply copying the shape's properties to a new shape. Intended to be used in the clone() method. - * - * @param shape The shape to copy the properties to. - * @return the updated new shape. - */ - @Contract("_ -> param1") - Shape copyTo(Shape shape); - - /** - * Gets the physical state of a shape, represented by its style, orientation, scale, offset, and particle density. - * Used for checking if a shape has changed since the last time it was drawn. - * @return A state object that represents the shape's physical state. - */ - @Contract("-> new") - State getState(); - - /** - * Gets the physical state of a shape, but with a custom orientation. - * - * @param orientation The orientation to use for the state. - * @return A state object that represents the shape's physical state. - */ - @Contract("_ -> new") - State getState(Quaternion orientation); - - /** - * Sets the last state of the shape. Used for checking if a shape has changed since the last time it was drawn. - * - * @param state The state to set. - */ - void setLastState(State state); - - - /** - * The style of a shape, which determines how it is drawn. - * OUTLINE: Draws the shape as a wireframe. - * SURFACE: Draws the shape as a surface. - * FILL: Draws the shape as a solid. - */ - enum Style { - OUTLINE((shape, points) -> shape.generateOutline(points)), - SURFACE((shape, points) -> shape.generateSurface(points)), - FILL((shape, points) -> shape.generateFilled(points)); - - private final BiConsumer> generatePoints; - - Style(BiConsumer> generatePoints) { - this.generatePoints = generatePoints; - } - - /** - * Puts the points of the shape, generated using the correct style, into the points parameter. - */ - public void generatePoints(Shape shape, Set points) { - generatePoints.accept(shape, points); - } - - public String toString() { - return name().toLowerCase(); - } - } - - /** - * A state object that represents the physical state of a shape. - * Used for checking if a shape has changed since the last time it was drawn. - */ - record State(Style style, int orientationHash, double scale, int offsetHash, double particleDensity) { - - /** - * @return whether the state is equal to another state. - */ - public boolean equals(State state) { - return state.style() == style && - state.orientationHash() == orientationHash && - state.scale() == scale && - state.offsetHash() == offsetHash && - state.particleDensity() == particleDensity; - } - } -} From 9990f311d520ebea8b867032c997e4ce6281580a Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Sat, 7 Feb 2026 19:42:22 -0800 Subject: [PATCH 3/6] update actions --- .github/workflows/gradle.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index ae2ebf3..904712e 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -22,9 +22,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' @@ -34,9 +34,9 @@ jobs: - name: Build with Gradle uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1 with: - arguments: shadowJar + arguments: build - name: Upload Nightly Build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v6 if: success() with: name: skript-particle-nightly From 447e832d2e1ef5755253c4b7318f7b338cdaa491 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Sat, 7 Feb 2026 23:59:21 -0800 Subject: [PATCH 4/6] move around math --- .../main/java/com/sovdee/shapes/Circle.java | 105 -------- .../main/java/com/sovdee/shapes/Ellipse.java | 98 ------- .../main/java/com/sovdee/shapes/Sphere.java | 74 ------ .../shapes/{ => shapes}/AbstractShape.java | 20 +- .../com/sovdee/shapes/{ => shapes}/Arc.java | 9 +- .../shapes/{ => shapes}/BezierCurve.java | 29 +- .../java/com/sovdee/shapes/shapes/Circle.java | 146 +++++++++++ .../sovdee/shapes/{ => shapes}/Cuboid.java | 27 +- .../shapes/{ => shapes}/CutoffShape.java | 2 +- .../com/sovdee/shapes/shapes/Ellipse.java | 160 +++++++++++ .../sovdee/shapes/{ => shapes}/Ellipsoid.java | 33 ++- .../shapes/{ => shapes}/EllipticalArc.java | 7 +- .../com/sovdee/shapes/{ => shapes}/Heart.java | 27 +- .../com/sovdee/shapes/{ => shapes}/Helix.java | 44 +++- .../shapes/{ => shapes}/IrregularPolygon.java | 9 +- .../sovdee/shapes/{ => shapes}/LWHShape.java | 2 +- .../com/sovdee/shapes/{ => shapes}/Line.java | 40 ++- .../sovdee/shapes/{ => shapes}/PolyShape.java | 2 +- .../shapes/{ => shapes}/RadialShape.java | 2 +- .../sovdee/shapes/{ => shapes}/Rectangle.java | 11 +- .../shapes/{ => shapes}/RegularPolygon.java | 74 +++++- .../{ => shapes}/RegularPolyhedron.java | 58 ++-- .../com/sovdee/shapes/{ => shapes}/Shape.java | 5 +- .../java/com/sovdee/shapes/shapes/Sphere.java | 123 +++++++++ .../shapes/{ => shapes}/SphericalCap.java | 8 +- .../com/sovdee/shapes/{ => shapes}/Star.java | 31 ++- .../java/com/sovdee/shapes/util/MathUtil.java | 248 ------------------ .../com/sovdee/shapes/util/VectorUtil.java | 2 +- .../elements/effects/EffRotateShape.java | 2 +- .../elements/effects/EffSetOrdering.java | 2 +- .../elements/effects/EffToggleAxes.java | 2 +- .../elements/expressions/ExprDrawnShapes.java | 2 +- .../elements/expressions/ExprShapeCopy.java | 2 +- .../expressions/constructors/ExprArc.java | 6 +- .../constructors/ExprBezierCurve.java | 4 +- .../expressions/constructors/ExprCircle.java | 6 +- .../expressions/constructors/ExprCuboid.java | 6 +- .../expressions/constructors/ExprEllipse.java | 6 +- .../constructors/ExprEllipsoid.java | 6 +- .../constructors/ExprEllipticalArc.java | 6 +- .../expressions/constructors/ExprHeart.java | 6 +- .../expressions/constructors/ExprHelix.java | 6 +- .../constructors/ExprIrregularPolygon.java | 4 +- .../expressions/constructors/ExprLine.java | 4 +- .../constructors/ExprRectangle.java | 8 +- .../constructors/ExprRegularPolygon.java | 6 +- .../constructors/ExprRegularPolyhedron.java | 6 +- .../expressions/constructors/ExprSphere.java | 6 +- .../constructors/ExprSphericalCap.java | 6 +- .../expressions/constructors/ExprStar.java | 6 +- .../properties/ExprHelixWindingRate.java | 4 +- .../properties/ExprShapeCutoffAngle.java | 4 +- .../expressions/properties/ExprShapeLWH.java | 4 +- .../properties/ExprShapeLocations.java | 2 +- .../properties/ExprShapeNormal.java | 2 +- .../properties/ExprShapeOffset.java | 2 +- .../properties/ExprShapeOrientation.java | 2 +- .../properties/ExprShapeParticle.java | 2 +- .../properties/ExprShapeParticleDensity.java | 2 +- .../properties/ExprShapePoints.java | 2 +- .../properties/ExprShapeRadius.java | 4 +- .../properties/ExprShapeRelativeAxis.java | 2 +- .../properties/ExprShapeScale.java | 2 +- .../properties/ExprShapeSideLength.java | 4 +- .../properties/ExprShapeSides.java | 4 +- .../properties/ExprShapeStyle.java | 2 +- .../properties/ExprStarPoints.java | 4 +- .../expressions/properties/ExprStarRadii.java | 11 +- .../sections/DrawShapeEffectSection.java | 3 +- .../elements/sections/EffSecDrawShape.java | 3 +- .../sections/EffSecDrawShapeAnimation.java | 2 +- .../elements/types/ShapeTypes.java | 2 +- .../skriptparticles/particles/Particle.java | 3 +- .../skriptparticles/shapes/DrawData.java | 3 +- .../skriptparticles/shapes/DrawManager.java | 2 +- .../sovdee/skriptparticles/util/MathUtil.java | 222 +--------------- 76 files changed, 815 insertions(+), 988 deletions(-) delete mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/Circle.java delete mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/Ellipse.java delete mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/Sphere.java rename shapes-lib/src/main/java/com/sovdee/shapes/{ => shapes}/AbstractShape.java (89%) rename shapes-lib/src/main/java/com/sovdee/shapes/{ => shapes}/Arc.java (76%) rename shapes-lib/src/main/java/com/sovdee/shapes/{ => shapes}/BezierCurve.java (81%) create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/shapes/Circle.java rename shapes-lib/src/main/java/com/sovdee/shapes/{ => shapes}/Cuboid.java (88%) rename shapes-lib/src/main/java/com/sovdee/shapes/{ => shapes}/CutoffShape.java (84%) create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/shapes/Ellipse.java rename shapes-lib/src/main/java/com/sovdee/shapes/{ => shapes}/Ellipsoid.java (71%) rename shapes-lib/src/main/java/com/sovdee/shapes/{ => shapes}/EllipticalArc.java (80%) rename shapes-lib/src/main/java/com/sovdee/shapes/{ => shapes}/Heart.java (58%) rename shapes-lib/src/main/java/com/sovdee/shapes/{ => shapes}/Helix.java (62%) rename shapes-lib/src/main/java/com/sovdee/shapes/{ => shapes}/IrregularPolygon.java (88%) rename shapes-lib/src/main/java/com/sovdee/shapes/{ => shapes}/LWHShape.java (89%) rename shapes-lib/src/main/java/com/sovdee/shapes/{ => shapes}/Line.java (70%) rename shapes-lib/src/main/java/com/sovdee/shapes/{ => shapes}/PolyShape.java (87%) rename shapes-lib/src/main/java/com/sovdee/shapes/{ => shapes}/RadialShape.java (85%) rename shapes-lib/src/main/java/com/sovdee/shapes/{ => shapes}/Rectangle.java (94%) rename shapes-lib/src/main/java/com/sovdee/shapes/{ => shapes}/RegularPolygon.java (51%) rename shapes-lib/src/main/java/com/sovdee/shapes/{ => shapes}/RegularPolyhedron.java (81%) rename shapes-lib/src/main/java/com/sovdee/shapes/{ => shapes}/Shape.java (98%) create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/shapes/Sphere.java rename shapes-lib/src/main/java/com/sovdee/shapes/{ => shapes}/SphericalCap.java (74%) rename shapes-lib/src/main/java/com/sovdee/shapes/{ => shapes}/Star.java (54%) delete mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/util/MathUtil.java diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Circle.java b/shapes-lib/src/main/java/com/sovdee/shapes/Circle.java deleted file mode 100644 index 4e8d2bc..0000000 --- a/shapes-lib/src/main/java/com/sovdee/shapes/Circle.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.sovdee.shapes; - -import com.sovdee.shapes.util.MathUtil; -import org.joml.Vector3d; - -import java.util.Set; - -public class Circle extends AbstractShape implements RadialShape, LWHShape { - - private double radius; - protected double cutoffAngle; - private double height; - - public Circle(double radius) { - this(radius, 0); - } - - public Circle(double radius, double height) { - super(); - this.radius = Math.max(radius, MathUtil.EPSILON); - this.height = Math.max(height, 0); - this.cutoffAngle = 2 * Math.PI; - } - - @Override - public void generateOutline(Set points) { - Set circle = MathUtil.calculateCircle(radius, this.getParticleDensity(), cutoffAngle); - if (height != 0) - points.addAll(MathUtil.fillVertically(circle, height, this.getParticleDensity())); - else - points.addAll(circle); - } - - @Override - public void generateSurface(Set points) { - if (height != 0) - points.addAll(MathUtil.calculateCylinder(radius, height, this.getParticleDensity(), cutoffAngle)); - else - points.addAll(MathUtil.calculateDisc(radius, this.getParticleDensity(), cutoffAngle)); - } - - @Override - public void generateFilled(Set points) { - Set disc = MathUtil.calculateDisc(radius, this.getParticleDensity(), cutoffAngle); - if (height != 0) - points.addAll(MathUtil.fillVertically(disc, height, this.getParticleDensity())); - else - points.addAll(disc); - } - - @Override - public void setParticleCount(int particleCount) { - particleCount = Math.max(particleCount, 1); - if (this.getStyle() == Style.OUTLINE && height == 0) { - this.setParticleDensity(cutoffAngle * radius / particleCount); - } else if (this.getStyle() == Style.SURFACE || height == 0) { - double discArea = cutoffAngle * 0.5 * radius * radius; - double wallArea = cutoffAngle * radius * height; - this.setParticleDensity(Math.sqrt((discArea + wallArea) / particleCount)); - } else { - this.setParticleDensity(Math.cbrt(cutoffAngle * 0.5 * radius * radius * height / particleCount)); - } - this.setNeedsUpdate(true); - } - - @Override - public double getRadius() { return radius; } - - @Override - public void setRadius(double radius) { - this.radius = Math.max(radius, MathUtil.EPSILON); - this.setNeedsUpdate(true); - } - - @Override - public double getLength() { return 0; } - - @Override - public void setLength(double length) { } - - @Override - public double getWidth() { return 0; } - - @Override - public void setWidth(double width) { } - - @Override - public double getHeight() { return height; } - - @Override - public void setHeight(double height) { - this.height = Math.max(height, 0); - this.setNeedsUpdate(true); - } - - @Override - public Shape clone() { - return this.copyTo(new Circle(radius, height)); - } - - @Override - public String toString() { - return "Circle{radius=" + radius + ", cutoffAngle=" + cutoffAngle + ", height=" + height + '}'; - } -} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Ellipse.java b/shapes-lib/src/main/java/com/sovdee/shapes/Ellipse.java deleted file mode 100644 index 42f60d8..0000000 --- a/shapes-lib/src/main/java/com/sovdee/shapes/Ellipse.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.sovdee.shapes; - -import com.sovdee.shapes.util.MathUtil; -import org.joml.Vector3d; - -import java.util.LinkedHashSet; -import java.util.Set; - -public class Ellipse extends AbstractShape implements LWHShape { - - private double xRadius; - private double zRadius; - private double height; - protected double cutoffAngle; - - public Ellipse(double xRadius, double zRadius) { - this(xRadius, zRadius, 0); - } - - public Ellipse(double xRadius, double zRadius, double height) { - super(); - this.xRadius = Math.max(xRadius, MathUtil.EPSILON); - this.zRadius = Math.max(zRadius, MathUtil.EPSILON); - this.height = Math.max(height, 0); - this.cutoffAngle = 2 * Math.PI; - } - - @Override - public void generateOutline(Set points) { - Set ellipse = new LinkedHashSet<>(MathUtil.calculateEllipse(xRadius, zRadius, this.getParticleDensity(), cutoffAngle)); - if (height != 0) - points.addAll(MathUtil.fillVertically(ellipse, height, this.getParticleDensity())); - else - points.addAll(ellipse); - } - - @Override - public void generateSurface(Set points) { - if (height != 0) - points.addAll(MathUtil.calculateCylinder(xRadius, zRadius, height, this.getParticleDensity(), cutoffAngle)); - else - points.addAll(MathUtil.calculateEllipticalDisc(xRadius, zRadius, this.getParticleDensity(), cutoffAngle)); - } - - @Override - public void generateFilled(Set points) { - Set disc = MathUtil.calculateEllipticalDisc(xRadius, zRadius, this.getParticleDensity(), cutoffAngle); - if (height != 0) - points.addAll(MathUtil.fillVertically(disc, height, this.getParticleDensity())); - else - points.addAll(disc); - } - - @Override - public void setParticleCount(int particleCount) { - particleCount = Math.max(particleCount, 1); - switch (this.getStyle()) { - case OUTLINE -> { - double h = (xRadius - zRadius) * (xRadius - zRadius) / ((xRadius + zRadius) + (xRadius + zRadius)); - double circumferenceXY = Math.PI * (xRadius + zRadius) * (1 + (3 * h / (10 + Math.sqrt(4 - 3 * h)))); - this.setParticleDensity(circumferenceXY / particleCount); - } - case SURFACE, FILL -> this.setParticleDensity(Math.sqrt((Math.PI * xRadius * zRadius) / particleCount)); - } - } - - @Override - public double getLength() { return xRadius * 2; } - - @Override - public void setLength(double length) { - xRadius = Math.max(length / 2, MathUtil.EPSILON); - this.setNeedsUpdate(true); - } - - @Override - public double getWidth() { return zRadius * 2; } - - @Override - public void setWidth(double width) { - zRadius = Math.max(width / 2, MathUtil.EPSILON); - this.setNeedsUpdate(true); - } - - @Override - public double getHeight() { return height; } - - @Override - public void setHeight(double height) { - this.height = Math.max(height, 0); - this.setNeedsUpdate(true); - } - - @Override - public Shape clone() { - return this.copyTo(new Ellipse(xRadius, zRadius, height)); - } -} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Sphere.java b/shapes-lib/src/main/java/com/sovdee/shapes/Sphere.java deleted file mode 100644 index 4cf6a49..0000000 --- a/shapes-lib/src/main/java/com/sovdee/shapes/Sphere.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.sovdee.shapes; - -import com.sovdee.shapes.util.MathUtil; -import org.joml.Vector3d; - -import java.util.Set; - -public class Sphere extends AbstractShape implements RadialShape { - - private double radius; - protected double cutoffAngle; - protected double cutoffAngleCos; - - public Sphere(double radius) { - super(); - this.radius = Math.max(radius, MathUtil.EPSILON); - this.cutoffAngle = Math.PI; - this.cutoffAngleCos = -1.0; - this.setStyle(Style.SURFACE); - } - - @Override - public void generateOutline(Set points) { - this.generateSurface(points); - } - - @Override - public void generateSurface(Set points) { - double particleDensity = this.getParticleDensity(); - int pointCount = 4 * (int) (Math.PI * radius * radius / (particleDensity * particleDensity)); - points.addAll(MathUtil.calculateFibonacciSphere(pointCount, radius, cutoffAngle)); - } - - @Override - public void generateFilled(Set points) { - double particleDensity = this.getParticleDensity(); - int subSpheres = (int) (radius / particleDensity) - 1; - double radiusStep = radius / subSpheres; - for (int i = 1; i < subSpheres; i++) { - double subRadius = i * radiusStep; - int pointCount = 4 * (int) (Math.PI * subRadius * subRadius / (particleDensity * particleDensity)); - points.addAll(MathUtil.calculateFibonacciSphere(pointCount, subRadius, cutoffAngle)); - } - } - - @Override - public void setParticleCount(int particleCount) { - particleCount = Math.max(particleCount, 1); - this.setParticleDensity(switch (this.getStyle()) { - case OUTLINE, SURFACE -> Math.sqrt(2 * Math.PI * radius * radius * (1 - cutoffAngleCos) / particleCount); - case FILL -> - Math.cbrt(Math.PI / 3 * radius * radius * radius * (2 + cutoffAngleCos) * (1 - cutoffAngleCos) * (1 - cutoffAngleCos) / particleCount); - }); - this.setNeedsUpdate(true); - } - - @Override - public double getRadius() { return radius; } - - @Override - public void setRadius(double radius) { - this.radius = Math.max(radius, MathUtil.EPSILON); - this.setNeedsUpdate(true); - } - - @Override - public Shape clone() { - return this.copyTo(new Sphere(radius)); - } - - public String toString() { - return this.getStyle() + " sphere with radius " + this.radius; - } -} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/AbstractShape.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/AbstractShape.java similarity index 89% rename from shapes-lib/src/main/java/com/sovdee/shapes/AbstractShape.java rename to shapes-lib/src/main/java/com/sovdee/shapes/shapes/AbstractShape.java index 8d4c3d5..2af6071 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/AbstractShape.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/AbstractShape.java @@ -1,6 +1,6 @@ -package com.sovdee.shapes; +package com.sovdee.shapes.shapes; -import com.sovdee.shapes.util.MathUtil; +import com.sovdee.shapes.DrawContext; import org.joml.Quaterniond; import org.joml.Vector3d; @@ -173,7 +173,7 @@ public double getParticleDensity() { @Override public void setParticleDensity(double particleDensity) { - this.particleDensity = Math.max(particleDensity, MathUtil.EPSILON); + this.particleDensity = Math.max(particleDensity, Shape.EPSILON); this.setNeedsUpdate(true); } @@ -212,6 +212,20 @@ public void setDynamic(boolean dynamic) { this.dynamic = dynamic; } + /** + * Adds vertically extruded copies of the given points up to the given height. + * Mutates the set in-place by adding new points at each height step. + */ + protected static void fillVertically(Set points, double height, double particleDensity) { + Set base = new LinkedHashSet<>(points); + double heightStep = height / Math.round(height / particleDensity); + for (double y = 0; y < height; y += heightStep) { + for (Vector3d v : base) { + points.add(new Vector3d(v.x, y, v.z)); + } + } + } + public abstract Shape clone(); @Override diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Arc.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Arc.java similarity index 76% rename from shapes-lib/src/main/java/com/sovdee/shapes/Arc.java rename to shapes-lib/src/main/java/com/sovdee/shapes/shapes/Arc.java index f971ba6..f04a775 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/Arc.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Arc.java @@ -1,6 +1,5 @@ -package com.sovdee.shapes; +package com.sovdee.shapes.shapes; -import com.sovdee.shapes.util.MathUtil; import org.joml.Vector3d; import java.util.Set; @@ -9,12 +8,12 @@ public class Arc extends Circle implements CutoffShape { public Arc(double radius, double cutoffAngle) { super(radius); - this.cutoffAngle = MathUtil.clamp(cutoffAngle, 0, Math.PI * 2); + this.cutoffAngle = Math.clamp(cutoffAngle, 0, Math.PI * 2); } public Arc(double radius, double height, double cutoffAngle) { super(radius, height); - this.cutoffAngle = MathUtil.clamp(cutoffAngle, 0, Math.PI * 2); + this.cutoffAngle = Math.clamp(cutoffAngle, 0, Math.PI * 2); } @Override @@ -29,7 +28,7 @@ public double getCutoffAngle() { @Override public void setCutoffAngle(double cutoffAngle) { - this.cutoffAngle = MathUtil.clamp(cutoffAngle, 0, Math.PI * 2); + this.cutoffAngle = Math.clamp(cutoffAngle, 0, Math.PI * 2); this.setNeedsUpdate(true); } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/BezierCurve.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/BezierCurve.java similarity index 81% rename from shapes-lib/src/main/java/com/sovdee/shapes/BezierCurve.java rename to shapes-lib/src/main/java/com/sovdee/shapes/shapes/BezierCurve.java index a712ccd..8f7e676 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/BezierCurve.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/BezierCurve.java @@ -1,4 +1,4 @@ -package com.sovdee.shapes; +package com.sovdee.shapes.shapes; import org.joml.Vector3d; @@ -59,19 +59,26 @@ public void generateOutline(Set points) { this.controlPoints.add(new Vector3d(cp)); } int steps = (int) (estimateLength() / getParticleDensity()); + int n = controlPoints.size(); - for (double step = 0; step < steps; step++) { - double t = step / steps; + // Pre-allocate temp array for de Casteljau — reused each step + Vector3d[] temp = new Vector3d[n]; + for (int i = 0; i < n; i++) + temp[i] = new Vector3d(); + + for (int step = 0; step < steps; step++) { + double t = (double) step / steps; double nt = 1 - t; - List tempCP = new ArrayList<>(); - for (Vector3d cp : controlPoints) - tempCP.add(new Vector3d(cp)); - while (tempCP.size() > 1) { - for (int i = 0; i < tempCP.size() - 1; i++) - tempCP.set(i, new Vector3d(tempCP.get(i)).mul(nt).add(new Vector3d(tempCP.get(i + 1)).mul(t))); - tempCP.remove(tempCP.size() - 1); + // Copy control points into temp + for (int i = 0; i < n; i++) + temp[i].set(controlPoints.get(i)); + // Reduce in-place + for (int level = n - 1; level > 0; level--) { + for (int i = 0; i < level; i++) { + temp[i].mul(nt).add(new Vector3d(temp[i + 1]).mul(t)); + } } - points.add(tempCP.get(0)); + points.add(new Vector3d(temp[0])); } } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Circle.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Circle.java new file mode 100644 index 0000000..2cf94fd --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Circle.java @@ -0,0 +1,146 @@ +package com.sovdee.shapes.shapes; + +import org.joml.Vector3d; + +import java.util.LinkedHashSet; +import java.util.Set; + +public class Circle extends AbstractShape implements RadialShape, LWHShape { + + private double radius; + protected double cutoffAngle; + private double height; + + public Circle(double radius) { + this(radius, 0); + } + + public Circle(double radius, double height) { + super(); + this.radius = Math.max(radius, Shape.EPSILON); + this.height = Math.max(height, 0); + this.cutoffAngle = 2 * Math.PI; + } + + // --- Static calculation methods --- + + public static Set calculateCircle(double radius, double particleDensity, double cutoffAngle) { + Set points = new LinkedHashSet<>(); + double stepSize = particleDensity / radius; + for (double theta = 0; theta < cutoffAngle; theta += stepSize) { + points.add(new Vector3d(Math.cos(theta) * radius, 0, Math.sin(theta) * radius)); + } + return points; + } + + public static Set calculateDisc(double radius, double particleDensity, double cutoffAngle) { + Set points = new LinkedHashSet<>(); + for (double subRadius = particleDensity; subRadius < radius; subRadius += particleDensity) { + points.addAll(calculateCircle(subRadius, particleDensity, cutoffAngle)); + } + points.addAll(calculateCircle(radius, particleDensity, cutoffAngle)); + return points; + } + + public static Set calculateCylinder(double radius, double height, double particleDensity, double cutoffAngle) { + Set points = calculateDisc(radius, particleDensity, cutoffAngle); + // Top disc via direct loop + Set top = new LinkedHashSet<>(); + for (Vector3d v : points) { + top.add(new Vector3d(v.x, height, v.z)); + } + points.addAll(top); + // Wall + Set wall = calculateCircle(radius, particleDensity, cutoffAngle); + fillVertically(wall, height, particleDensity); + points.addAll(wall); + return points; + } + + // --- Generation methods --- + + @Override + public void generateOutline(Set points) { + Set circle = calculateCircle(radius, this.getParticleDensity(), cutoffAngle); + if (height != 0) { + fillVertically(circle, height, this.getParticleDensity()); + points.addAll(circle); + } else { + points.addAll(circle); + } + } + + @Override + public void generateSurface(Set points) { + if (height != 0) + points.addAll(calculateCylinder(radius, height, this.getParticleDensity(), cutoffAngle)); + else + points.addAll(calculateDisc(radius, this.getParticleDensity(), cutoffAngle)); + } + + @Override + public void generateFilled(Set points) { + Set disc = calculateDisc(radius, this.getParticleDensity(), cutoffAngle); + if (height != 0) { + fillVertically(disc, height, this.getParticleDensity()); + points.addAll(disc); + } else { + points.addAll(disc); + } + } + + @Override + public void setParticleCount(int particleCount) { + particleCount = Math.max(particleCount, 1); + if (this.getStyle() == Style.OUTLINE && height == 0) { + this.setParticleDensity(cutoffAngle * radius / particleCount); + } else if (this.getStyle() == Style.SURFACE || height == 0) { + double discArea = cutoffAngle * 0.5 * radius * radius; + double wallArea = cutoffAngle * radius * height; + this.setParticleDensity(Math.sqrt((discArea + wallArea) / particleCount)); + } else { + this.setParticleDensity(Math.cbrt(cutoffAngle * 0.5 * radius * radius * height / particleCount)); + } + this.setNeedsUpdate(true); + } + + @Override + public double getRadius() { return radius; } + + @Override + public void setRadius(double radius) { + this.radius = Math.max(radius, Shape.EPSILON); + this.setNeedsUpdate(true); + } + + @Override + public double getLength() { return 0; } + + @Override + public void setLength(double length) { } + + @Override + public double getWidth() { return 0; } + + @Override + public void setWidth(double width) { } + + @Override + public double getHeight() { return height; } + + @Override + public void setHeight(double height) { + this.height = Math.max(height, 0); + this.setNeedsUpdate(true); + } + + @Override + public Shape clone() { + return this.copyTo(new Circle(radius, height)); + } + + @Override + public String toString() { + return "Circle{radius=" + radius + ", cutoffAngle=" + cutoffAngle + ", height=" + height + '}'; + } +} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Cuboid.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Cuboid.java similarity index 88% rename from shapes-lib/src/main/java/com/sovdee/shapes/Cuboid.java rename to shapes-lib/src/main/java/com/sovdee/shapes/shapes/Cuboid.java index b88d4ed..089ec9e 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/Cuboid.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Cuboid.java @@ -1,6 +1,5 @@ -package com.sovdee.shapes; +package com.sovdee.shapes.shapes; -import com.sovdee.shapes.util.MathUtil; import org.joml.Vector3d; import java.util.Set; @@ -20,9 +19,9 @@ public class Cuboid extends AbstractShape implements LWHShape { public Cuboid(double length, double width, double height) { super(); - this.halfWidth = Math.max(width / 2, MathUtil.EPSILON); - this.halfLength = Math.max(length / 2, MathUtil.EPSILON); - this.halfHeight = Math.max(height / 2, MathUtil.EPSILON); + this.halfWidth = Math.max(width / 2, Shape.EPSILON); + this.halfLength = Math.max(length / 2, Shape.EPSILON); + this.halfHeight = Math.max(height / 2, Shape.EPSILON); calculateSteps(); } @@ -43,9 +42,9 @@ public Cuboid(Supplier cornerA, Supplier cornerB) { this.cornerBSupplier = cornerB; Vector3d a = cornerA.get(); Vector3d b = cornerB.get(); - this.halfLength = Math.max(Math.abs(b.x - a.x) / 2, MathUtil.EPSILON); - this.halfWidth = Math.max(Math.abs(b.z - a.z) / 2, MathUtil.EPSILON); - this.halfHeight = Math.max(Math.abs(b.y - a.y) / 2, MathUtil.EPSILON); + this.halfLength = Math.max(Math.abs(b.x - a.x) / 2, Shape.EPSILON); + this.halfWidth = Math.max(Math.abs(b.z - a.z) / 2, Shape.EPSILON); + this.halfHeight = Math.max(Math.abs(b.y - a.y) / 2, Shape.EPSILON); calculateSteps(); setDynamic(true); } @@ -116,9 +115,9 @@ public void generatePoints(Set points) { if (cornerASupplier != null && cornerBSupplier != null) { Vector3d a = cornerASupplier.get(); Vector3d b = cornerBSupplier.get(); - this.halfLength = Math.max(Math.abs(b.x - a.x) / 2, MathUtil.EPSILON); - this.halfWidth = Math.max(Math.abs(b.z - a.z) / 2, MathUtil.EPSILON); - this.halfHeight = Math.max(Math.abs(b.y - a.y) / 2, MathUtil.EPSILON); + this.halfLength = Math.max(Math.abs(b.x - a.x) / 2, Shape.EPSILON); + this.halfWidth = Math.max(Math.abs(b.z - a.z) / 2, Shape.EPSILON); + this.halfHeight = Math.max(Math.abs(b.y - a.y) / 2, Shape.EPSILON); } calculateSteps(); super.generatePoints(points); @@ -143,7 +142,7 @@ public void setParticleCount(int particleCount) { @Override public void setLength(double length) { - this.halfLength = Math.max(length / 2, MathUtil.EPSILON); + this.halfLength = Math.max(length / 2, Shape.EPSILON); this.setNeedsUpdate(true); } @@ -152,7 +151,7 @@ public void setLength(double length) { @Override public void setWidth(double width) { - this.halfWidth = Math.max(width / 2, MathUtil.EPSILON); + this.halfWidth = Math.max(width / 2, Shape.EPSILON); this.setNeedsUpdate(true); } @@ -161,7 +160,7 @@ public void setWidth(double width) { @Override public void setHeight(double height) { - this.halfHeight = Math.max(height / 2, MathUtil.EPSILON); + this.halfHeight = Math.max(height / 2, Shape.EPSILON); this.setNeedsUpdate(true); } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/CutoffShape.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/CutoffShape.java similarity index 84% rename from shapes-lib/src/main/java/com/sovdee/shapes/CutoffShape.java rename to shapes-lib/src/main/java/com/sovdee/shapes/shapes/CutoffShape.java index 1da69c2..19d3760 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/CutoffShape.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/CutoffShape.java @@ -1,4 +1,4 @@ -package com.sovdee.shapes; +package com.sovdee.shapes.shapes; /** * Represents a shape that has a cutoff angle, like an arc. diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Ellipse.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Ellipse.java new file mode 100644 index 0000000..97943ef --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Ellipse.java @@ -0,0 +1,160 @@ +package com.sovdee.shapes.shapes; + +import org.joml.Vector3d; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +public class Ellipse extends AbstractShape implements LWHShape { + + private double xRadius; + private double zRadius; + private double height; + protected double cutoffAngle; + + public Ellipse(double xRadius, double zRadius) { + this(xRadius, zRadius, 0); + } + + public Ellipse(double xRadius, double zRadius, double height) { + super(); + this.xRadius = Math.max(xRadius, Shape.EPSILON); + this.zRadius = Math.max(zRadius, Shape.EPSILON); + this.height = Math.max(height, 0); + this.cutoffAngle = 2 * Math.PI; + } + + // --- Static calculation methods --- + + private static double ellipseCircumference(double r1, double r2) { + double a = Math.max(r1, r2); + double b = Math.min(r1, r2); + double h = Math.pow(a - b, 2) / Math.pow(a + b, 2); + return Math.PI * (a + b) * (1 + 3 * h / (10 + Math.sqrt(4 - 3 * h))); + } + + public static List calculateEllipse(double r1, double r2, double particleDensity, double cutoffAngle) { + List points = new ArrayList<>(); + double circumference = ellipseCircumference(r1, r2); + + int steps = (int) Math.round(circumference / particleDensity); + double theta = 0; + double angleStep = 0; + for (int i = 0; i < steps; i++) { + if (theta > cutoffAngle) { + break; + } + points.add(new Vector3d(r1 * Math.cos(theta), 0, r2 * Math.sin(theta))); + double dx = r1 * Math.sin(theta + 0.5 * angleStep); + double dy = r2 * Math.cos(theta + 0.5 * angleStep); + angleStep = particleDensity / Math.sqrt(dx * dx + dy * dy); + theta += angleStep; + } + return points; + } + + public static Set calculateEllipticalDisc(double r1, double r2, double particleDensity, double cutoffAngle) { + Set points = new LinkedHashSet<>(); + int steps = (int) Math.round(Math.max(r1, r2) / particleDensity); + double r; + for (double i = 1; i <= steps; i += 1) { + r = i / steps; + points.addAll(calculateEllipse(r1 * r, r2 * r, particleDensity, cutoffAngle)); + } + return points; + } + + public static Set calculateCylinder(double r1, double r2, double height, double particleDensity, double cutoffAngle) { + Set points = calculateEllipticalDisc(r1, r2, particleDensity, cutoffAngle); + // Top disc via direct loop + Set top = new LinkedHashSet<>(); + for (Vector3d v : points) { + top.add(new Vector3d(v.x, height, v.z)); + } + points.addAll(top); + // Wall + Set wall = new LinkedHashSet<>(calculateEllipse(r1, r2, particleDensity, cutoffAngle)); + fillVertically(wall, height, particleDensity); + points.addAll(wall); + return points; + } + + // --- Generation methods --- + + @Override + public void generateOutline(Set points) { + Set ellipse = new LinkedHashSet<>(calculateEllipse(xRadius, zRadius, this.getParticleDensity(), cutoffAngle)); + if (height != 0) { + fillVertically(ellipse, height, this.getParticleDensity()); + points.addAll(ellipse); + } else { + points.addAll(ellipse); + } + } + + @Override + public void generateSurface(Set points) { + if (height != 0) + points.addAll(calculateCylinder(xRadius, zRadius, height, this.getParticleDensity(), cutoffAngle)); + else + points.addAll(calculateEllipticalDisc(xRadius, zRadius, this.getParticleDensity(), cutoffAngle)); + } + + @Override + public void generateFilled(Set points) { + Set disc = calculateEllipticalDisc(xRadius, zRadius, this.getParticleDensity(), cutoffAngle); + if (height != 0) { + fillVertically(disc, height, this.getParticleDensity()); + points.addAll(disc); + } else { + points.addAll(disc); + } + } + + @Override + public void setParticleCount(int particleCount) { + particleCount = Math.max(particleCount, 1); + switch (this.getStyle()) { + case OUTLINE -> { + double h = (xRadius - zRadius) * (xRadius - zRadius) / ((xRadius + zRadius) + (xRadius + zRadius)); + double circumferenceXY = Math.PI * (xRadius + zRadius) * (1 + (3 * h / (10 + Math.sqrt(4 - 3 * h)))); + this.setParticleDensity(circumferenceXY / particleCount); + } + case SURFACE, FILL -> this.setParticleDensity(Math.sqrt((Math.PI * xRadius * zRadius) / particleCount)); + } + } + + @Override + public double getLength() { return xRadius * 2; } + + @Override + public void setLength(double length) { + xRadius = Math.max(length / 2, Shape.EPSILON); + this.setNeedsUpdate(true); + } + + @Override + public double getWidth() { return zRadius * 2; } + + @Override + public void setWidth(double width) { + zRadius = Math.max(width / 2, Shape.EPSILON); + this.setNeedsUpdate(true); + } + + @Override + public double getHeight() { return height; } + + @Override + public void setHeight(double height) { + this.height = Math.max(height, 0); + this.setNeedsUpdate(true); + } + + @Override + public Shape clone() { + return this.copyTo(new Ellipse(xRadius, zRadius, height)); + } +} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Ellipsoid.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Ellipsoid.java similarity index 71% rename from shapes-lib/src/main/java/com/sovdee/shapes/Ellipsoid.java rename to shapes-lib/src/main/java/com/sovdee/shapes/shapes/Ellipsoid.java index 5eb7ad6..d29183d 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/Ellipsoid.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Ellipsoid.java @@ -1,6 +1,5 @@ -package com.sovdee.shapes; +package com.sovdee.shapes.shapes; -import com.sovdee.shapes.util.MathUtil; import com.sovdee.shapes.util.VectorUtil; import org.joml.Quaterniond; import org.joml.Vector3d; @@ -19,17 +18,17 @@ public class Ellipsoid extends AbstractShape implements LWHShape { public Ellipsoid(double xRadius, double yRadius, double zRadius) { super(); - this.xRadius = Math.max(xRadius, MathUtil.EPSILON); - this.yRadius = Math.max(yRadius, MathUtil.EPSILON); - this.zRadius = Math.max(zRadius, MathUtil.EPSILON); + this.xRadius = Math.max(xRadius, Shape.EPSILON); + this.yRadius = Math.max(yRadius, Shape.EPSILON); + this.zRadius = Math.max(zRadius, Shape.EPSILON); } @Override public void generateOutline(Set points) { double particleDensity = this.getParticleDensity(); - points.addAll(MathUtil.calculateEllipse(xRadius, zRadius, particleDensity, 2 * Math.PI)); - points.addAll(VectorUtil.transform(XY_ROTATION, MathUtil.calculateEllipse(xRadius, yRadius, particleDensity, 2 * Math.PI))); - points.addAll(VectorUtil.transform(ZY_ROTATION, MathUtil.calculateEllipse(yRadius, zRadius, particleDensity, 2 * Math.PI))); + points.addAll(Ellipse.calculateEllipse(xRadius, zRadius, particleDensity, 2 * Math.PI)); + points.addAll(VectorUtil.transform(XY_ROTATION, Ellipse.calculateEllipse(xRadius, yRadius, particleDensity, 2 * Math.PI))); + points.addAll(VectorUtil.transform(ZY_ROTATION, Ellipse.calculateEllipse(yRadius, zRadius, particleDensity, 2 * Math.PI))); } @Override @@ -37,9 +36,9 @@ public void generateSurface(Set points) { List ellipse; double particleDensity = this.getParticleDensity(); if (xRadius > zRadius) { - ellipse = VectorUtil.transform(XY_ROTATION, MathUtil.calculateEllipse(xRadius, yRadius, particleDensity, 2 * Math.PI)); + ellipse = VectorUtil.transform(XY_ROTATION, Ellipse.calculateEllipse(xRadius, yRadius, particleDensity, 2 * Math.PI)); } else { - ellipse = VectorUtil.transform(ZY_ROTATION, MathUtil.calculateEllipse(yRadius, zRadius, particleDensity, 2 * Math.PI)); + ellipse = VectorUtil.transform(ZY_ROTATION, Ellipse.calculateEllipse(yRadius, zRadius, particleDensity, 2 * Math.PI)); } points.addAll(generateEllipsoid(ellipse, 1)); } @@ -53,9 +52,9 @@ public void generateFilled(Set points) { for (int i = steps; i > 0; i--) { double r = (i / (double) steps); if (xRadius > zRadius) { - ellipse = VectorUtil.transform(XY_ROTATION, MathUtil.calculateEllipse(xRadius * r, yRadius * r, particleDensity, 2 * Math.PI)); + ellipse = VectorUtil.transform(XY_ROTATION, Ellipse.calculateEllipse(xRadius * r, yRadius * r, particleDensity, 2 * Math.PI)); } else { - ellipse = VectorUtil.transform(ZY_ROTATION, MathUtil.calculateEllipse(yRadius * r, zRadius * r, particleDensity, 2 * Math.PI)); + ellipse = VectorUtil.transform(ZY_ROTATION, Ellipse.calculateEllipse(yRadius * r, zRadius * r, particleDensity, 2 * Math.PI)); } points.addAll(generateEllipsoid(ellipse, r)); } @@ -66,12 +65,12 @@ private Set generateEllipsoid(List ellipse, double radius) { for (int i = 0; i < Math.ceil(ellipse.size() / 4.0); i++) { double y = ellipse.get(i).y; double theta = Math.asin(y / (yRadius * radius)); - for (Vector3d v2 : MathUtil.calculateEllipse(radius * xRadius * Math.cos(theta), radius * zRadius * Math.cos(theta), this.getParticleDensity(), 2 * Math.PI)) { + for (Vector3d v2 : Ellipse.calculateEllipse(radius * xRadius * Math.cos(theta), radius * zRadius * Math.cos(theta), this.getParticleDensity(), 2 * Math.PI)) { points.add(new Vector3d(v2.x, y, v2.z)); points.add(new Vector3d(v2.x, -y, v2.z)); } } - points.addAll(MathUtil.calculateEllipse(radius * xRadius, radius * zRadius, this.getParticleDensity(), 2 * Math.PI)); + points.addAll(Ellipse.calculateEllipse(radius * xRadius, radius * zRadius, this.getParticleDensity(), 2 * Math.PI)); return points; } @@ -104,7 +103,7 @@ public void setParticleCount(int particleCount) { @Override public void setLength(double length) { - xRadius = Math.max(length / 2, MathUtil.EPSILON); + xRadius = Math.max(length / 2, Shape.EPSILON); this.setNeedsUpdate(true); } @@ -113,7 +112,7 @@ public void setLength(double length) { @Override public void setWidth(double width) { - zRadius = Math.max(width / 2, MathUtil.EPSILON); + zRadius = Math.max(width / 2, Shape.EPSILON); this.setNeedsUpdate(true); } @@ -122,7 +121,7 @@ public void setWidth(double width) { @Override public void setHeight(double height) { - yRadius = Math.max(height / 2, MathUtil.EPSILON); + yRadius = Math.max(height / 2, Shape.EPSILON); this.setNeedsUpdate(true); } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/EllipticalArc.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/EllipticalArc.java similarity index 80% rename from shapes-lib/src/main/java/com/sovdee/shapes/EllipticalArc.java rename to shapes-lib/src/main/java/com/sovdee/shapes/shapes/EllipticalArc.java index 2654203..abc9514 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/EllipticalArc.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/EllipticalArc.java @@ -1,6 +1,5 @@ -package com.sovdee.shapes; +package com.sovdee.shapes.shapes; -import com.sovdee.shapes.util.MathUtil; import org.joml.Vector3d; import java.util.Set; @@ -13,7 +12,7 @@ public EllipticalArc(double xRadius, double zRadius, double cutoffAngle) { public EllipticalArc(double xRadius, double zRadius, double height, double cutoffAngle) { super(xRadius, zRadius, height); - this.cutoffAngle = MathUtil.clamp(cutoffAngle, 0, Math.PI * 2); + this.cutoffAngle = Math.clamp(cutoffAngle, 0, Math.PI * 2); } @Override @@ -28,7 +27,7 @@ public double getCutoffAngle() { @Override public void setCutoffAngle(double cutoffAngle) { - this.cutoffAngle = MathUtil.clamp(cutoffAngle, 0, Math.PI * 2); + this.cutoffAngle = Math.clamp(cutoffAngle, 0, Math.PI * 2); this.setNeedsUpdate(true); } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Heart.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Heart.java similarity index 58% rename from shapes-lib/src/main/java/com/sovdee/shapes/Heart.java rename to shapes-lib/src/main/java/com/sovdee/shapes/shapes/Heart.java index 87ed198..a57b650 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/Heart.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Heart.java @@ -1,8 +1,8 @@ -package com.sovdee.shapes; +package com.sovdee.shapes.shapes; -import com.sovdee.shapes.util.MathUtil; import org.joml.Vector3d; +import java.util.LinkedHashSet; import java.util.Set; public class Heart extends AbstractShape implements LWHShape { @@ -13,21 +13,32 @@ public class Heart extends AbstractShape implements LWHShape { public Heart(double length, double width, double eccentricity) { super(); - this.length = Math.max(length, MathUtil.EPSILON); - this.width = Math.max(width, MathUtil.EPSILON); + this.length = Math.max(length, Shape.EPSILON); + this.width = Math.max(width, Shape.EPSILON); this.eccentricity = Math.max(eccentricity, 1); } + private static Set calculateHeart(double length, double width, double eccentricity, double particleDensity) { + Set points = new LinkedHashSet<>(); + double angleStep = 4 / 3.0 * particleDensity / (width + length); + for (double theta = 0; theta < Math.PI * 2; theta += angleStep) { + double x = width * Math.pow(Math.sin(theta), 3); + double y = length * (Math.cos(theta) - 1 / eccentricity * Math.cos(2 * theta) - 1.0 / 6 * Math.cos(3 * theta) - 1.0 / 16 * Math.cos(4 * theta)); + points.add(new Vector3d(x, 0, y)); + } + return points; + } + @Override public void generateOutline(Set points) { - points.addAll(MathUtil.calculateHeart(length / 2, width / 2, eccentricity, this.getParticleDensity())); + points.addAll(calculateHeart(length / 2, width / 2, eccentricity, this.getParticleDensity())); } @Override public void generateSurface(Set points) { double particleDensity = this.getParticleDensity(); for (double w = width, l = length; w > 0 && l > 0; w -= particleDensity * 1.5, l -= particleDensity * 1.5) { - points.addAll(MathUtil.calculateHeart(l / 2, w / 2, eccentricity, particleDensity)); + points.addAll(calculateHeart(l / 2, w / 2, eccentricity, particleDensity)); } } @@ -45,7 +56,7 @@ public void setHeight(double height) { } @Override public void setWidth(double width) { - this.width = Math.max(width, MathUtil.EPSILON); + this.width = Math.max(width, Shape.EPSILON); this.setNeedsUpdate(true); } @@ -54,7 +65,7 @@ public void setWidth(double width) { @Override public void setLength(double length) { - this.length = Math.max(length, MathUtil.EPSILON); + this.length = Math.max(length, Shape.EPSILON); this.setNeedsUpdate(true); } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Helix.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Helix.java similarity index 62% rename from shapes-lib/src/main/java/com/sovdee/shapes/Helix.java rename to shapes-lib/src/main/java/com/sovdee/shapes/shapes/Helix.java index 9d0d966..3eaf706 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/Helix.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Helix.java @@ -1,8 +1,8 @@ -package com.sovdee.shapes; +package com.sovdee.shapes.shapes; -import com.sovdee.shapes.util.MathUtil; import org.joml.Vector3d; +import java.util.LinkedHashSet; import java.util.Set; public class Helix extends AbstractShape implements RadialShape, LWHShape { @@ -14,31 +14,47 @@ public class Helix extends AbstractShape implements RadialShape, LWHShape { public Helix(double radius, double height, double slope) { super(); - this.radius = Math.max(radius, MathUtil.EPSILON); - this.height = Math.max(height, MathUtil.EPSILON); - this.slope = Math.max(slope, MathUtil.EPSILON); + this.radius = Math.max(radius, Shape.EPSILON); + this.height = Math.max(height, Shape.EPSILON); + this.slope = Math.max(slope, Shape.EPSILON); } public Helix(double radius, double height, double slope, int direction) { super(); - this.radius = Math.max(radius, MathUtil.EPSILON); - this.height = Math.max(height, MathUtil.EPSILON); - this.slope = Math.max(slope, MathUtil.EPSILON); + this.radius = Math.max(radius, Shape.EPSILON); + this.height = Math.max(height, Shape.EPSILON); + this.slope = Math.max(slope, Shape.EPSILON); if (direction != 1 && direction != -1) throw new IllegalArgumentException("Direction must be 1 or -1"); this.direction = direction; } + private static Set calculateHelix(double radius, double height, double slope, int direction, double particleDensity) { + Set points = new LinkedHashSet<>(); + if (radius <= 0 || height <= 0) { + return points; + } + double loops = Math.abs(height / slope); + double length = slope * slope + radius * radius; + double stepSize = particleDensity / length; + for (double t = 0; t < loops; t += stepSize) { + double x = radius * Math.cos(direction * t); + double z = radius * Math.sin(direction * t); + points.add(new Vector3d(x, t * slope, z)); + } + return points; + } + @Override public void generateOutline(Set points) { - points.addAll(MathUtil.calculateHelix(radius, height, slope, direction, this.getParticleDensity())); + points.addAll(calculateHelix(radius, height, slope, direction, this.getParticleDensity())); } @Override public void generateSurface(Set points) { double particleDensity = this.getParticleDensity(); for (double r = radius; r > 0; r -= particleDensity) { - points.addAll(MathUtil.calculateHelix(r, height, slope, direction, particleDensity)); + points.addAll(calculateHelix(r, height, slope, direction, particleDensity)); } } @@ -54,7 +70,7 @@ public void setParticleCount(int particleCount) { public double getSlope() { return slope; } public void setSlope(double slope) { - this.slope = Math.max(slope, MathUtil.EPSILON); + this.slope = Math.max(slope, Shape.EPSILON); this.setNeedsUpdate(true); } @@ -72,7 +88,7 @@ public void setDirection(int direction) { @Override public void setLength(double length) { - height = Math.max(length, MathUtil.EPSILON); + height = Math.max(length, Shape.EPSILON); this.setNeedsUpdate(true); } @@ -87,7 +103,7 @@ public void setWidth(double width) { } @Override public void setHeight(double height) { - this.height = Math.max(height, MathUtil.EPSILON); + this.height = Math.max(height, Shape.EPSILON); this.setNeedsUpdate(true); } @@ -96,7 +112,7 @@ public void setHeight(double height) { @Override public void setRadius(double radius) { - this.radius = Math.max(radius, MathUtil.EPSILON); + this.radius = Math.max(radius, Shape.EPSILON); this.setNeedsUpdate(true); } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/IrregularPolygon.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/IrregularPolygon.java similarity index 88% rename from shapes-lib/src/main/java/com/sovdee/shapes/IrregularPolygon.java rename to shapes-lib/src/main/java/com/sovdee/shapes/shapes/IrregularPolygon.java index ff00a15..f5d3df3 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/IrregularPolygon.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/IrregularPolygon.java @@ -1,6 +1,5 @@ -package com.sovdee.shapes; +package com.sovdee.shapes.shapes; -import com.sovdee.shapes.util.MathUtil; import org.joml.Vector3d; import java.util.ArrayList; @@ -48,8 +47,8 @@ private void setBounds(Collection vertices) { @Override public void generateOutline(Set points) { double particleDensity = this.getParticleDensity(); - points.addAll(MathUtil.connectPoints(vertices, particleDensity)); - points.addAll(MathUtil.calculateLine(vertices.get(0), vertices.get(vertices.size() - 1), particleDensity)); + points.addAll(Line.connectPoints(vertices, particleDensity)); + points.addAll(Line.calculateLine(vertices.get(0), vertices.get(vertices.size() - 1), particleDensity)); if (height != 0) { Set upperPoints = new LinkedHashSet<>(); for (Vector3d v : points) { @@ -57,7 +56,7 @@ public void generateOutline(Set points) { } points.addAll(upperPoints); for (Vector3d v : vertices) { - points.addAll(MathUtil.calculateLine(v, new Vector3d(v.x, height, v.z), particleDensity)); + points.addAll(Line.calculateLine(v, new Vector3d(v.x, height, v.z), particleDensity)); } } } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/LWHShape.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/LWHShape.java similarity index 89% rename from shapes-lib/src/main/java/com/sovdee/shapes/LWHShape.java rename to shapes-lib/src/main/java/com/sovdee/shapes/shapes/LWHShape.java index b142db2..54ce72a 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/LWHShape.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/LWHShape.java @@ -1,4 +1,4 @@ -package com.sovdee.shapes; +package com.sovdee.shapes.shapes; /** * Represents a shape that has a length, width, and/or height. diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Line.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Line.java similarity index 70% rename from shapes-lib/src/main/java/com/sovdee/shapes/Line.java rename to shapes-lib/src/main/java/com/sovdee/shapes/shapes/Line.java index 4fe7efe..60f90ec 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/Line.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Line.java @@ -1,8 +1,9 @@ -package com.sovdee.shapes; +package com.sovdee.shapes.shapes; -import com.sovdee.shapes.util.MathUtil; import org.joml.Vector3d; +import java.util.LinkedHashSet; +import java.util.List; import java.util.Set; import java.util.function.Supplier; @@ -36,9 +37,40 @@ public Line(Supplier start, Supplier end) { setDynamic(true); } + /** + * Calculates points along a line from start to end with the given particle density. + * Uses additive stepping for efficiency. + */ + public static Set calculateLine(Vector3d start, Vector3d end, double particleDensity) { + Set points = new LinkedHashSet<>(); + Vector3d direction = new Vector3d(end).sub(start); + double length = direction.length(); + double step = length / Math.round(length / particleDensity); + direction.normalize().mul(step); + + Vector3d current = new Vector3d(start); + int count = (int) (length / step); + for (int i = 0; i <= count; i++) { + points.add(new Vector3d(current)); + current.add(direction); + } + return points; + } + + /** + * Connects a list of points with lines, returning all intermediate points. + */ + public static Set connectPoints(List points, double particleDensity) { + Set connectedPoints = new LinkedHashSet<>(); + for (int i = 0; i < points.size() - 1; i++) { + connectedPoints.addAll(calculateLine(points.get(i), points.get(i + 1), particleDensity)); + } + return connectedPoints; + } + @Override public void generateOutline(Set points) { - points.addAll(MathUtil.calculateLine(getStart(), getEnd(), this.getParticleDensity())); + points.addAll(calculateLine(getStart(), getEnd(), this.getParticleDensity())); } public Vector3d getStart() { @@ -93,7 +125,7 @@ public double getLength() { @Override public void setLength(double length) { - length = Math.max(length, MathUtil.EPSILON); + length = Math.max(length, Shape.EPSILON); Vector3d start = getStart(); Vector3d end = getEnd(); Vector3d direction = new Vector3d(end).sub(start).normalize(); diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/PolyShape.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/PolyShape.java similarity index 87% rename from shapes-lib/src/main/java/com/sovdee/shapes/PolyShape.java rename to shapes-lib/src/main/java/com/sovdee/shapes/shapes/PolyShape.java index 3213314..51a75f9 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/PolyShape.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/PolyShape.java @@ -1,4 +1,4 @@ -package com.sovdee.shapes; +package com.sovdee.shapes.shapes; /** * Represents a shape that has a number of sides and a side length. diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/RadialShape.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/RadialShape.java similarity index 85% rename from shapes-lib/src/main/java/com/sovdee/shapes/RadialShape.java rename to shapes-lib/src/main/java/com/sovdee/shapes/shapes/RadialShape.java index 109d226..3ffa825 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/RadialShape.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/RadialShape.java @@ -1,4 +1,4 @@ -package com.sovdee.shapes; +package com.sovdee.shapes.shapes; /** * Represents a shape that has a radius. diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Rectangle.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Rectangle.java similarity index 94% rename from shapes-lib/src/main/java/com/sovdee/shapes/Rectangle.java rename to shapes-lib/src/main/java/com/sovdee/shapes/shapes/Rectangle.java index ccfae5f..0e36789 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/Rectangle.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Rectangle.java @@ -1,6 +1,5 @@ -package com.sovdee.shapes; +package com.sovdee.shapes.shapes; -import com.sovdee.shapes.util.MathUtil; import org.joml.Vector3d; import java.util.Set; @@ -24,8 +23,8 @@ public class Rectangle extends AbstractShape implements LWHShape { public Rectangle(double length, double width, Plane plane) { super(); this.plane = plane; - this.halfLength = Math.max(length / 2, MathUtil.EPSILON); - this.halfWidth = Math.max(width / 2, MathUtil.EPSILON); + this.halfLength = Math.max(length / 2, Shape.EPSILON); + this.halfWidth = Math.max(width / 2, Shape.EPSILON); calculateSteps(); } @@ -131,7 +130,7 @@ public void setParticleCount(int particleCount) { @Override public void setLength(double length) { - this.halfLength = Math.max(length / 2, MathUtil.EPSILON); + this.halfLength = Math.max(length / 2, Shape.EPSILON); this.setNeedsUpdate(true); } @@ -140,7 +139,7 @@ public void setLength(double length) { @Override public void setWidth(double width) { - this.halfWidth = Math.max(width / 2, MathUtil.EPSILON); + this.halfWidth = Math.max(width / 2, Shape.EPSILON); this.setNeedsUpdate(true); } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/RegularPolygon.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/RegularPolygon.java similarity index 51% rename from shapes-lib/src/main/java/com/sovdee/shapes/RegularPolygon.java rename to shapes-lib/src/main/java/com/sovdee/shapes/shapes/RegularPolygon.java index 85f48b0..297d79b 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/RegularPolygon.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/RegularPolygon.java @@ -1,8 +1,9 @@ -package com.sovdee.shapes; +package com.sovdee.shapes.shapes; -import com.sovdee.shapes.util.MathUtil; +import com.sovdee.shapes.util.VectorUtil; import org.joml.Vector3d; +import java.util.LinkedHashSet; import java.util.Set; public class RegularPolygon extends AbstractShape implements PolyShape, RadialShape, LWHShape { @@ -25,25 +26,71 @@ public RegularPolygon(int sides, double radius, double height) { public RegularPolygon(double angle, double radius, double height) { super(); - this.angle = MathUtil.clamp(angle, MathUtil.EPSILON, Math.PI * 2 / 3); - this.radius = Math.max(radius, MathUtil.EPSILON); + this.angle = Math.clamp(angle, Shape.EPSILON, Math.PI * 2 / 3); + this.radius = Math.max(radius, Shape.EPSILON); this.height = Math.max(height, 0); } + // --- Static calculation methods --- + + public static Set calculateRegularPolygon(double radius, double angle, double particleDensity, boolean wireframe) { + angle = Math.max(angle, Shape.EPSILON); + + Set points = new LinkedHashSet<>(); + double apothem = radius * Math.cos(angle / 2); + double radiusStep = radius / Math.round(apothem / particleDensity); + if (wireframe) { + radiusStep = 2 * radius; + } else { + points.add(new Vector3d(0, 0, 0)); + } + for (double subRadius = radius; subRadius >= 0; subRadius -= radiusStep) { + Vector3d vertex = new Vector3d(subRadius, 0, 0); + for (double i = 0; i < 2 * Math.PI; i += angle) { + points.addAll(Line.calculateLine( + VectorUtil.rotateAroundY(new Vector3d(vertex), i), + VectorUtil.rotateAroundY(new Vector3d(vertex), i + angle), + particleDensity)); + } + } + return points; + } + + public static Set calculateRegularPrism(double radius, double angle, double height, double particleDensity, boolean wireframe) { + Set points = new LinkedHashSet<>(); + Vector3d vertex = new Vector3d(radius, 0, 0); + for (double i = 0; i < 2 * Math.PI; i += angle) { + Vector3d currentVertex = VectorUtil.rotateAroundY(new Vector3d(vertex), i); + for (Vector3d vector : Line.calculateLine(currentVertex, VectorUtil.rotateAroundY(new Vector3d(vertex), i + angle), particleDensity)) { + points.add(vector); + if (wireframe) { + points.add(new Vector3d(vector.x, height, vector.z)); + } else { + points.addAll(Line.calculateLine(vector, new Vector3d(vector.x, height, vector.z), particleDensity)); + } + } + if (wireframe) + points.addAll(Line.calculateLine(currentVertex, new Vector3d(currentVertex.x, height, currentVertex.z), particleDensity)); + } + return points; + } + + // --- Generation methods --- + @Override public void generateOutline(Set points) { if (height == 0) - points.addAll(MathUtil.calculateRegularPolygon(this.radius, this.angle, this.getParticleDensity(), true)); + points.addAll(calculateRegularPolygon(this.radius, this.angle, this.getParticleDensity(), true)); else - points.addAll(MathUtil.calculateRegularPrism(this.radius, this.angle, this.height, this.getParticleDensity(), true)); + points.addAll(calculateRegularPrism(this.radius, this.angle, this.height, this.getParticleDensity(), true)); } @Override public void generateSurface(Set points) { if (height == 0) - points.addAll(MathUtil.calculateRegularPolygon(this.radius, this.angle, this.getParticleDensity(), false)); + points.addAll(calculateRegularPolygon(this.radius, this.angle, this.getParticleDensity(), false)); else - points.addAll(MathUtil.calculateRegularPrism(this.radius, this.angle, this.height, this.getParticleDensity(), false)); + points.addAll(calculateRegularPrism(this.radius, this.angle, this.height, this.getParticleDensity(), false)); } @Override @@ -52,8 +99,9 @@ public void generateFilled(Set points) { generateSurface(points); else { double particleDensity = this.getParticleDensity(); - Set polygon = MathUtil.calculateRegularPolygon(this.radius, this.angle, particleDensity, false); - points.addAll(MathUtil.fillVertically(polygon, height, particleDensity)); + Set polygon = calculateRegularPolygon(this.radius, this.angle, particleDensity, false); + fillVertically(polygon, height, particleDensity); + points.addAll(polygon); } } @@ -91,9 +139,9 @@ public void setSides(int sides) { @Override public void setSideLength(double sideLength) { - sideLength = Math.max(sideLength, MathUtil.EPSILON); + sideLength = Math.max(sideLength, Shape.EPSILON); this.radius = sideLength / (2 * Math.sin(this.angle / 2)); - this.radius = Math.max(radius, MathUtil.EPSILON); + this.radius = Math.max(radius, Shape.EPSILON); this.setNeedsUpdate(true); } @@ -102,7 +150,7 @@ public void setSideLength(double sideLength) { @Override public void setRadius(double radius) { - this.radius = Math.max(radius, MathUtil.EPSILON); + this.radius = Math.max(radius, Shape.EPSILON); this.setNeedsUpdate(true); } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/RegularPolyhedron.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/RegularPolyhedron.java similarity index 81% rename from shapes-lib/src/main/java/com/sovdee/shapes/RegularPolyhedron.java rename to shapes-lib/src/main/java/com/sovdee/shapes/shapes/RegularPolyhedron.java index 5d6ed5a..6bee084 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/RegularPolyhedron.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/RegularPolyhedron.java @@ -1,7 +1,5 @@ -package com.sovdee.shapes; +package com.sovdee.shapes.shapes; -import com.sovdee.shapes.util.MathUtil; -import com.sovdee.shapes.util.VectorUtil; import org.joml.Quaterniond; import org.joml.Vector3d; @@ -60,12 +58,24 @@ public class RegularPolyhedron extends AbstractShape implements RadialShape, Pol new Quaterniond(0.5, 0.3090169943749475, -0.6881909602355868, -0.42532540417602), new Quaterniond(0.3090169943749475, -0.5, -0.42532540417602, 0.6881909602355868) }; + // Radius-to-side-length conversion factors for each polyhedron type + private static final double TETRA_R2SL = 0.6123724356957945; + private static final double OCTA_R2SL = 0.7071067811865; + private static final double DODECA_R2SL = 1.401258538; + private static final double ICOSA_R2SL = 0.9510565162951535; + + // Side-length to inscribed-radius conversion factors + private static final double TETRA_INSC = 1.0 / 4.89897948556; + private static final double OCTA_INSC = 0.408248290; + private static final double DODECA_INSC = 1.113516364; + private static final double ICOSA_INSC = 0.7557613141; + private double radius; private int faces; public RegularPolyhedron(double radius, int faces) { super(); - this.radius = Math.max(radius, MathUtil.EPSILON); + this.radius = Math.max(radius, Shape.EPSILON); this.faces = switch (faces) { case 4, 8, 12, 20 -> faces; default -> 4; @@ -110,17 +120,17 @@ private Set generatePolyhedron(Quaterniond[] rotations, double radius) Set points = new LinkedHashSet<>(); int sides = this.faces == 12 ? 5 : 3; double sideLength = switch (faces) { - case 4 -> radius / 0.6123724356957945; - case 8 -> radius / 0.7071067811865; - case 12 -> radius / 1.401258538; - case 20 -> radius / 0.9510565162951535; + case 4 -> radius / TETRA_R2SL; + case 8 -> radius / OCTA_R2SL; + case 12 -> radius / DODECA_R2SL; + case 20 -> radius / ICOSA_R2SL; default -> 0.0; }; double inscribedRadius = switch (this.faces) { - case 4 -> sideLength / 4.89897948556; - case 8 -> sideLength * 0.408248290; - case 12 -> sideLength * 1.113516364; - case 20 -> sideLength * 0.7557613141; + case 4 -> sideLength * TETRA_INSC; + case 8 -> sideLength * OCTA_INSC; + case 12 -> sideLength * DODECA_INSC; + case 20 -> sideLength * ICOSA_INSC; default -> 1; }; Vector3d offset = new Vector3d(0, inscribedRadius, 0); @@ -138,7 +148,7 @@ private Set generatePolyhedron(Quaterniond[] rotations, double radius) } private Set generateFaceOutline(int sides, double radius) { - return new LinkedHashSet<>(MathUtil.calculateRegularPolygon(radius, 2 * Math.PI / sides, this.getParticleDensity(), true)); + return new LinkedHashSet<>(RegularPolygon.calculateRegularPolygon(radius, 2 * Math.PI / sides, this.getParticleDensity(), true)); } private Set generateFaceSurface(int sides, double radius) { @@ -147,7 +157,7 @@ private Set generateFaceSurface(int sides, double radius) { double apothem = radius * Math.cos(Math.PI / sides); double radiusStep = radius / Math.round(apothem / particleDensity); for (double subRadius = radius; subRadius > 0; subRadius -= radiusStep) { - facePoints.addAll(MathUtil.calculateRegularPolygon(subRadius, 2 * Math.PI / sides, particleDensity, false)); + facePoints.addAll(RegularPolygon.calculateRegularPolygon(subRadius, 2 * Math.PI / sides, particleDensity, false)); } facePoints.add(new Vector3d(0, 0, 0)); return facePoints; @@ -176,22 +186,22 @@ public void setSides(int sides) { @Override public double getSideLength() { return switch (faces) { - case 4 -> radius / 0.6123724356957945; - case 8 -> radius / 0.7071067811865; - case 12 -> radius / 1.401258538; - case 20 -> radius / 0.9510565162951535; + case 4 -> radius / TETRA_R2SL; + case 8 -> radius / OCTA_R2SL; + case 12 -> radius / DODECA_R2SL; + case 20 -> radius / ICOSA_R2SL; default -> 0.0; }; } @Override public void setSideLength(double sideLength) { - sideLength = Math.max(sideLength, MathUtil.EPSILON); + sideLength = Math.max(sideLength, Shape.EPSILON); switch (faces) { - case 4 -> this.radius = sideLength * 0.6123724356957945; - case 8 -> this.radius = sideLength * 0.7071067811865; - case 12 -> this.radius = sideLength * 1.401258538; - case 20 -> this.radius = sideLength * 0.9510565162951535; + case 4 -> this.radius = sideLength * TETRA_R2SL; + case 8 -> this.radius = sideLength * OCTA_R2SL; + case 12 -> this.radius = sideLength * DODECA_R2SL; + case 20 -> this.radius = sideLength * ICOSA_R2SL; default -> { return; } } this.setNeedsUpdate(true); @@ -202,7 +212,7 @@ public void setSideLength(double sideLength) { @Override public void setRadius(double radius) { - this.radius = Math.max(radius, MathUtil.EPSILON); + this.radius = Math.max(radius, Shape.EPSILON); this.setNeedsUpdate(true); } } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Shape.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Shape.java similarity index 98% rename from shapes-lib/src/main/java/com/sovdee/shapes/Shape.java rename to shapes-lib/src/main/java/com/sovdee/shapes/shapes/Shape.java index 3f40a7a..793699e 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/Shape.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Shape.java @@ -1,5 +1,6 @@ -package com.sovdee.shapes; +package com.sovdee.shapes.shapes; +import com.sovdee.shapes.DrawContext; import org.joml.Quaterniond; import org.joml.Vector3d; @@ -14,6 +15,8 @@ */ public interface Shape extends Cloneable { + double EPSILON = 0.0001; + /** * Gets the points for the shape using the shape's own orientation. * Uses cached points if the shape has not been modified. diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Sphere.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Sphere.java new file mode 100644 index 0000000..df384c3 --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Sphere.java @@ -0,0 +1,123 @@ +package com.sovdee.shapes.shapes; + +import org.joml.Vector3d; + +import java.util.LinkedHashSet; +import java.util.Set; + +public class Sphere extends AbstractShape implements RadialShape { + + private static final double PHI = Math.PI * (3.0 - Math.sqrt(5.0)); + private static final double[] SPHERE_THETA_COS = new double[4096]; + private static final double[] SPHERE_THETA_SIN = new double[4096]; + + static { + for (int i = 0; i < SPHERE_THETA_COS.length; i++) { + SPHERE_THETA_COS[i] = Math.cos(PHI * i); + SPHERE_THETA_SIN[i] = Math.sin(PHI * i); + } + } + + private double radius; + protected double cutoffAngle; + protected double cutoffAngleCos; + + public Sphere(double radius) { + super(); + this.radius = Math.max(radius, Shape.EPSILON); + this.cutoffAngle = Math.PI; + this.cutoffAngleCos = -1.0; + this.setStyle(Style.SURFACE); + } + + // --- Static calculation methods --- + + private static Set calculateFibonacciSphere(int pointCount, double radius) { + return calculateFibonacciSphere(pointCount, radius, Math.PI); + } + + private static Set calculateFibonacciSphere(int pointCount, double radius, double angleCutoff) { + Set points = new LinkedHashSet<>(); + double y = 1; + if (angleCutoff > Math.PI) angleCutoff = Math.PI; + double yLimit = Math.cos(angleCutoff); + + double yStep = 2.0 / pointCount; + int preCompPoints = Math.min(pointCount, SPHERE_THETA_COS.length); + for (int i = 0; i < preCompPoints; i++) { + double r = Math.sqrt(1 - y * y) * radius; + points.add(new Vector3d(r * SPHERE_THETA_COS[i], y * radius, r * SPHERE_THETA_SIN[i])); + y -= yStep; + if (y <= yLimit) { + return points; + } + } + if (pointCount > preCompPoints) { + for (int i = preCompPoints; i < pointCount; i++) { + double r = Math.sqrt(1 - y * y) * radius; + double theta = PHI * i; + points.add(new Vector3d(r * Math.cos(theta), y * radius, r * Math.sin(theta))); + y -= yStep; + if (y <= yLimit) { + return points; + } + } + } + return points; + } + + // --- Generation methods --- + + @Override + public void generateOutline(Set points) { + this.generateSurface(points); + } + + @Override + public void generateSurface(Set points) { + double particleDensity = this.getParticleDensity(); + int pointCount = 4 * (int) (Math.PI * radius * radius / (particleDensity * particleDensity)); + points.addAll(calculateFibonacciSphere(pointCount, radius, cutoffAngle)); + } + + @Override + public void generateFilled(Set points) { + double particleDensity = this.getParticleDensity(); + int subSpheres = (int) (radius / particleDensity) - 1; + double radiusStep = radius / subSpheres; + for (int i = 1; i < subSpheres; i++) { + double subRadius = i * radiusStep; + int pointCount = 4 * (int) (Math.PI * subRadius * subRadius / (particleDensity * particleDensity)); + points.addAll(calculateFibonacciSphere(pointCount, subRadius, cutoffAngle)); + } + } + + @Override + public void setParticleCount(int particleCount) { + particleCount = Math.max(particleCount, 1); + this.setParticleDensity(switch (this.getStyle()) { + case OUTLINE, SURFACE -> Math.sqrt(2 * Math.PI * radius * radius * (1 - cutoffAngleCos) / particleCount); + case FILL -> + Math.cbrt(Math.PI / 3 * radius * radius * radius * (2 + cutoffAngleCos) * (1 - cutoffAngleCos) * (1 - cutoffAngleCos) / particleCount); + }); + this.setNeedsUpdate(true); + } + + @Override + public double getRadius() { return radius; } + + @Override + public void setRadius(double radius) { + this.radius = Math.max(radius, Shape.EPSILON); + this.setNeedsUpdate(true); + } + + @Override + public Shape clone() { + return this.copyTo(new Sphere(radius)); + } + + public String toString() { + return this.getStyle() + " sphere with radius " + this.radius; + } +} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/SphericalCap.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/SphericalCap.java similarity index 74% rename from shapes-lib/src/main/java/com/sovdee/shapes/SphericalCap.java rename to shapes-lib/src/main/java/com/sovdee/shapes/shapes/SphericalCap.java index 964c646..45b3bc1 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/SphericalCap.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/SphericalCap.java @@ -1,12 +1,10 @@ -package com.sovdee.shapes; - -import com.sovdee.shapes.util.MathUtil; +package com.sovdee.shapes.shapes; public class SphericalCap extends Sphere implements CutoffShape { public SphericalCap(double radius, double cutoffAngle) { super(radius); - this.cutoffAngle = MathUtil.clamp(cutoffAngle, 0, Math.PI); + this.cutoffAngle = Math.clamp(cutoffAngle, 0, Math.PI); this.cutoffAngleCos = Math.cos(this.cutoffAngle); } @@ -17,7 +15,7 @@ public double getCutoffAngle() { @Override public void setCutoffAngle(double cutoffAngle) { - this.cutoffAngle = MathUtil.clamp(cutoffAngle, 0, Math.PI); + this.cutoffAngle = Math.clamp(cutoffAngle, 0, Math.PI); this.cutoffAngleCos = Math.cos(this.cutoffAngle); this.setNeedsUpdate(true); } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/Star.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Star.java similarity index 54% rename from shapes-lib/src/main/java/com/sovdee/shapes/Star.java rename to shapes-lib/src/main/java/com/sovdee/shapes/shapes/Star.java index 80a8b79..b6633ee 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/Star.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Star.java @@ -1,8 +1,9 @@ -package com.sovdee.shapes; +package com.sovdee.shapes.shapes; -import com.sovdee.shapes.util.MathUtil; +import com.sovdee.shapes.util.VectorUtil; import org.joml.Vector3d; +import java.util.LinkedHashSet; import java.util.Set; public class Star extends AbstractShape { @@ -13,14 +14,26 @@ public class Star extends AbstractShape { public Star(double innerRadius, double outerRadius, double angle) { super(); - this.innerRadius = Math.max(innerRadius, MathUtil.EPSILON); - this.outerRadius = Math.max(outerRadius, MathUtil.EPSILON); - this.angle = MathUtil.clamp(angle, MathUtil.EPSILON, Math.PI); + this.innerRadius = Math.max(innerRadius, Shape.EPSILON); + this.outerRadius = Math.max(outerRadius, Shape.EPSILON); + this.angle = Math.clamp(angle, Shape.EPSILON, Math.PI); + } + + private static Set calculateStar(double innerRadius, double outerRadius, double angle, double particleDensity) { + Set points = new LinkedHashSet<>(); + Vector3d outerVertex = new Vector3d(outerRadius, 0, 0); + Vector3d innerVertex = new Vector3d(innerRadius, 0, 0); + for (double theta = 0; theta < 2 * Math.PI; theta += angle) { + Vector3d currentVertex = VectorUtil.rotateAroundY(new Vector3d(outerVertex), theta); + points.addAll(Line.calculateLine(currentVertex, VectorUtil.rotateAroundY(new Vector3d(innerVertex), theta + angle / 2), particleDensity)); + points.addAll(Line.calculateLine(currentVertex, VectorUtil.rotateAroundY(new Vector3d(innerVertex), theta - angle / 2), particleDensity)); + } + return points; } @Override public void generateOutline(Set points) { - points.addAll(MathUtil.calculateStar(innerRadius, outerRadius, angle, this.getParticleDensity())); + points.addAll(calculateStar(innerRadius, outerRadius, angle, this.getParticleDensity())); } @Override @@ -28,7 +41,7 @@ public void generateSurface(Set points) { double minRadius = Math.min(innerRadius, outerRadius); double particleDensity = this.getParticleDensity(); for (double r = 0; r < minRadius; r += particleDensity) { - points.addAll(MathUtil.calculateStar(innerRadius - r, outerRadius - r, angle, particleDensity)); + points.addAll(calculateStar(innerRadius - r, outerRadius - r, angle, particleDensity)); } } @@ -43,14 +56,14 @@ public void setParticleCount(int particleCount) { public double getInnerRadius() { return innerRadius; } public void setInnerRadius(double innerRadius) { - this.innerRadius = Math.max(innerRadius, MathUtil.EPSILON); + this.innerRadius = Math.max(innerRadius, Shape.EPSILON); this.setNeedsUpdate(true); } public double getOuterRadius() { return outerRadius; } public void setOuterRadius(double outerRadius) { - this.outerRadius = Math.max(outerRadius, MathUtil.EPSILON); + this.outerRadius = Math.max(outerRadius, Shape.EPSILON); this.setNeedsUpdate(true); } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/util/MathUtil.java b/shapes-lib/src/main/java/com/sovdee/shapes/util/MathUtil.java deleted file mode 100644 index 7152b04..0000000 --- a/shapes-lib/src/main/java/com/sovdee/shapes/util/MathUtil.java +++ /dev/null @@ -1,248 +0,0 @@ -package com.sovdee.shapes.util; - -import org.joml.Vector3d; - -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -public class MathUtil { - public static final double PHI = Math.PI * (3.0 - Math.sqrt(5.0)); - public static final double PHI_RECIPROCAL = 1.0 / PHI; - public static final double PHI_SQUARED = PHI * PHI; - public static final double[] SPHERE_THETA_COS = new double[4096]; - public static final double[] SPHERE_THETA_SIN = new double[4096]; - public static final double EPSILON = 0.0001; - - static { - for (int i = 0; i < SPHERE_THETA_COS.length; i++) { - SPHERE_THETA_COS[i] = Math.cos(MathUtil.PHI * i); - SPHERE_THETA_SIN[i] = Math.sin(MathUtil.PHI * i); - } - } - - public static double clamp(double value, double min, double max) { - return Math.max(min, Math.min(max, value)); - } - - public static Set calculateFibonacciSphere(int pointCount, double radius) { - return calculateFibonacciSphere(pointCount, radius, Math.PI); - } - - public static Set calculateFibonacciSphere(int pointCount, double radius, double angleCutoff) { - Set points = new LinkedHashSet<>(); - double y = 1; - if (angleCutoff > Math.PI) angleCutoff = Math.PI; - double yLimit = Math.cos(angleCutoff); - - double yStep = 2.0 / pointCount; - int preCompPoints = Math.min(pointCount, MathUtil.SPHERE_THETA_COS.length); - for (int i = 0; i < preCompPoints; i++) { - double r = Math.sqrt(1 - y * y) * radius; - points.add(new Vector3d(r * MathUtil.SPHERE_THETA_COS[i], y * radius, r * MathUtil.SPHERE_THETA_SIN[i])); - y -= yStep; - if (y <= yLimit) { - return points; - } - } - if (pointCount > preCompPoints) { - for (int i = preCompPoints; i < pointCount; i++) { - double r = Math.sqrt(1 - y * y) * radius; - double theta = MathUtil.PHI * i; - points.add(new Vector3d(r * Math.cos(theta), y * radius, r * Math.sin(theta))); - y -= yStep; - if (y <= yLimit) { - return points; - } - } - } - return points; - } - - public static Set calculateCircle(double radius, double particleDensity, double cutoffAngle) { - Set points = new LinkedHashSet<>(); - double stepSize = particleDensity / radius; - for (double theta = 0; theta < cutoffAngle; theta += stepSize) { - points.add(new Vector3d(Math.cos(theta) * radius, 0, Math.sin(theta) * radius)); - } - return points; - } - - public static Set calculateDisc(double radius, double particleDensity, double cutoffAngle) { - Set points = new LinkedHashSet<>(); - for (double subRadius = particleDensity; subRadius < radius; subRadius += particleDensity) { - points.addAll(calculateCircle(subRadius, particleDensity, cutoffAngle)); - } - points.addAll(calculateCircle(radius, particleDensity, cutoffAngle)); - return points; - } - - public static Set calculateHelix(double radius, double height, double slope, int direction, double particleDensity) { - Set points = new LinkedHashSet<>(); - if (radius <= 0 || height <= 0) { - return points; - } - double loops = Math.abs(height / slope); - double length = slope * slope + radius * radius; - double stepSize = particleDensity / length; - for (double t = 0; t < loops; t += stepSize) { - double x = radius * Math.cos(direction * t); - double z = radius * Math.sin(direction * t); - points.add(new Vector3d(x, t * slope, z)); - } - return points; - } - - public static Set calculateLine(Vector3d start, Vector3d end, double particleDensity) { - Set points = new LinkedHashSet<>(); - Vector3d direction = new Vector3d(end).sub(start); - double length = direction.length(); - double step = length / Math.round(length / particleDensity); - direction.normalize().mul(step); - - for (double i = 0; i <= (length / step); i++) { - points.add(new Vector3d(start).add(new Vector3d(direction).mul(i))); - } - return points; - } - - public static Set calculateRegularPolygon(double radius, double angle, double particleDensity, boolean wireframe) { - angle = Math.max(angle, MathUtil.EPSILON); - - Set points = new LinkedHashSet<>(); - double apothem = radius * Math.cos(angle / 2); - double radiusStep = radius / Math.round(apothem / particleDensity); - if (wireframe) { - radiusStep = 2 * radius; - } else { - points.add(new Vector3d(0, 0, 0)); - } - for (double subRadius = radius; subRadius >= 0; subRadius -= radiusStep) { - Vector3d vertex = new Vector3d(subRadius, 0, 0); - for (double i = 0; i < 2 * Math.PI; i += angle) { - points.addAll(calculateLine( - VectorUtil.rotateAroundY(new Vector3d(vertex), i), - VectorUtil.rotateAroundY(new Vector3d(vertex), i + angle), - particleDensity)); - } - } - return points; - } - - public static Set calculateRegularPrism(double radius, double angle, double height, double particleDensity, boolean wireframe) { - Set points = new LinkedHashSet<>(); - Vector3d vertex = new Vector3d(radius, 0, 0); - for (double i = 0; i < 2 * Math.PI; i += angle) { - Vector3d currentVertex = VectorUtil.rotateAroundY(new Vector3d(vertex), i); - for (Vector3d vector : calculateLine(currentVertex, VectorUtil.rotateAroundY(new Vector3d(vertex), i + angle), particleDensity)) { - points.add(vector); - if (wireframe) { - points.add(new Vector3d(vector.x, height, vector.z)); - } else { - points.addAll(calculateLine(vector, new Vector3d(vector.x, height, vector.z), particleDensity)); - } - } - if (wireframe) - points.addAll(calculateLine(currentVertex, new Vector3d(currentVertex.x, height, currentVertex.z), particleDensity)); - } - return points; - } - - public static Set connectPoints(List points, double particleDensity) { - Set connectedPoints = new LinkedHashSet<>(); - for (int i = 0; i < points.size() - 1; i++) { - connectedPoints.addAll(calculateLine(points.get(i), points.get(i + 1), particleDensity)); - } - return connectedPoints; - } - - private static double ellipseCircumference(double r1, double r2) { - double a = Math.max(r1, r2); - double b = Math.min(r1, r2); - double h = Math.pow(a - b, 2) / Math.pow(a + b, 2); - return Math.PI * (a + b) * (1 + 3 * h / (10 + Math.sqrt(4 - 3 * h))); - } - - public static List calculateEllipse(double r1, double r2, double particleDensity, double cutoffAngle) { - List points = new ArrayList<>(); - double circumference = ellipseCircumference(r1, r2); - - int steps = (int) Math.round(circumference / particleDensity); - double theta = 0; - double angleStep = 0; - for (int i = 0; i < steps; i++) { - if (theta > cutoffAngle) { - break; - } - points.add(new Vector3d(r1 * Math.cos(theta), 0, r2 * Math.sin(theta))); - double dx = r1 * Math.sin(theta + 0.5 * angleStep); - double dy = r2 * Math.cos(theta + 0.5 * angleStep); - angleStep = particleDensity / Math.sqrt(dx * dx + dy * dy); - theta += angleStep; - } - return points; - } - - public static Set calculateEllipticalDisc(double r1, double r2, double particleDensity, double cutoffAngle) { - Set points = new LinkedHashSet<>(); - int steps = (int) Math.round(Math.max(r1, r2) / particleDensity); - double r; - for (double i = 1; i <= steps; i += 1) { - r = i / steps; - points.addAll(calculateEllipse(r1 * r, r2 * r, particleDensity, cutoffAngle)); - } - return points; - } - - public static Set calculateCylinder(double r1, double height, double particleDensity, double cutoffAngle) { - Set points = calculateDisc(r1, particleDensity, cutoffAngle); - points.addAll(points.stream().map(v -> new Vector3d(v.x, height, v.z)).collect(Collectors.toSet())); - Set wall = calculateCircle(r1, particleDensity, cutoffAngle); - points.addAll(fillVertically(wall, height, particleDensity)); - return points; - } - - public static Set calculateCylinder(double r1, double r2, double height, double particleDensity, double cutoffAngle) { - Set points = calculateEllipticalDisc(r1, r2, particleDensity, cutoffAngle); - points.addAll(points.stream().map(v -> new Vector3d(v.x, height, v.z)).collect(Collectors.toSet())); - Set wall = new LinkedHashSet<>(calculateEllipse(r1, r2, particleDensity, cutoffAngle)); - points.addAll(fillVertically(wall, height, particleDensity)); - return points; - } - - public static Set fillVertically(Set vectors, double height, double particleDensity) { - Set points = new LinkedHashSet<>(vectors); - double heightStep = height / Math.round(height / particleDensity); - for (double i = 0; i < height; i += heightStep) { - for (Vector3d vector : vectors) { - points.add(new Vector3d(vector.x, i, vector.z)); - } - } - return points; - } - - public static Set calculateHeart(double length, double width, double eccentricity, double particleDensity) { - Set points = new LinkedHashSet<>(); - double angleStep = 4 / 3.0 * particleDensity / (width + length); - for (double theta = 0; theta < Math.PI * 2; theta += angleStep) { - double x = width * Math.pow(Math.sin(theta), 3); - double y = length * (Math.cos(theta) - 1 / eccentricity * Math.cos(2 * theta) - 1.0 / 6 * Math.cos(3 * theta) - 1.0 / 16 * Math.cos(4 * theta)); - points.add(new Vector3d(x, 0, y)); - } - return points; - } - - public static Set calculateStar(double innerRadius, double outerRadius, double angle, double particleDensity) { - Set points = new LinkedHashSet<>(); - Vector3d outerVertex = new Vector3d(outerRadius, 0, 0); - Vector3d innerVertex = new Vector3d(innerRadius, 0, 0); - for (double theta = 0; theta < 2 * Math.PI; theta += angle) { - Vector3d currentVertex = VectorUtil.rotateAroundY(new Vector3d(outerVertex), theta); - points.addAll(calculateLine(currentVertex, VectorUtil.rotateAroundY(new Vector3d(innerVertex), theta + angle / 2), particleDensity)); - points.addAll(calculateLine(currentVertex, VectorUtil.rotateAroundY(new Vector3d(innerVertex), theta - angle / 2), particleDensity)); - } - return points; - } -} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/util/VectorUtil.java b/shapes-lib/src/main/java/com/sovdee/shapes/util/VectorUtil.java index dc05b57..8519212 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/util/VectorUtil.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/util/VectorUtil.java @@ -34,7 +34,7 @@ public static Vector3d rotateAroundY(Vector3d v, double angle) { * Transforms a list of vectors using a quaternion, modifying them in place. */ public static List transform(Quaterniond quaternion, List vectors) { - vectors.replaceAll(v -> quaternion.transform(v)); + vectors.replaceAll(quaternion::transform); return vectors; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffRotateShape.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffRotateShape.java index ca35a31..031b798 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffRotateShape.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffRotateShape.java @@ -10,7 +10,7 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.util.Kleenean; import com.sovdee.skriptparticles.elements.sections.DrawShapeEffectSection.DrawEvent; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Shape; import org.bukkit.event.Event; import org.bukkit.util.Vector; import org.jetbrains.annotations.Nullable; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffSetOrdering.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffSetOrdering.java index 962a650..a2b5c7e 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffSetOrdering.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffSetOrdering.java @@ -9,7 +9,7 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Shape; import org.bukkit.event.Event; import org.checkerframework.checker.nullness.qual.Nullable; import org.joml.Vector3d; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffToggleAxes.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffToggleAxes.java index 9e701db..b8026f5 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffToggleAxes.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffToggleAxes.java @@ -9,7 +9,7 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Shape; import com.sovdee.skriptparticles.shapes.DrawData; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprDrawnShapes.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprDrawnShapes.java index e9874f6..87cb82a 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprDrawnShapes.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprDrawnShapes.java @@ -7,7 +7,7 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.EventValueExpression; import ch.njol.skript.lang.ExpressionType; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprShapeCopy.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprShapeCopy.java index db30d40..a85e9a1 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprShapeCopy.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/ExprShapeCopy.java @@ -10,7 +10,7 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Shape; import org.bukkit.event.Event; import org.checkerframework.checker.nullness.qual.NonNull; import org.jetbrains.annotations.Nullable; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprArc.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprArc.java index 9985de0..3629f64 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprArc.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprArc.java @@ -11,9 +11,9 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Arc; -import com.sovdee.shapes.Shape; -import com.sovdee.shapes.Shape.Style; +import com.sovdee.shapes.shapes.Arc; +import com.sovdee.shapes.shapes.Shape; +import com.sovdee.shapes.shapes.Shape.Style; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprBezierCurve.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprBezierCurve.java index f53286a..d06629c 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprBezierCurve.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprBezierCurve.java @@ -10,8 +10,8 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.shapes.BezierCurve; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.BezierCurve; +import com.sovdee.shapes.shapes.Shape; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.Point; import com.sovdee.skriptparticles.util.VectorConversion; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCircle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCircle.java index 3bc2b3b..efcebc8 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCircle.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCircle.java @@ -11,9 +11,9 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Circle; -import com.sovdee.shapes.Shape; -import com.sovdee.shapes.Shape.Style; +import com.sovdee.shapes.shapes.Circle; +import com.sovdee.shapes.shapes.Shape; +import com.sovdee.shapes.shapes.Shape.Style; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCuboid.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCuboid.java index c8e2e79..be82e4e 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCuboid.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCuboid.java @@ -10,9 +10,9 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Cuboid; -import com.sovdee.shapes.Shape; -import com.sovdee.shapes.Shape.Style; +import com.sovdee.shapes.shapes.Cuboid; +import com.sovdee.shapes.shapes.Shape; +import com.sovdee.shapes.shapes.Shape.Style; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.DynamicLocation; import com.sovdee.skriptparticles.util.MathUtil; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipse.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipse.java index 39d754c..e04e24c 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipse.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipse.java @@ -11,9 +11,9 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Ellipse; -import com.sovdee.shapes.Shape; -import com.sovdee.shapes.Shape.Style; +import com.sovdee.shapes.shapes.Ellipse; +import com.sovdee.shapes.shapes.Shape; +import com.sovdee.shapes.shapes.Shape.Style; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipsoid.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipsoid.java index 14c64e7..c33e5bd 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipsoid.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipsoid.java @@ -11,9 +11,9 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Ellipsoid; -import com.sovdee.shapes.Shape; -import com.sovdee.shapes.Shape.Style; +import com.sovdee.shapes.shapes.Ellipsoid; +import com.sovdee.shapes.shapes.Shape; +import com.sovdee.shapes.shapes.Shape.Style; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipticalArc.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipticalArc.java index 4318d1c..f68ab22 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipticalArc.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipticalArc.java @@ -11,9 +11,9 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.shapes.EllipticalArc; -import com.sovdee.shapes.Shape; -import com.sovdee.shapes.Shape.Style; +import com.sovdee.shapes.shapes.EllipticalArc; +import com.sovdee.shapes.shapes.Shape; +import com.sovdee.shapes.shapes.Shape.Style; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHeart.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHeart.java index bbbdb69..d315ae3 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHeart.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHeart.java @@ -11,9 +11,9 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Heart; -import com.sovdee.shapes.Shape; -import com.sovdee.shapes.Shape.Style; +import com.sovdee.shapes.shapes.Heart; +import com.sovdee.shapes.shapes.Shape; +import com.sovdee.shapes.shapes.Shape.Style; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHelix.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHelix.java index 5f01c46..85d4a8a 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHelix.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHelix.java @@ -11,9 +11,9 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Helix; -import com.sovdee.shapes.Shape; -import com.sovdee.shapes.Shape.Style; +import com.sovdee.shapes.shapes.Helix; +import com.sovdee.shapes.shapes.Shape; +import com.sovdee.shapes.shapes.Shape.Style; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprIrregularPolygon.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprIrregularPolygon.java index 2f4b531..58cd16c 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprIrregularPolygon.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprIrregularPolygon.java @@ -11,8 +11,8 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.shapes.IrregularPolygon; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.IrregularPolygon; +import com.sovdee.shapes.shapes.Shape; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.DynamicLocation; import com.sovdee.skriptparticles.util.VectorConversion; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprLine.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprLine.java index 887ab95..3b0a7cd 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprLine.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprLine.java @@ -11,8 +11,8 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Line; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Line; +import com.sovdee.shapes.shapes.Shape; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.DynamicLocation; import com.sovdee.skriptparticles.util.MathUtil; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRectangle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRectangle.java index c0437ee..cf79587 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRectangle.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRectangle.java @@ -10,10 +10,10 @@ import ch.njol.skript.lang.SkriptParser; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Rectangle; -import com.sovdee.shapes.Rectangle.Plane; -import com.sovdee.shapes.Shape; -import com.sovdee.shapes.Shape.Style; +import com.sovdee.shapes.shapes.Rectangle; +import com.sovdee.shapes.shapes.Rectangle.Plane; +import com.sovdee.shapes.shapes.Shape; +import com.sovdee.shapes.shapes.Shape.Style; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.DynamicLocation; import com.sovdee.skriptparticles.util.MathUtil; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolygon.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolygon.java index af46763..44fcd12 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolygon.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolygon.java @@ -11,9 +11,9 @@ import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.skript.lang.util.SimpleLiteral; import ch.njol.util.Kleenean; -import com.sovdee.shapes.RegularPolygon; -import com.sovdee.shapes.Shape; -import com.sovdee.shapes.Shape.Style; +import com.sovdee.shapes.shapes.RegularPolygon; +import com.sovdee.shapes.shapes.Shape; +import com.sovdee.shapes.shapes.Shape.Style; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolyhedron.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolyhedron.java index 396056f..24c9c39 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolyhedron.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolyhedron.java @@ -10,9 +10,9 @@ import ch.njol.skript.lang.SkriptParser; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.shapes.RegularPolyhedron; -import com.sovdee.shapes.Shape; -import com.sovdee.shapes.Shape.Style; +import com.sovdee.shapes.shapes.RegularPolyhedron; +import com.sovdee.shapes.shapes.Shape; +import com.sovdee.shapes.shapes.Shape.Style; import com.sovdee.skriptparticles.shapes.DrawData; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphere.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphere.java index bea1b6d..69956d8 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphere.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphere.java @@ -11,9 +11,9 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Shape; -import com.sovdee.shapes.Shape.Style; -import com.sovdee.shapes.Sphere; +import com.sovdee.shapes.shapes.Shape; +import com.sovdee.shapes.shapes.Shape.Style; +import com.sovdee.shapes.shapes.Sphere; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphericalCap.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphericalCap.java index 5687bcc..ff8f3d4 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphericalCap.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphericalCap.java @@ -11,9 +11,9 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Shape; -import com.sovdee.shapes.Shape.Style; -import com.sovdee.shapes.SphericalCap; +import com.sovdee.shapes.shapes.Shape; +import com.sovdee.shapes.shapes.Shape.Style; +import com.sovdee.shapes.shapes.SphericalCap; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprStar.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprStar.java index 5ef3713..c0cb3ec 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprStar.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprStar.java @@ -10,9 +10,9 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Shape; -import com.sovdee.shapes.Shape.Style; -import com.sovdee.shapes.Star; +import com.sovdee.shapes.shapes.Shape; +import com.sovdee.shapes.shapes.Shape.Style; +import com.sovdee.shapes.shapes.Star; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprHelixWindingRate.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprHelixWindingRate.java index 807568c..d1a501c 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprHelixWindingRate.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprHelixWindingRate.java @@ -6,8 +6,8 @@ import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import com.sovdee.shapes.Helix; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Helix; +import com.sovdee.shapes.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeCutoffAngle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeCutoffAngle.java index 33a9c9e..8ad19ff 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeCutoffAngle.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeCutoffAngle.java @@ -6,8 +6,8 @@ import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import com.sovdee.shapes.CutoffShape; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.CutoffShape; +import com.sovdee.shapes.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLWH.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLWH.java index 7f4bd82..810af6e 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLWH.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLWH.java @@ -9,8 +9,8 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser; import ch.njol.util.Kleenean; -import com.sovdee.shapes.LWHShape; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.LWHShape; +import com.sovdee.shapes.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLocations.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLocations.java index 607cdfb..ee6191b 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLocations.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLocations.java @@ -11,7 +11,7 @@ import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.skript.util.Direction; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Shape; import com.sovdee.skriptparticles.util.VectorConversion; import org.bukkit.Location; import org.bukkit.event.Event; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeNormal.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeNormal.java index 90d3de9..2f23b22 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeNormal.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeNormal.java @@ -7,7 +7,7 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.PropertyExpression; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Shape; import com.sovdee.skriptparticles.util.Quaternion; import com.sovdee.skriptparticles.util.VectorConversion; import org.joml.Quaterniond; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOffset.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOffset.java index f61c64f..d92597c 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOffset.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOffset.java @@ -6,7 +6,7 @@ import ch.njol.skript.doc.Name; import ch.njol.skript.expressions.base.PropertyExpression; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Shape; import com.sovdee.skriptparticles.util.VectorConversion; import org.bukkit.event.Event; import org.bukkit.util.Vector; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOrientation.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOrientation.java index 42a50bb..4eaeda5 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOrientation.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeOrientation.java @@ -7,7 +7,7 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.PropertyExpression; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Shape; import com.sovdee.skriptparticles.util.Quaternion; import org.joml.Quaterniond; import org.bukkit.event.Event; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticle.java index 098eeab..458dd38 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticle.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticle.java @@ -7,7 +7,7 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.PropertyExpression; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Shape; import com.sovdee.skriptparticles.particles.Particle; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.ParticleUtil; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticleDensity.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticleDensity.java index 7da9ddd..977c2ad 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticleDensity.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticleDensity.java @@ -10,7 +10,7 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Shape; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapePoints.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapePoints.java index 8f2d1c9..86f1948 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapePoints.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapePoints.java @@ -8,7 +8,7 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Shape; import com.sovdee.skriptparticles.util.VectorConversion; import org.bukkit.event.Event; import org.bukkit.util.Vector; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRadius.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRadius.java index c68dde2..d0fca8b 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRadius.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRadius.java @@ -7,8 +7,8 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.PropertyExpression; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import com.sovdee.shapes.RadialShape; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.RadialShape; +import com.sovdee.shapes.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRelativeAxis.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRelativeAxis.java index c709680..562cb30 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRelativeAxis.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRelativeAxis.java @@ -9,7 +9,7 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Shape; import com.sovdee.skriptparticles.util.VectorConversion; import org.bukkit.util.Vector; import org.jetbrains.annotations.Nullable; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeScale.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeScale.java index 53e3221..89beadb 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeScale.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeScale.java @@ -7,7 +7,7 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.PropertyExpression; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSideLength.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSideLength.java index 3a379b5..552bc4f 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSideLength.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSideLength.java @@ -6,8 +6,8 @@ import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import com.sovdee.shapes.PolyShape; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.PolyShape; +import com.sovdee.shapes.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSides.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSides.java index fd1524d..01cb659 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSides.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSides.java @@ -6,8 +6,8 @@ import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import com.sovdee.shapes.PolyShape; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.PolyShape; +import com.sovdee.shapes.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeStyle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeStyle.java index b67d5b5..fdbb8c5 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeStyle.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeStyle.java @@ -7,7 +7,7 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.PropertyExpression; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarPoints.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarPoints.java index 81ae6a2..94f30eb 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarPoints.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarPoints.java @@ -6,8 +6,8 @@ import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import com.sovdee.shapes.Star; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Star; +import com.sovdee.shapes.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarRadii.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarRadii.java index d555dab..5108987 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarRadii.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprStarRadii.java @@ -9,9 +9,8 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Shape; -import com.sovdee.shapes.Star; -import com.sovdee.shapes.util.MathUtil; +import com.sovdee.shapes.shapes.Shape; +import com.sovdee.shapes.shapes.Star; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -71,15 +70,15 @@ public void change(Event event, Object @Nullable [] delta, Changer.ChangeMode mo for (Shape shape : shapes) { if (shape instanceof Star star) { if (isInner) { - star.setInnerRadius(Math.max(star.getInnerRadius() + deltaValue, MathUtil.EPSILON)); + star.setInnerRadius(Math.max(star.getInnerRadius() + deltaValue, Shape.EPSILON)); } else { - star.setOuterRadius(Math.max(star.getOuterRadius() + deltaValue, MathUtil.EPSILON)); + star.setOuterRadius(Math.max(star.getOuterRadius() + deltaValue, Shape.EPSILON)); } } } break; case SET: - deltaValue = Math.max(deltaValue, MathUtil.EPSILON); + deltaValue = Math.max(deltaValue, Shape.EPSILON); for (Shape shape : shapes) { if (shape instanceof Star star) { if (isInner) { diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/DrawShapeEffectSection.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/DrawShapeEffectSection.java index 1a58185..370bb28 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/DrawShapeEffectSection.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/DrawShapeEffectSection.java @@ -8,14 +8,13 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.Trigger; import ch.njol.skript.lang.TriggerItem; -import ch.njol.skript.lang.parser.ParserInstance; import ch.njol.skript.registrations.EventValues; import ch.njol.skript.util.Direction; import ch.njol.skript.util.Timespan; import ch.njol.skript.util.Timespan.TimePeriod; import ch.njol.skript.variables.Variables; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Shape; import com.sovdee.skriptparticles.SkriptParticle; import com.sovdee.skriptparticles.shapes.DrawManager; import com.sovdee.skriptparticles.util.DynamicLocation; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/EffSecDrawShape.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/EffSecDrawShape.java index 64918ac..c3afaba 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/EffSecDrawShape.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/EffSecDrawShape.java @@ -11,8 +11,7 @@ import ch.njol.skript.util.Timespan; import ch.njol.skript.util.Timespan.TimePeriod; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Shape; -import com.sovdee.skriptparticles.shapes.DrawData; +import com.sovdee.shapes.shapes.Shape; import com.sovdee.skriptparticles.util.DynamicLocation; import org.bukkit.entity.Player; import org.bukkit.event.Event; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/EffSecDrawShapeAnimation.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/EffSecDrawShapeAnimation.java index e861a7a..d65dbf1 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/EffSecDrawShapeAnimation.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/sections/EffSecDrawShapeAnimation.java @@ -10,7 +10,7 @@ import ch.njol.skript.util.Timespan; import ch.njol.skript.util.Timespan.TimePeriod; import ch.njol.util.Kleenean; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Shape; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.DynamicLocation; import org.bukkit.entity.Player; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java index 2a9e0d2..ef95726 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java @@ -4,7 +4,7 @@ import ch.njol.skript.classes.Parser; import ch.njol.skript.lang.ParseContext; import ch.njol.skript.registrations.Classes; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Shape; import org.jetbrains.annotations.Nullable; public class ShapeTypes { diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/particles/Particle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/particles/Particle.java index c460aff..3d16431 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/particles/Particle.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/particles/Particle.java @@ -1,8 +1,7 @@ package com.sovdee.skriptparticles.particles; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Shape; import com.sovdee.skriptparticles.shapes.DrawData; -import com.sovdee.skriptparticles.util.VectorConversion; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.entity.Player; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawData.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawData.java index 37cca49..cc70a35 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawData.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawData.java @@ -1,6 +1,7 @@ package com.sovdee.skriptparticles.shapes; import com.sovdee.shapes.DrawContext; +import com.sovdee.shapes.shapes.Shape; import com.sovdee.skriptparticles.particles.Particle; import com.sovdee.skriptparticles.util.DynamicLocation; import com.sovdee.skriptparticles.util.Quaternion; @@ -28,7 +29,7 @@ public DrawData() { /** * Gets the DrawData attached to a shape, creating and attaching one if missing. */ - public static DrawData of(com.sovdee.shapes.Shape shape) { + public static DrawData of(Shape shape) { DrawContext ctx = shape.getDrawContext(); if (ctx instanceof DrawData dd) return dd; DrawData dd = new DrawData(); diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawManager.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawManager.java index 56e26ef..a351827 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawManager.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawManager.java @@ -1,7 +1,7 @@ package com.sovdee.skriptparticles.shapes; import ch.njol.skript.Skript; -import com.sovdee.shapes.Shape; +import com.sovdee.shapes.shapes.Shape; import com.sovdee.skriptparticles.particles.Particle; import com.sovdee.skriptparticles.particles.ParticleGradient; import com.sovdee.skriptparticles.util.DynamicLocation; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/util/MathUtil.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/util/MathUtil.java index 8b7cbb7..c92e297 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/util/MathUtil.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/util/MathUtil.java @@ -8,95 +8,12 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; -import java.util.stream.Collectors; public class MathUtil { - public static final double PHI = Math.PI * (3.0 - Math.sqrt(5.0)); - public static final double PHI_RECIPROCAL = 1.0 / PHI; - public static final double PHI_SQUARED = PHI * PHI; - public static final double[] SPHERE_THETA_COS = new double[4096]; - public static final double[] SPHERE_THETA_SIN = new double[4096]; public static final double EPSILON = 0.0001; - static { - for (int i = 0; i < SPHERE_THETA_COS.length; i++) { - SPHERE_THETA_COS[i] = Math.cos(MathUtil.PHI * i); - SPHERE_THETA_SIN[i] = Math.sin(MathUtil.PHI * i); - } - } - public static double clamp(double value, double min, double max) { - return Math.max(min, Math.min(max, value)); - } - - public static Set calculateFibonacciSphere(int pointCount, double radius) { - return calculateFibonacciSphere(pointCount, radius, Math.PI); - } - - public static Set calculateFibonacciSphere(int pointCount, double radius, double angleCutoff) { - Set points = new LinkedHashSet<>(); - double y = 1; - if (angleCutoff > Math.PI) angleCutoff = Math.PI; - double yLimit = Math.cos(angleCutoff); - - double yStep = 2.0 / pointCount; - int preCompPoints = Math.min(pointCount, MathUtil.SPHERE_THETA_COS.length); - // Use precomputed points if possible - for (int i = 0; i < preCompPoints; i++) { - double r = Math.sqrt(1 - y * y) * radius; - points.add(new Vector(r * MathUtil.SPHERE_THETA_COS[i], y * radius, r * MathUtil.SPHERE_THETA_SIN[i])); - y -= yStep; - if (y <= yLimit) { - return points; - } - } - // If we have more points than we precomputed, we need to calculate the rest - if (pointCount > preCompPoints) { - for (int i = preCompPoints; i < pointCount; i++) { - double r = Math.sqrt(1 - y * y) * radius; - double theta = MathUtil.PHI * i; - points.add(new Vector(r * Math.cos(theta), y * radius, r * Math.sin(theta))); - y -= yStep; - if (y <= yLimit) { - return points; - } - } - } - return points; - } - - public static Set calculateCircle(double radius, double particleDensity, double cutoffAngle) { - Set points = new LinkedHashSet<>(); - double stepSize = particleDensity / radius; - for (double theta = 0; theta < cutoffAngle; theta += stepSize) { - points.add(new Vector(Math.cos(theta) * radius, 0, Math.sin(theta) * radius)); - } - return points; - } - - public static Set calculateDisc(double radius, double particleDensity, double cutoffAngle) { - Set points = new LinkedHashSet<>(); - for (double subRadius = particleDensity; subRadius < radius; subRadius += particleDensity) { - points.addAll(calculateCircle(subRadius, particleDensity, cutoffAngle)); - } - points.addAll(calculateCircle(radius, particleDensity, cutoffAngle)); - return points; - } - - public static Set calculateHelix(double radius, double height, double slope, int direction, double particleDensity) { - Set points = new LinkedHashSet<>(); - if (radius <= 0 || height <= 0) { - return points; - } - double loops = Math.abs(height / slope); - double length = slope * slope + radius * radius; - double stepSize = particleDensity / length; - for (double t = 0; t < loops; t += stepSize) { - double x = radius * Math.cos(direction * t); - double z = radius * Math.sin(direction * t); - points.add(new Vector(x, t * slope, z)); - } - return points; + return Math.clamp(value, min, max); } public static Set calculateLine(Vector start, Vector end, double particleDensity) { @@ -112,143 +29,6 @@ public static Set calculateLine(Vector start, Vector end, double particl return points; } - public static Set calculateRegularPolygon(double radius, double angle, double particleDensity, boolean wireframe) { - angle = Math.max(angle, MathUtil.EPSILON); - - Set points = new LinkedHashSet<>(); - double apothem = radius * Math.cos(angle / 2); - double radiusStep = radius / Math.round(apothem / particleDensity); - if (wireframe) { - radiusStep = 2 * radius; - } else { - points.add(new Vector(0, 0, 0)); - } - for (double subRadius = radius; subRadius >= 0; subRadius -= radiusStep) { - Vector vertex = new Vector(subRadius, 0, 0); - for (double i = 0; i < 2 * Math.PI; i += angle) { - points.addAll(calculateLine(vertex.clone().rotateAroundY(i), vertex.clone().rotateAroundY(i + angle), particleDensity)); - } - } - return points; - } - - public static Set calculateRegularPrism(double radius, double angle, double height, double particleDensity, boolean wireframe) { - Set points = new LinkedHashSet<>(); - Vector vertex = new Vector(radius, 0, 0); - for (double i = 0; i < 2 * Math.PI; i += angle) { - Vector currentVertex = vertex.clone().rotateAroundY(i); - for (Vector vector : calculateLine(currentVertex, vertex.clone().rotateAroundY(i + angle), particleDensity)) { - points.add(vector); - if (wireframe) { - points.add(vector.clone().setY(height)); - } else { - points.addAll(calculateLine(vector, vector.clone().setY(height), particleDensity)); - } - } - if (wireframe) - points.addAll(calculateLine(currentVertex, currentVertex.clone().setY(height), particleDensity)); - } - return points; - } - - public static Set connectPoints(List points, double particleDensity) { - Set connectedPoints = new LinkedHashSet<>(); - for (int i = 0; i < points.size() - 1; i++) { - connectedPoints.addAll(calculateLine(points.get(i), points.get(i + 1), particleDensity)); - } - return connectedPoints; - } - - private static double ellipseCircumference(double r1, double r2) { - double a = Math.max(r1, r2); - double b = Math.min(r1, r2); - double h = Math.pow(a - b, 2) / Math.pow(a + b, 2); - return Math.PI * (a + b) * (1 + 3 * h / (10 + Math.sqrt(4 - 3 * h))); - } - - public static List calculateEllipse(double r1, double r2, double particleDensity, double cutoffAngle) { - List points = new ArrayList<>(); - double circumference = ellipseCircumference(r1, r2); - - int steps = (int) Math.round(circumference / particleDensity); - double theta = 0; - double angleStep = 0; - for (int i = 0; i < steps; i++) { - if (theta > cutoffAngle) { - break; - } - points.add(new Vector(r1 * Math.cos(theta), 0, r2 * Math.sin(theta))); - double dx = r1 * Math.sin(theta + 0.5 * angleStep); - double dy = r2 * Math.cos(theta + 0.5 * angleStep); - angleStep = particleDensity / Math.sqrt(dx * dx + dy * dy); - theta += angleStep; - } - return points; - } - - public static Set calculateEllipticalDisc(double r1, double r2, double particleDensity, double cutoffAngle) { - Set points = new LinkedHashSet<>(); - int steps = (int) Math.round(Math.max(r1, r2) / particleDensity); - double r; - for (double i = 1; i <= steps; i += 1) { - r = i / steps; - points.addAll(calculateEllipse(r1 * r, r2 * r, particleDensity, cutoffAngle)); - } - return points; - } - - public static Set calculateCylinder(double r1, double height, double particleDensity, double cutoffAngle) { - Set points = calculateDisc(r1, particleDensity, cutoffAngle); - points.addAll(points.stream().map(v -> v.clone().setY(height)).collect(Collectors.toSet())); - // wall - Set wall = calculateCircle(r1, particleDensity, cutoffAngle); - points.addAll(fillVertically(wall, height, particleDensity)); - return points; - } - - public static Set calculateCylinder(double r1, double r2, double height, double particleDensity, double cutoffAngle) { - Set points = calculateEllipticalDisc(r1, r2, particleDensity, cutoffAngle); - points.addAll(points.stream().map(v -> v.clone().setY(height)).collect(Collectors.toSet())); - // wall - Set wall = new LinkedHashSet<>(calculateEllipse(r1, r2, particleDensity, cutoffAngle)); - points.addAll(fillVertically(wall, height, particleDensity)); - return points; - } - - public static Set fillVertically(Set vectors, double height, double particleDensity) { - Set points = new LinkedHashSet<>(vectors); - double heightStep = height / Math.round(height / particleDensity); - for (double i = 0; i < height; i += heightStep) { - for (Vector vector : vectors) { - points.add(vector.clone().setY(i)); - } - } - return points; - } - - public static Set calculateHeart(double length, double width, double eccentricity, double particleDensity) { - Set points = new LinkedHashSet<>(); - double angleStep = 4 / 3.0 * particleDensity / (width + length); - for (double theta = 0; theta < Math.PI * 2; theta += angleStep) { - double x = width * Math.pow(Math.sin(theta), 3); - double y = length * (Math.cos(theta) - 1 / eccentricity * Math.cos(2 * theta) - 1.0 / 6 * Math.cos(3 * theta) - 1.0 / 16 * Math.cos(4 * theta)); - points.add(new Vector(x, 0, y)); - } - return points; - } - - public static Set calculateStar(double innerRadius, double outerRadius, double angle, double particleDensity) { - Set points = new LinkedHashSet<>(); - Vector outerVertex = new Vector(outerRadius, 0, 0); - Vector innerVertex = new Vector(innerRadius, 0, 0); - for (double theta = 0; theta < 2 * Math.PI; theta += angle) { - Vector currentVertex = outerVertex.clone().rotateAroundY(theta); - points.addAll(calculateLine(currentVertex, innerVertex.clone().rotateAroundY(theta + angle / 2), particleDensity)); - points.addAll(calculateLine(currentVertex, innerVertex.clone().rotateAroundY(theta - angle / 2), particleDensity)); - } - return points; - } - public static List> batch(Collection toDraw, double millisecondsPerPoint) { List> batches = new ArrayList<>(); double totalDuration = 0; From 245bc52f373f4b149e5c20a2f6b35b0eefd233c8 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Sun, 8 Feb 2026 01:17:55 -0800 Subject: [PATCH 5/6] Separate shape point sampling/drawing mechanisms from the actual geometry logic --- .../shapes/sampling/DefaultPointSampler.java | 123 ++++++++ .../shapes/{ => sampling}/DrawContext.java | 2 +- .../sovdee/shapes/sampling/PointSampler.java | 49 ++++ .../sovdee/shapes/sampling/SamplingStyle.java | 15 + .../sovdee/shapes/shapes/AbstractShape.java | 228 +++++---------- .../java/com/sovdee/shapes/shapes/Arc.java | 14 +- .../com/sovdee/shapes/shapes/BezierCurve.java | 45 +-- .../java/com/sovdee/shapes/shapes/Circle.java | 79 ++--- .../java/com/sovdee/shapes/shapes/Cuboid.java | 73 ++--- .../com/sovdee/shapes/shapes/Ellipse.java | 68 +++-- .../com/sovdee/shapes/shapes/Ellipsoid.java | 64 +++-- .../sovdee/shapes/shapes/EllipticalArc.java | 16 +- .../java/com/sovdee/shapes/shapes/Heart.java | 42 ++- .../java/com/sovdee/shapes/shapes/Helix.java | 49 ++-- .../shapes/shapes/IrregularPolygon.java | 37 ++- .../java/com/sovdee/shapes/shapes/Line.java | 40 ++- .../com/sovdee/shapes/shapes/Rectangle.java | 64 +++-- .../sovdee/shapes/shapes/RegularPolygon.java | 73 ++--- .../shapes/shapes/RegularPolyhedron.java | 96 ++++--- .../java/com/sovdee/shapes/shapes/Shape.java | 269 ++---------------- .../java/com/sovdee/shapes/shapes/Sphere.java | 42 +-- .../sovdee/shapes/shapes/SphericalCap.java | 12 +- .../java/com/sovdee/shapes/shapes/Star.java | 38 ++- .../elements/effects/EffSetOrdering.java | 2 +- .../expressions/constructors/ExprArc.java | 6 +- .../constructors/ExprBezierCurve.java | 2 +- .../expressions/constructors/ExprCircle.java | 16 +- .../expressions/constructors/ExprCuboid.java | 14 +- .../expressions/constructors/ExprEllipse.java | 18 +- .../constructors/ExprEllipsoid.java | 10 +- .../constructors/ExprEllipticalArc.java | 12 +- .../expressions/constructors/ExprHeart.java | 6 +- .../expressions/constructors/ExprHelix.java | 10 +- .../constructors/ExprIrregularPolygon.java | 2 +- .../expressions/constructors/ExprLine.java | 2 +- .../constructors/ExprRectangle.java | 12 +- .../constructors/ExprRegularPolygon.java | 12 +- .../constructors/ExprRegularPolyhedron.java | 10 +- .../expressions/constructors/ExprSphere.java | 6 +- .../constructors/ExprSphericalCap.java | 6 +- .../expressions/constructors/ExprStar.java | 6 +- .../properties/ExprShapeLocations.java | 2 +- .../properties/ExprShapeNormal.java | 2 +- .../properties/ExprShapeParticleDensity.java | 20 +- .../properties/ExprShapePoints.java | 2 +- .../properties/ExprShapeRelativeAxis.java | 6 +- .../properties/ExprShapeStyle.java | 19 +- .../elements/types/ShapeTypes.java | 19 +- .../skriptparticles/shapes/DrawData.java | 8 +- .../skriptparticles/shapes/DrawManager.java | 2 +- 50 files changed, 916 insertions(+), 854 deletions(-) create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/sampling/DefaultPointSampler.java rename shapes-lib/src/main/java/com/sovdee/shapes/{ => sampling}/DrawContext.java (80%) create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/sampling/PointSampler.java create mode 100644 shapes-lib/src/main/java/com/sovdee/shapes/sampling/SamplingStyle.java diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/sampling/DefaultPointSampler.java b/shapes-lib/src/main/java/com/sovdee/shapes/sampling/DefaultPointSampler.java new file mode 100644 index 0000000..cd76bac --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/sampling/DefaultPointSampler.java @@ -0,0 +1,123 @@ +package com.sovdee.shapes.sampling; + +import com.sovdee.shapes.shapes.Shape; +import org.joml.Quaterniond; +import org.joml.Vector3d; + +import java.util.Comparator; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.TreeSet; +import java.util.UUID; + +/** + * Default implementation of {@link PointSampler} with hash-based caching. + */ +public class DefaultPointSampler implements PointSampler { + + private SamplingStyle style = SamplingStyle.OUTLINE; + private double density = 0.25; + private Comparator ordering; + private final UUID uuid; + private DrawContext drawContext; + + // Cache + private Set cachedPoints = new LinkedHashSet<>(); + private CacheState lastState; + private boolean needsUpdate = false; + + public DefaultPointSampler() { + this.uuid = UUID.randomUUID(); + this.lastState = new CacheState(style, 0, 1.0, 0, density, 0); + } + + @Override + public Set getPoints(Shape shape) { + return getPoints(shape, shape.getOrientation()); + } + + @Override + public Set getPoints(Shape shape, Quaterniond orientation) { + CacheState state = new CacheState(style, orientation.hashCode(), + shape.getScale(), shape.getOffset().hashCode(), density, shape.getVersion()); + if (shape.isDynamic() || needsUpdate || !state.equals(lastState) || cachedPoints.isEmpty()) { + Set points = (ordering != null) ? new TreeSet<>(ordering) : new LinkedHashSet<>(); + + shape.beforeSampling(density); + switch (style) { + case OUTLINE -> shape.generateOutline(points, density); + case SURFACE -> shape.generateSurface(points, density); + case FILL -> shape.generateFilled(points, density); + } + shape.afterSampling(points); + + for (Vector3d point : points) { + orientation.transform(point); + point.mul(shape.getScale()); + point.add(shape.getOffset()); + } + cachedPoints = points; + lastState = state; + needsUpdate = false; + } + return cachedPoints; + } + + public void markDirty() { + needsUpdate = true; + } + + @Override + public SamplingStyle getStyle() { return style; } + + @Override + public void setStyle(SamplingStyle style) { + this.style = style; + this.needsUpdate = true; + } + + @Override + public double getDensity() { return density; } + + @Override + public void setDensity(double density) { + this.density = Math.max(density, Shape.EPSILON); + this.needsUpdate = true; + } + + @Override + public Comparator getOrdering() { return ordering; } + + @Override + public void setOrdering(Comparator ordering) { + this.ordering = ordering; + this.needsUpdate = true; + } + + @Override + public UUID getUUID() { return uuid; } + + @Override + public DrawContext getDrawContext() { return drawContext; } + + @Override + public void setDrawContext(DrawContext context) { this.drawContext = context; } + + @Override + public DefaultPointSampler clone() { + try { + DefaultPointSampler copy = (DefaultPointSampler) super.clone(); + // Don't share the cache + copy.cachedPoints = new LinkedHashSet<>(); + copy.needsUpdate = true; + if (drawContext != null) + copy.drawContext = drawContext.copy(); + return copy; + } catch (CloneNotSupportedException e) { + throw new AssertionError(e); + } + } + + private record CacheState(SamplingStyle style, int orientationHash, double scale, + int offsetHash, double density, long shapeVersion) {} +} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/DrawContext.java b/shapes-lib/src/main/java/com/sovdee/shapes/sampling/DrawContext.java similarity index 80% rename from shapes-lib/src/main/java/com/sovdee/shapes/DrawContext.java rename to shapes-lib/src/main/java/com/sovdee/shapes/sampling/DrawContext.java index 8321e3b..19427f0 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/DrawContext.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/sampling/DrawContext.java @@ -1,4 +1,4 @@ -package com.sovdee.shapes; +package com.sovdee.shapes.sampling; /** * Client-provided rendering metadata. Implementations are opaque to the library. diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/sampling/PointSampler.java b/shapes-lib/src/main/java/com/sovdee/shapes/sampling/PointSampler.java new file mode 100644 index 0000000..1d7221c --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/sampling/PointSampler.java @@ -0,0 +1,49 @@ +package com.sovdee.shapes.sampling; + +import com.sovdee.shapes.shapes.Shape; +import org.joml.Quaterniond; +import org.joml.Vector3d; + +import java.util.Comparator; +import java.util.Set; +import java.util.UUID; + +/** + * Responsible for sampling points from a {@link Shape}'s geometry. + * Manages sampling configuration (style, density, ordering) and caching. + */ +public interface PointSampler extends Cloneable { + + SamplingStyle getStyle(); + void setStyle(SamplingStyle style); + + double getDensity(); + void setDensity(double density); + + Comparator getOrdering(); + void setOrdering(Comparator ordering); + + UUID getUUID(); + + DrawContext getDrawContext(); + void setDrawContext(DrawContext context); + + /** + * Samples points from the given shape using the shape's own orientation. + */ + Set getPoints(Shape shape); + + /** + * Samples points from the given shape using the given orientation. + */ + Set getPoints(Shape shape, Quaterniond orientation); + + /** + * Computes and sets the density to achieve approximately the given particle count. + */ + default void setParticleCount(Shape shape, int count) { + setDensity(shape.computeDensity(getStyle(), count)); + } + + PointSampler clone(); +} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/sampling/SamplingStyle.java b/shapes-lib/src/main/java/com/sovdee/shapes/sampling/SamplingStyle.java new file mode 100644 index 0000000..d65ef9b --- /dev/null +++ b/shapes-lib/src/main/java/com/sovdee/shapes/sampling/SamplingStyle.java @@ -0,0 +1,15 @@ +package com.sovdee.shapes.sampling; + +/** + * Determines how a shape's geometry is sampled into points. + */ +public enum SamplingStyle { + OUTLINE, + SURFACE, + FILL; + + @Override + public String toString() { + return name().toLowerCase(); + } +} diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/AbstractShape.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/AbstractShape.java index 2af6071..1ba1478 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/AbstractShape.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/AbstractShape.java @@ -1,113 +1,35 @@ package com.sovdee.shapes.shapes; -import com.sovdee.shapes.DrawContext; +import com.sovdee.shapes.sampling.DefaultPointSampler; +import com.sovdee.shapes.sampling.PointSampler; +import com.sovdee.shapes.sampling.SamplingStyle; import org.joml.Quaterniond; import org.joml.Vector3d; -import java.util.Comparator; import java.util.LinkedHashSet; import java.util.Set; -import java.util.TreeSet; -import java.util.UUID; +/** + * Base implementation of {@link Shape} providing spatial transform, versioning, + * and a default {@link PointSampler}. + */ public abstract class AbstractShape implements Shape { - private final UUID uuid; - private Set points; - - private Style style; private final Quaterniond orientation; - private final Quaterniond lastOrientation; private double scale; private Vector3d offset; - private Comparator ordering; - private double particleDensity = 0.25; - - private DrawContext drawContext; private boolean dynamic = false; - - private State lastState; - private boolean needsUpdate = false; + private long version = 0; + private PointSampler pointSampler; public AbstractShape() { - this.style = Style.OUTLINE; - this.points = new LinkedHashSet<>(); - this.orientation = new Quaterniond(); - this.lastOrientation = new Quaterniond(); this.scale = 1; this.offset = new Vector3d(0, 0, 0); - - this.uuid = UUID.randomUUID(); - - this.lastState = getState(); - } - - @Override - public Set getPoints() { - return getPoints(this.orientation); - } - - @Override - public Set getPoints(Quaterniond orientation) { - State state = getState(orientation); - if (dynamic || needsUpdate || !lastState.equals(state) || points.isEmpty()) { - if (ordering != null) - points = new TreeSet<>(ordering); - else - points = new LinkedHashSet<>(); - generatePoints(points); - for (Vector3d point : points) { - orientation.transform(point); - point.mul(scale); - point.add(offset); - } - lastState = state; - needsUpdate = false; - } - return points; - } - - @Override - public void setPoints(Set points) { - this.points = points; - } - - @Override - public void generateSurface(Set points) { - generateOutline(points); - } - - @Override - public void generateFilled(Set points) { - generateSurface(points); - } - - @Override - public Vector3d getRelativeXAxis(boolean useLastOrientation) { - return (useLastOrientation ? lastOrientation : orientation).transform(new Vector3d(1, 0, 0)); - } - - @Override - public Vector3d getRelativeYAxis(boolean useLastOrientation) { - return (useLastOrientation ? lastOrientation : orientation).transform(new Vector3d(0, 1, 0)); - } - - @Override - public Vector3d getRelativeZAxis(boolean useLastOrientation) { - return (useLastOrientation ? lastOrientation : orientation).transform(new Vector3d(0, 0, 1)); - } - - @Override - public Style getStyle() { - return style; + this.pointSampler = new DefaultPointSampler(); } - @Override - public void setStyle(Style style) { - this.style = style; - this.setNeedsUpdate(true); - } + // --- Spatial transform --- @Override public Quaterniond getOrientation() { @@ -117,26 +39,14 @@ public Quaterniond getOrientation() { @Override public void setOrientation(Quaterniond orientation) { this.orientation.set(orientation); - this.setNeedsUpdate(true); - } - - public Quaterniond getLastOrientation() { - return lastOrientation; - } - - public void setLastOrientation(Quaterniond orientation) { - this.lastOrientation.set(orientation); } @Override - public double getScale() { - return scale; - } + public double getScale() { return scale; } @Override public void setScale(double scale) { this.scale = scale; - this.setNeedsUpdate(true); } @Override @@ -147,78 +57,82 @@ public Vector3d getOffset() { @Override public void setOffset(Vector3d offset) { this.offset = offset; - this.setNeedsUpdate(true); } + // --- Oriented axes --- + @Override - public UUID getUUID() { - return uuid; + public Vector3d getRelativeXAxis() { + return orientation.transform(new Vector3d(1, 0, 0)); } @Override - public Comparator getOrdering() { - return ordering; + public Vector3d getRelativeYAxis() { + return orientation.transform(new Vector3d(0, 1, 0)); } @Override - public void setOrdering(Comparator comparator) { - ordering = comparator; - this.setNeedsUpdate(true); + public Vector3d getRelativeZAxis() { + return orientation.transform(new Vector3d(0, 0, 1)); } + // --- Change detection --- + @Override - public double getParticleDensity() { - return particleDensity; + public long getVersion() { return version; } + + /** + * Increments the version counter, signaling that geometry has changed. + * Call from dimension setters. + */ + protected void invalidate() { + version++; } + // --- Dynamic support --- + @Override - public void setParticleDensity(double particleDensity) { - this.particleDensity = Math.max(particleDensity, Shape.EPSILON); - this.setNeedsUpdate(true); - } + public boolean isDynamic() { return dynamic; } @Override - public int getParticleCount() { - return getPoints().size(); - } + public void setDynamic(boolean dynamic) { this.dynamic = dynamic; } + + // --- Point generation defaults --- @Override - public boolean needsUpdate() { - return needsUpdate; + public void generateSurface(Set points, double density) { + generateOutline(points, density); } @Override - public void setNeedsUpdate(boolean needsUpdate) { - this.needsUpdate = needsUpdate; + public void generateFilled(Set points, double density) { + generateSurface(points, density); } @Override - public DrawContext getDrawContext() { - return drawContext; + public double computeDensity(SamplingStyle style, int targetPointCount) { + // Generic fallback — subclasses should override for accuracy + return 0.25; } + // --- Geometry query --- + @Override - public void setDrawContext(DrawContext drawContext) { - this.drawContext = drawContext; - } + public abstract boolean contains(Vector3d point); + + // --- PointSampler --- @Override - public boolean isDynamic() { - return dynamic; - } + public PointSampler getPointSampler() { return pointSampler; } @Override - public void setDynamic(boolean dynamic) { - this.dynamic = dynamic; - } + public void setPointSampler(PointSampler sampler) { this.pointSampler = sampler; } - /** - * Adds vertically extruded copies of the given points up to the given height. - * Mutates the set in-place by adding new points at each height step. - */ - protected static void fillVertically(Set points, double height, double particleDensity) { + // --- Vertical fill helper --- + + protected static void fillVertically(Set points, double height, double density) { Set base = new LinkedHashSet<>(points); - double heightStep = height / Math.round(height / particleDensity); + double heightStep = height / Math.round(height / density); for (double y = 0; y < height; y += heightStep) { for (Vector3d v : base) { points.add(new Vector3d(v.x, y, v.z)); @@ -226,6 +140,9 @@ protected static void fillVertically(Set points, double height, double } } + // --- Replication --- + + @Override public abstract Shape clone(); @Override @@ -233,30 +150,17 @@ public Shape copyTo(Shape shape) { shape.setOrientation(new Quaterniond(this.orientation)); shape.setScale(this.scale); shape.setOffset(new Vector3d(this.offset)); - shape.setParticleDensity(this.particleDensity); - shape.setStyle(this.style); - shape.setOrdering(this.ordering); - shape.setPoints(this.getPoints()); - shape.setNeedsUpdate(this.needsUpdate); - shape.setLastState(this.lastState); shape.setDynamic(this.dynamic); - if (this.drawContext != null) - shape.setDrawContext(this.drawContext.copy()); - return shape; - } - - @Override - public State getState() { - return new State(style, orientation.hashCode(), scale, offset.hashCode(), particleDensity); - } - @Override - public State getState(Quaterniond orientation) { - return new State(style, orientation.hashCode(), scale, offset.hashCode(), particleDensity); - } + // Clone sampler config + PointSampler srcSampler = this.pointSampler; + PointSampler destSampler = shape.getPointSampler(); + destSampler.setStyle(srcSampler.getStyle()); + destSampler.setDensity(srcSampler.getDensity()); + destSampler.setOrdering(srcSampler.getOrdering()); + if (srcSampler.getDrawContext() != null) + destSampler.setDrawContext(srcSampler.getDrawContext().copy()); - @Override - public void setLastState(State state) { - this.lastState = state; + return shape; } } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Arc.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Arc.java index f04a775..a4a8d7e 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Arc.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Arc.java @@ -17,8 +17,8 @@ public Arc(double radius, double height, double cutoffAngle) { } @Override - public void generateSurface(Set points) { - generateFilled(points); + public void generateSurface(Set points, double density) { + generateFilled(points, density); } @Override @@ -29,7 +29,15 @@ public double getCutoffAngle() { @Override public void setCutoffAngle(double cutoffAngle) { this.cutoffAngle = Math.clamp(cutoffAngle, 0, Math.PI * 2); - this.setNeedsUpdate(true); + invalidate(); + } + + @Override + public boolean contains(Vector3d point) { + if (!super.contains(point)) return false; + double angle = Math.atan2(point.z, point.x); + if (angle < 0) angle += 2 * Math.PI; + return angle <= cutoffAngle; } @Override diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/BezierCurve.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/BezierCurve.java index 8f7e676..734f01a 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/BezierCurve.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/BezierCurve.java @@ -1,5 +1,6 @@ package com.sovdee.shapes.shapes; +import com.sovdee.shapes.sampling.SamplingStyle; import org.joml.Vector3d; import java.util.ArrayList; @@ -16,12 +17,6 @@ public class BezierCurve extends AbstractShape { private List controlPoints; private Supplier> controlPointsSupplier; - /** - * Creates a bezier curve from the given control points. - * Must have at least 2 control points (start and end). - * - * @param controlPoints the control points, including start and end - */ public BezierCurve(List controlPoints) { super(); if (controlPoints.size() < 2) @@ -51,17 +46,16 @@ public BezierCurve(BezierCurve curve) { } @Override - public void generateOutline(Set points) { + public void generateOutline(Set points, double density) { if (controlPointsSupplier != null) { List pts = controlPointsSupplier.get(); this.controlPoints = new ArrayList<>(); for (Vector3d cp : pts) this.controlPoints.add(new Vector3d(cp)); } - int steps = (int) (estimateLength() / getParticleDensity()); + int steps = (int) (estimateLength() / density); int n = controlPoints.size(); - // Pre-allocate temp array for de Casteljau — reused each step Vector3d[] temp = new Vector3d[n]; for (int i = 0; i < n; i++) temp[i] = new Vector3d(); @@ -69,10 +63,8 @@ public void generateOutline(Set points) { for (int step = 0; step < steps; step++) { double t = (double) step / steps; double nt = 1 - t; - // Copy control points into temp for (int i = 0; i < n; i++) temp[i].set(controlPoints.get(i)); - // Reduce in-place for (int level = n - 1; level > 0; level--) { for (int i = 0; i < level; i++) { temp[i].mul(nt).add(new Vector3d(temp[i + 1]).mul(t)); @@ -91,10 +83,31 @@ private double estimateLength() { } @Override - public void setParticleCount(int particleCount) { - particleCount = Math.max(particleCount, 1); - this.setParticleDensity(estimateLength() / particleCount); - this.setNeedsUpdate(true); + public double computeDensity(SamplingStyle style, int targetPointCount) { + int count = Math.max(targetPointCount, 1); + return estimateLength() / count; + } + + @Override + public boolean contains(Vector3d point) { + // Approximate: check distance to nearest sampled point + int samples = Math.max((int) (estimateLength() / 0.1), 10); + int n = controlPoints.size(); + Vector3d[] temp = new Vector3d[n]; + for (int i = 0; i < n; i++) temp[i] = new Vector3d(); + + for (int step = 0; step <= samples; step++) { + double t = (double) step / samples; + double nt = 1 - t; + for (int i = 0; i < n; i++) temp[i].set(controlPoints.get(i)); + for (int level = n - 1; level > 0; level--) { + for (int i = 0; i < level; i++) { + temp[i].mul(nt).add(new Vector3d(temp[i + 1]).mul(t)); + } + } + if (point.distance(temp[0]) <= EPSILON) return true; + } + return false; } public List getControlPoints() { @@ -105,7 +118,7 @@ public void setControlPoints(List controlPoints) { this.controlPoints = new ArrayList<>(); for (Vector3d cp : controlPoints) this.controlPoints.add(new Vector3d(cp)); - this.setNeedsUpdate(true); + invalidate(); } public Supplier> getControlPointsSupplier() { diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Circle.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Circle.java index 2cf94fd..5e7b510 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Circle.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Circle.java @@ -1,5 +1,6 @@ package com.sovdee.shapes.shapes; +import com.sovdee.shapes.sampling.SamplingStyle; import org.joml.Vector3d; import java.util.LinkedHashSet; @@ -24,26 +25,26 @@ public Circle(double radius, double height) { // --- Static calculation methods --- - public static Set calculateCircle(double radius, double particleDensity, double cutoffAngle) { + public static Set calculateCircle(double radius, double density, double cutoffAngle) { Set points = new LinkedHashSet<>(); - double stepSize = particleDensity / radius; + double stepSize = density / radius; for (double theta = 0; theta < cutoffAngle; theta += stepSize) { points.add(new Vector3d(Math.cos(theta) * radius, 0, Math.sin(theta) * radius)); } return points; } - public static Set calculateDisc(double radius, double particleDensity, double cutoffAngle) { + public static Set calculateDisc(double radius, double density, double cutoffAngle) { Set points = new LinkedHashSet<>(); - for (double subRadius = particleDensity; subRadius < radius; subRadius += particleDensity) { - points.addAll(calculateCircle(subRadius, particleDensity, cutoffAngle)); + for (double subRadius = density; subRadius < radius; subRadius += density) { + points.addAll(calculateCircle(subRadius, density, cutoffAngle)); } - points.addAll(calculateCircle(radius, particleDensity, cutoffAngle)); + points.addAll(calculateCircle(radius, density, cutoffAngle)); return points; } - public static Set calculateCylinder(double radius, double height, double particleDensity, double cutoffAngle) { - Set points = calculateDisc(radius, particleDensity, cutoffAngle); + public static Set calculateCylinder(double radius, double height, double density, double cutoffAngle) { + Set points = calculateDisc(radius, density, cutoffAngle); // Top disc via direct loop Set top = new LinkedHashSet<>(); for (Vector3d v : points) { @@ -51,8 +52,8 @@ public static Set calculateCylinder(double radius, double height, doub } points.addAll(top); // Wall - Set wall = calculateCircle(radius, particleDensity, cutoffAngle); - fillVertically(wall, height, particleDensity); + Set wall = calculateCircle(radius, density, cutoffAngle); + fillVertically(wall, height, density); points.addAll(wall); return points; } @@ -60,10 +61,10 @@ public static Set calculateCylinder(double radius, double height, doub // --- Generation methods --- @Override - public void generateOutline(Set points) { - Set circle = calculateCircle(radius, this.getParticleDensity(), cutoffAngle); + public void generateOutline(Set points, double density) { + Set circle = calculateCircle(radius, density, cutoffAngle); if (height != 0) { - fillVertically(circle, height, this.getParticleDensity()); + fillVertically(circle, height, density); points.addAll(circle); } else { points.addAll(circle); @@ -71,18 +72,18 @@ public void generateOutline(Set points) { } @Override - public void generateSurface(Set points) { + public void generateSurface(Set points, double density) { if (height != 0) - points.addAll(calculateCylinder(radius, height, this.getParticleDensity(), cutoffAngle)); + points.addAll(calculateCylinder(radius, height, density, cutoffAngle)); else - points.addAll(calculateDisc(radius, this.getParticleDensity(), cutoffAngle)); + points.addAll(calculateDisc(radius, density, cutoffAngle)); } @Override - public void generateFilled(Set points) { - Set disc = calculateDisc(radius, this.getParticleDensity(), cutoffAngle); + public void generateFilled(Set points, double density) { + Set disc = calculateDisc(radius, density, cutoffAngle); if (height != 0) { - fillVertically(disc, height, this.getParticleDensity()); + fillVertically(disc, height, density); points.addAll(disc); } else { points.addAll(disc); @@ -90,18 +91,30 @@ public void generateFilled(Set points) { } @Override - public void setParticleCount(int particleCount) { - particleCount = Math.max(particleCount, 1); - if (this.getStyle() == Style.OUTLINE && height == 0) { - this.setParticleDensity(cutoffAngle * radius / particleCount); - } else if (this.getStyle() == Style.SURFACE || height == 0) { - double discArea = cutoffAngle * 0.5 * radius * radius; - double wallArea = cutoffAngle * radius * height; - this.setParticleDensity(Math.sqrt((discArea + wallArea) / particleCount)); - } else { - this.setParticleDensity(Math.cbrt(cutoffAngle * 0.5 * radius * radius * height / particleCount)); - } - this.setNeedsUpdate(true); + public double computeDensity(SamplingStyle style, int targetPointCount) { + int count = Math.max(targetPointCount, 1); + return switch (style) { + case OUTLINE -> { + if (height == 0) yield cutoffAngle * radius / count; + double circumference = cutoffAngle * radius; + double wallArea = circumference * height; + yield Math.sqrt((circumference + wallArea) / count); + } + case SURFACE -> { + double discArea = cutoffAngle * 0.5 * radius * radius; + double wallArea = cutoffAngle * radius * height; + yield Math.sqrt((discArea + wallArea) / count); + } + case FILL -> Math.cbrt(cutoffAngle * 0.5 * radius * radius * height / count); + }; + } + + @Override + public boolean contains(Vector3d point) { + double distSq = point.x * point.x + point.z * point.z; + if (distSq > radius * radius) return false; + if (height > 0) return point.y >= 0 && point.y <= height; + return Math.abs(point.y) < EPSILON; } @Override @@ -110,7 +123,7 @@ public void setParticleCount(int particleCount) { @Override public void setRadius(double radius) { this.radius = Math.max(radius, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } @Override @@ -131,7 +144,7 @@ public void setWidth(double width) { } @Override public void setHeight(double height) { this.height = Math.max(height, 0); - this.setNeedsUpdate(true); + invalidate(); } @Override diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Cuboid.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Cuboid.java index 089ec9e..57b1e0c 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Cuboid.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Cuboid.java @@ -1,5 +1,6 @@ package com.sovdee.shapes.shapes; +import com.sovdee.shapes.sampling.SamplingStyle; import org.joml.Vector3d; import java.util.Set; @@ -22,7 +23,6 @@ public Cuboid(double length, double width, double height) { this.halfWidth = Math.max(width / 2, Shape.EPSILON); this.halfLength = Math.max(length / 2, Shape.EPSILON); this.halfHeight = Math.max(height / 2, Shape.EPSILON); - calculateSteps(); } public Cuboid(Vector3d cornerA, Vector3d cornerB) { @@ -33,7 +33,6 @@ public Cuboid(Vector3d cornerA, Vector3d cornerB) { this.halfWidth = Math.abs(cornerB.z - cornerA.z) / 2; this.halfHeight = Math.abs(cornerB.y - cornerA.y) / 2; centerOffset = new Vector3d(cornerB).add(cornerA).mul(0.5); - calculateSteps(); } public Cuboid(Supplier cornerA, Supplier cornerB) { @@ -45,18 +44,34 @@ public Cuboid(Supplier cornerA, Supplier cornerB) { this.halfLength = Math.max(Math.abs(b.x - a.x) / 2, Shape.EPSILON); this.halfWidth = Math.max(Math.abs(b.z - a.z) / 2, Shape.EPSILON); this.halfHeight = Math.max(Math.abs(b.y - a.y) / 2, Shape.EPSILON); - calculateSteps(); setDynamic(true); } - private void calculateSteps() { - widthStep = 2 * halfWidth / Math.round(2 * halfWidth / this.getParticleDensity()); - lengthStep = 2 * halfLength / Math.round(2 * halfLength / this.getParticleDensity()); - heightStep = 2 * halfHeight / Math.round(2 * halfHeight / this.getParticleDensity()); + private void calculateSteps(double density) { + widthStep = 2 * halfWidth / Math.round(2 * halfWidth / density); + lengthStep = 2 * halfLength / Math.round(2 * halfLength / density); + heightStep = 2 * halfHeight / Math.round(2 * halfHeight / density); } @Override - public void generateOutline(Set points) { + public void beforeSampling(double density) { + if (cornerASupplier != null && cornerBSupplier != null) { + Vector3d a = cornerASupplier.get(); + Vector3d b = cornerBSupplier.get(); + this.halfLength = Math.max(Math.abs(b.x - a.x) / 2, Shape.EPSILON); + this.halfWidth = Math.max(Math.abs(b.z - a.z) / 2, Shape.EPSILON); + this.halfHeight = Math.max(Math.abs(b.y - a.y) / 2, Shape.EPSILON); + } + calculateSteps(density); + } + + @Override + public void afterSampling(Set points) { + points.forEach(vector -> vector.add(centerOffset)); + } + + @Override + public void generateOutline(Set points, double density) { for (double x = -halfLength; x <= halfLength; x += lengthStep) { points.add(new Vector3d(x, -halfHeight, -halfWidth)); points.add(new Vector3d(x, -halfHeight, halfWidth)); @@ -78,7 +93,7 @@ public void generateOutline(Set points) { } @Override - public void generateSurface(Set points) { + public void generateSurface(Set points, double density) { for (double x = -halfLength; x <= halfLength; x += lengthStep) { for (double z = -halfWidth; z <= halfWidth; z += widthStep) { points.add(new Vector3d(x, -halfHeight, z)); @@ -100,7 +115,7 @@ public void generateSurface(Set points) { } @Override - public void generateFilled(Set points) { + public void generateFilled(Set points, double density) { for (double x = -halfLength; x <= halfLength; x += lengthStep) { for (double y = -halfHeight; y <= halfHeight; y += heightStep) { for (double z = -halfWidth; z <= halfWidth; z += widthStep) { @@ -111,30 +126,20 @@ public void generateFilled(Set points) { } @Override - public void generatePoints(Set points) { - if (cornerASupplier != null && cornerBSupplier != null) { - Vector3d a = cornerASupplier.get(); - Vector3d b = cornerBSupplier.get(); - this.halfLength = Math.max(Math.abs(b.x - a.x) / 2, Shape.EPSILON); - this.halfWidth = Math.max(Math.abs(b.z - a.z) / 2, Shape.EPSILON); - this.halfHeight = Math.max(Math.abs(b.y - a.y) / 2, Shape.EPSILON); - } - calculateSteps(); - super.generatePoints(points); - points.forEach(vector -> vector.add(centerOffset)); + public double computeDensity(SamplingStyle style, int targetPointCount) { + int count = Math.max(1, targetPointCount); + return switch (style) { + case OUTLINE -> 8 * (halfLength + halfHeight + halfWidth) / count; + case SURFACE -> Math.sqrt(8 * (halfLength * halfHeight + halfLength * halfWidth + halfHeight * halfWidth) / count); + case FILL -> Math.cbrt(8 * halfLength * halfHeight * halfWidth / count); + }; } @Override - public void setParticleCount(int particleCount) { - particleCount = Math.max(1, particleCount); - this.setParticleDensity(switch (this.getStyle()) { - case OUTLINE -> 8 * (halfLength + halfHeight + halfWidth) / particleCount; - case SURFACE -> - Math.sqrt(8 * (halfLength * halfHeight + halfLength * halfWidth + halfHeight * halfWidth) / particleCount); - case FILL -> Math.cbrt(8 * halfLength * halfHeight * halfWidth / particleCount); - }); - calculateSteps(); - this.setNeedsUpdate(true); + public boolean contains(Vector3d point) { + return Math.abs(point.x) <= halfLength && + Math.abs(point.y) <= halfHeight && + Math.abs(point.z) <= halfWidth; } @Override @@ -143,7 +148,7 @@ public void setParticleCount(int particleCount) { @Override public void setLength(double length) { this.halfLength = Math.max(length / 2, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } @Override @@ -152,7 +157,7 @@ public void setLength(double length) { @Override public void setWidth(double width) { this.halfWidth = Math.max(width / 2, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } @Override @@ -161,7 +166,7 @@ public void setWidth(double width) { @Override public void setHeight(double height) { this.halfHeight = Math.max(height / 2, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } public Supplier getCornerASupplier() { return cornerASupplier; } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Ellipse.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Ellipse.java index 97943ef..eb99dad 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Ellipse.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Ellipse.java @@ -1,5 +1,6 @@ package com.sovdee.shapes.shapes; +import com.sovdee.shapes.sampling.SamplingStyle; import org.joml.Vector3d; import java.util.ArrayList; @@ -35,11 +36,11 @@ private static double ellipseCircumference(double r1, double r2) { return Math.PI * (a + b) * (1 + 3 * h / (10 + Math.sqrt(4 - 3 * h))); } - public static List calculateEllipse(double r1, double r2, double particleDensity, double cutoffAngle) { + public static List calculateEllipse(double r1, double r2, double density, double cutoffAngle) { List points = new ArrayList<>(); double circumference = ellipseCircumference(r1, r2); - int steps = (int) Math.round(circumference / particleDensity); + int steps = (int) Math.round(circumference / density); double theta = 0; double angleStep = 0; for (int i = 0; i < steps; i++) { @@ -49,25 +50,25 @@ public static List calculateEllipse(double r1, double r2, double parti points.add(new Vector3d(r1 * Math.cos(theta), 0, r2 * Math.sin(theta))); double dx = r1 * Math.sin(theta + 0.5 * angleStep); double dy = r2 * Math.cos(theta + 0.5 * angleStep); - angleStep = particleDensity / Math.sqrt(dx * dx + dy * dy); + angleStep = density / Math.sqrt(dx * dx + dy * dy); theta += angleStep; } return points; } - public static Set calculateEllipticalDisc(double r1, double r2, double particleDensity, double cutoffAngle) { + public static Set calculateEllipticalDisc(double r1, double r2, double density, double cutoffAngle) { Set points = new LinkedHashSet<>(); - int steps = (int) Math.round(Math.max(r1, r2) / particleDensity); + int steps = (int) Math.round(Math.max(r1, r2) / density); double r; for (double i = 1; i <= steps; i += 1) { r = i / steps; - points.addAll(calculateEllipse(r1 * r, r2 * r, particleDensity, cutoffAngle)); + points.addAll(calculateEllipse(r1 * r, r2 * r, density, cutoffAngle)); } return points; } - public static Set calculateCylinder(double r1, double r2, double height, double particleDensity, double cutoffAngle) { - Set points = calculateEllipticalDisc(r1, r2, particleDensity, cutoffAngle); + public static Set calculateCylinder(double r1, double r2, double height, double density, double cutoffAngle) { + Set points = calculateEllipticalDisc(r1, r2, density, cutoffAngle); // Top disc via direct loop Set top = new LinkedHashSet<>(); for (Vector3d v : points) { @@ -75,8 +76,8 @@ public static Set calculateCylinder(double r1, double r2, double heigh } points.addAll(top); // Wall - Set wall = new LinkedHashSet<>(calculateEllipse(r1, r2, particleDensity, cutoffAngle)); - fillVertically(wall, height, particleDensity); + Set wall = new LinkedHashSet<>(calculateEllipse(r1, r2, density, cutoffAngle)); + fillVertically(wall, height, density); points.addAll(wall); return points; } @@ -84,10 +85,10 @@ public static Set calculateCylinder(double r1, double r2, double heigh // --- Generation methods --- @Override - public void generateOutline(Set points) { - Set ellipse = new LinkedHashSet<>(calculateEllipse(xRadius, zRadius, this.getParticleDensity(), cutoffAngle)); + public void generateOutline(Set points, double density) { + Set ellipse = new LinkedHashSet<>(calculateEllipse(xRadius, zRadius, density, cutoffAngle)); if (height != 0) { - fillVertically(ellipse, height, this.getParticleDensity()); + fillVertically(ellipse, height, density); points.addAll(ellipse); } else { points.addAll(ellipse); @@ -95,18 +96,18 @@ public void generateOutline(Set points) { } @Override - public void generateSurface(Set points) { + public void generateSurface(Set points, double density) { if (height != 0) - points.addAll(calculateCylinder(xRadius, zRadius, height, this.getParticleDensity(), cutoffAngle)); + points.addAll(calculateCylinder(xRadius, zRadius, height, density, cutoffAngle)); else - points.addAll(calculateEllipticalDisc(xRadius, zRadius, this.getParticleDensity(), cutoffAngle)); + points.addAll(calculateEllipticalDisc(xRadius, zRadius, density, cutoffAngle)); } @Override - public void generateFilled(Set points) { - Set disc = calculateEllipticalDisc(xRadius, zRadius, this.getParticleDensity(), cutoffAngle); + public void generateFilled(Set points, double density) { + Set disc = calculateEllipticalDisc(xRadius, zRadius, density, cutoffAngle); if (height != 0) { - fillVertically(disc, height, this.getParticleDensity()); + fillVertically(disc, height, density); points.addAll(disc); } else { points.addAll(disc); @@ -114,16 +115,25 @@ public void generateFilled(Set points) { } @Override - public void setParticleCount(int particleCount) { - particleCount = Math.max(particleCount, 1); - switch (this.getStyle()) { + public double computeDensity(SamplingStyle style, int targetPointCount) { + int count = Math.max(targetPointCount, 1); + return switch (style) { case OUTLINE -> { double h = (xRadius - zRadius) * (xRadius - zRadius) / ((xRadius + zRadius) + (xRadius + zRadius)); - double circumferenceXY = Math.PI * (xRadius + zRadius) * (1 + (3 * h / (10 + Math.sqrt(4 - 3 * h)))); - this.setParticleDensity(circumferenceXY / particleCount); + double circumference = Math.PI * (xRadius + zRadius) * (1 + (3 * h / (10 + Math.sqrt(4 - 3 * h)))); + yield circumference / count; } - case SURFACE, FILL -> this.setParticleDensity(Math.sqrt((Math.PI * xRadius * zRadius) / particleCount)); - } + case SURFACE, FILL -> Math.sqrt((Math.PI * xRadius * zRadius) / count); + }; + } + + @Override + public boolean contains(Vector3d point) { + double nx = point.x / xRadius; + double nz = point.z / zRadius; + if (nx * nx + nz * nz > 1) return false; + if (height > 0) return point.y >= 0 && point.y <= height; + return Math.abs(point.y) < EPSILON; } @Override @@ -132,7 +142,7 @@ public void setParticleCount(int particleCount) { @Override public void setLength(double length) { xRadius = Math.max(length / 2, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } @Override @@ -141,7 +151,7 @@ public void setLength(double length) { @Override public void setWidth(double width) { zRadius = Math.max(width / 2, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } @Override @@ -150,7 +160,7 @@ public void setWidth(double width) { @Override public void setHeight(double height) { this.height = Math.max(height, 0); - this.setNeedsUpdate(true); + invalidate(); } @Override diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Ellipsoid.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Ellipsoid.java index d29183d..cab4100 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Ellipsoid.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Ellipsoid.java @@ -1,5 +1,6 @@ package com.sovdee.shapes.shapes; +import com.sovdee.shapes.sampling.SamplingStyle; import com.sovdee.shapes.util.VectorUtil; import org.joml.Quaterniond; import org.joml.Vector3d; @@ -24,60 +25,57 @@ public Ellipsoid(double xRadius, double yRadius, double zRadius) { } @Override - public void generateOutline(Set points) { - double particleDensity = this.getParticleDensity(); - points.addAll(Ellipse.calculateEllipse(xRadius, zRadius, particleDensity, 2 * Math.PI)); - points.addAll(VectorUtil.transform(XY_ROTATION, Ellipse.calculateEllipse(xRadius, yRadius, particleDensity, 2 * Math.PI))); - points.addAll(VectorUtil.transform(ZY_ROTATION, Ellipse.calculateEllipse(yRadius, zRadius, particleDensity, 2 * Math.PI))); + public void generateOutline(Set points, double density) { + points.addAll(Ellipse.calculateEllipse(xRadius, zRadius, density, 2 * Math.PI)); + points.addAll(VectorUtil.transform(XY_ROTATION, Ellipse.calculateEllipse(xRadius, yRadius, density, 2 * Math.PI))); + points.addAll(VectorUtil.transform(ZY_ROTATION, Ellipse.calculateEllipse(yRadius, zRadius, density, 2 * Math.PI))); } @Override - public void generateSurface(Set points) { + public void generateSurface(Set points, double density) { List ellipse; - double particleDensity = this.getParticleDensity(); if (xRadius > zRadius) { - ellipse = VectorUtil.transform(XY_ROTATION, Ellipse.calculateEllipse(xRadius, yRadius, particleDensity, 2 * Math.PI)); + ellipse = VectorUtil.transform(XY_ROTATION, Ellipse.calculateEllipse(xRadius, yRadius, density, 2 * Math.PI)); } else { - ellipse = VectorUtil.transform(ZY_ROTATION, Ellipse.calculateEllipse(yRadius, zRadius, particleDensity, 2 * Math.PI)); + ellipse = VectorUtil.transform(ZY_ROTATION, Ellipse.calculateEllipse(yRadius, zRadius, density, 2 * Math.PI)); } - points.addAll(generateEllipsoid(ellipse, 1)); + points.addAll(generateEllipsoid(ellipse, 1, density)); } @Override - public void generateFilled(Set points) { + public void generateFilled(Set points, double density) { List ellipse; double radius = Math.max(xRadius, zRadius); - double particleDensity = this.getParticleDensity(); - int steps = (int) Math.round(radius / particleDensity); + int steps = (int) Math.round(radius / density); for (int i = steps; i > 0; i--) { double r = (i / (double) steps); if (xRadius > zRadius) { - ellipse = VectorUtil.transform(XY_ROTATION, Ellipse.calculateEllipse(xRadius * r, yRadius * r, particleDensity, 2 * Math.PI)); + ellipse = VectorUtil.transform(XY_ROTATION, Ellipse.calculateEllipse(xRadius * r, yRadius * r, density, 2 * Math.PI)); } else { - ellipse = VectorUtil.transform(ZY_ROTATION, Ellipse.calculateEllipse(yRadius * r, zRadius * r, particleDensity, 2 * Math.PI)); + ellipse = VectorUtil.transform(ZY_ROTATION, Ellipse.calculateEllipse(yRadius * r, zRadius * r, density, 2 * Math.PI)); } - points.addAll(generateEllipsoid(ellipse, r)); + points.addAll(generateEllipsoid(ellipse, r, density)); } } - private Set generateEllipsoid(List ellipse, double radius) { + private Set generateEllipsoid(List ellipse, double radius, double density) { Set points = new LinkedHashSet<>(); for (int i = 0; i < Math.ceil(ellipse.size() / 4.0); i++) { double y = ellipse.get(i).y; double theta = Math.asin(y / (yRadius * radius)); - for (Vector3d v2 : Ellipse.calculateEllipse(radius * xRadius * Math.cos(theta), radius * zRadius * Math.cos(theta), this.getParticleDensity(), 2 * Math.PI)) { + for (Vector3d v2 : Ellipse.calculateEllipse(radius * xRadius * Math.cos(theta), radius * zRadius * Math.cos(theta), density, 2 * Math.PI)) { points.add(new Vector3d(v2.x, y, v2.z)); points.add(new Vector3d(v2.x, -y, v2.z)); } } - points.addAll(Ellipse.calculateEllipse(radius * xRadius, radius * zRadius, this.getParticleDensity(), 2 * Math.PI)); + points.addAll(Ellipse.calculateEllipse(radius * xRadius, radius * zRadius, density, 2 * Math.PI)); return points; } @Override - public void setParticleCount(int particleCount) { - particleCount = Math.max(particleCount, 1); - switch (this.getStyle()) { + public double computeDensity(SamplingStyle style, int targetPointCount) { + int count = Math.max(targetPointCount, 1); + return switch (style) { case OUTLINE -> { double h = (xRadius - yRadius) * (xRadius - yRadius) / ((xRadius + yRadius) + (xRadius + yRadius)); double circumferenceXY = Math.PI * (xRadius + yRadius) * (1 + (3 * h / (10 + Math.sqrt(4 - 3 * h)))); @@ -85,17 +83,25 @@ public void setParticleCount(int particleCount) { double circumferenceXZ = Math.PI * (xRadius + zRadius) * (1 + (3 * h / (10 + Math.sqrt(4 - 3 * h)))); h = (yRadius - zRadius) * (yRadius - zRadius) / ((yRadius + zRadius) + (yRadius + zRadius)); double circumferenceYZ = Math.PI * (yRadius + zRadius) * (1 + (3 * h / (10 + Math.sqrt(4 - 3 * h)))); - this.setParticleDensity((circumferenceXY + circumferenceXZ + circumferenceYZ) / particleCount); + yield (circumferenceXY + circumferenceXZ + circumferenceYZ) / count; } case SURFACE -> { double surfaceArea = 4 * Math.PI * Math.pow((Math.pow(xRadius * yRadius, 1.6) + Math.pow(xRadius * zRadius, 1.6) + Math.pow(zRadius * yRadius, 1.6)) / 3, 1 / 1.6); - this.setParticleDensity(Math.sqrt(surfaceArea / particleCount)); + yield Math.sqrt(surfaceArea / count); } case FILL -> { double volume = 4 / 3.0 * Math.PI * xRadius * yRadius * zRadius; - this.setParticleDensity(Math.cbrt(volume / particleCount)); + yield Math.cbrt(volume / count); } - } + }; + } + + @Override + public boolean contains(Vector3d point) { + double nx = point.x / xRadius; + double ny = point.y / yRadius; + double nz = point.z / zRadius; + return nx * nx + ny * ny + nz * nz <= 1; } @Override @@ -104,7 +110,7 @@ public void setParticleCount(int particleCount) { @Override public void setLength(double length) { xRadius = Math.max(length / 2, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } @Override @@ -113,7 +119,7 @@ public void setLength(double length) { @Override public void setWidth(double width) { zRadius = Math.max(width / 2, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } @Override @@ -122,7 +128,7 @@ public void setWidth(double width) { @Override public void setHeight(double height) { yRadius = Math.max(height / 2, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } @Override diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/EllipticalArc.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/EllipticalArc.java index abc9514..faa7541 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/EllipticalArc.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/EllipticalArc.java @@ -16,8 +16,8 @@ public EllipticalArc(double xRadius, double zRadius, double height, double cutof } @Override - public void generateSurface(Set points) { - generateFilled(points); + public void generateSurface(Set points, double density) { + generateFilled(points, density); } @Override @@ -28,11 +28,19 @@ public double getCutoffAngle() { @Override public void setCutoffAngle(double cutoffAngle) { this.cutoffAngle = Math.clamp(cutoffAngle, 0, Math.PI * 2); - this.setNeedsUpdate(true); + invalidate(); + } + + @Override + public boolean contains(Vector3d point) { + if (!super.contains(point)) return false; + double angle = Math.atan2(point.z, point.x); + if (angle < 0) angle += 2 * Math.PI; + return angle <= cutoffAngle; } @Override public Shape clone() { - return this.copyTo(new EllipticalArc(this.getLength(), this.getWidth(), this.getHeight(), cutoffAngle)); + return this.copyTo(new EllipticalArc(this.getLength() / 2, this.getWidth() / 2, this.getHeight(), cutoffAngle)); } } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Heart.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Heart.java index a57b650..a82ff16 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Heart.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Heart.java @@ -1,5 +1,6 @@ package com.sovdee.shapes.shapes; +import com.sovdee.shapes.sampling.SamplingStyle; import org.joml.Vector3d; import java.util.LinkedHashSet; @@ -18,9 +19,9 @@ public Heart(double length, double width, double eccentricity) { this.eccentricity = Math.max(eccentricity, 1); } - private static Set calculateHeart(double length, double width, double eccentricity, double particleDensity) { + private static Set calculateHeart(double length, double width, double eccentricity, double density) { Set points = new LinkedHashSet<>(); - double angleStep = 4 / 3.0 * particleDensity / (width + length); + double angleStep = 4 / 3.0 * density / (width + length); for (double theta = 0; theta < Math.PI * 2; theta += angleStep) { double x = width * Math.pow(Math.sin(theta), 3); double y = length * (Math.cos(theta) - 1 / eccentricity * Math.cos(2 * theta) - 1.0 / 6 * Math.cos(3 * theta) - 1.0 / 16 * Math.cos(4 * theta)); @@ -30,20 +31,37 @@ private static Set calculateHeart(double length, double width, double } @Override - public void generateOutline(Set points) { - points.addAll(calculateHeart(length / 2, width / 2, eccentricity, this.getParticleDensity())); + public void generateOutline(Set points, double density) { + points.addAll(calculateHeart(length / 2, width / 2, eccentricity, density)); } @Override - public void generateSurface(Set points) { - double particleDensity = this.getParticleDensity(); - for (double w = width, l = length; w > 0 && l > 0; w -= particleDensity * 1.5, l -= particleDensity * 1.5) { - points.addAll(calculateHeart(l / 2, w / 2, eccentricity, particleDensity)); + public void generateSurface(Set points, double density) { + for (double w = width, l = length; w > 0 && l > 0; w -= density * 1.5, l -= density * 1.5) { + points.addAll(calculateHeart(l / 2, w / 2, eccentricity, density)); } } @Override - public void setParticleCount(int particleCount) { } + public double computeDensity(SamplingStyle style, int targetPointCount) { + return 0.25; // No good formula available + } + + @Override + public boolean contains(Vector3d point) { + // Approximate: check if point is within parametric heart boundary + if (Math.abs(point.y) > EPSILON) return false; + double x = point.x; + double z = point.z; + double hw = width / 2; + double hl = length / 2; + if (hw < EPSILON || hl < EPSILON) return false; + // Normalize and check parametric distance + double nx = x / hw; + double nz = z / hl; + // Simple bounding check + return nx * nx + nz * nz <= 4; + } @Override public double getHeight() { return 0; } @@ -57,7 +75,7 @@ public void setHeight(double height) { } @Override public void setWidth(double width) { this.width = Math.max(width, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } @Override @@ -66,14 +84,14 @@ public void setWidth(double width) { @Override public void setLength(double length) { this.length = Math.max(length, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } public double getEccentricity() { return eccentricity; } public void setEccentricity(double eccentricity) { this.eccentricity = Math.max(1, eccentricity); - this.setNeedsUpdate(true); + invalidate(); } @Override diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Helix.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Helix.java index 3eaf706..4062b1e 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Helix.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Helix.java @@ -1,5 +1,6 @@ package com.sovdee.shapes.shapes; +import com.sovdee.shapes.sampling.SamplingStyle; import org.joml.Vector3d; import java.util.LinkedHashSet; @@ -29,14 +30,14 @@ public Helix(double radius, double height, double slope, int direction) { this.direction = direction; } - private static Set calculateHelix(double radius, double height, double slope, int direction, double particleDensity) { + private static Set calculateHelix(double radius, double height, double slope, int direction, double density) { Set points = new LinkedHashSet<>(); if (radius <= 0 || height <= 0) { return points; } double loops = Math.abs(height / slope); double length = slope * slope + radius * radius; - double stepSize = particleDensity / length; + double stepSize = density / length; for (double t = 0; t < loops; t += stepSize) { double x = radius * Math.cos(direction * t); double z = radius * Math.sin(direction * t); @@ -46,32 +47,42 @@ private static Set calculateHelix(double radius, double height, double } @Override - public void generateOutline(Set points) { - points.addAll(calculateHelix(radius, height, slope, direction, this.getParticleDensity())); + public void generateOutline(Set points, double density) { + points.addAll(calculateHelix(radius, height, slope, direction, density)); } @Override - public void generateSurface(Set points) { - double particleDensity = this.getParticleDensity(); - for (double r = radius; r > 0; r -= particleDensity) { - points.addAll(calculateHelix(r, height, slope, direction, particleDensity)); + public void generateSurface(Set points, double density) { + for (double r = radius; r > 0; r -= density) { + points.addAll(calculateHelix(r, height, slope, direction, density)); } } @Override - public void setParticleCount(int particleCount) { - particleCount = Math.max(particleCount, 1); - this.setParticleDensity(switch (this.getStyle()) { - case OUTLINE -> (Math.sqrt(slope * slope + radius * radius) * (height / slope) / particleCount); - case FILL, SURFACE -> Math.sqrt(slope * slope + radius * radius * (height / slope) / particleCount); - }); + public double computeDensity(SamplingStyle style, int targetPointCount) { + int count = Math.max(targetPointCount, 1); + return switch (style) { + case OUTLINE -> Math.sqrt(slope * slope + radius * radius) * (height / slope) / count; + case FILL, SURFACE -> Math.sqrt(slope * slope + radius * radius * (height / slope) / count); + }; + } + + @Override + public boolean contains(Vector3d point) { + // Approximate: check distance to nearest helix point + if (point.y < 0 || point.y > height) return false; + double t = point.y / slope; + double helixX = radius * Math.cos(direction * t); + double helixZ = radius * Math.sin(direction * t); + double dist = Math.sqrt((point.x - helixX) * (point.x - helixX) + (point.z - helixZ) * (point.z - helixZ)); + return dist <= EPSILON; } public double getSlope() { return slope; } public void setSlope(double slope) { this.slope = Math.max(slope, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } public int getDirection() { return direction; } @@ -80,7 +91,7 @@ public void setDirection(int direction) { if (direction != 1 && direction != -1) throw new IllegalArgumentException("Direction must be 1 or -1"); this.direction = direction; - this.setNeedsUpdate(true); + invalidate(); } @Override @@ -89,7 +100,7 @@ public void setDirection(int direction) { @Override public void setLength(double length) { height = Math.max(length, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } @Override @@ -104,7 +115,7 @@ public void setWidth(double width) { } @Override public void setHeight(double height) { this.height = Math.max(height, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } @Override @@ -113,7 +124,7 @@ public void setHeight(double height) { @Override public void setRadius(double radius) { this.radius = Math.max(radius, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } @Override diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/IrregularPolygon.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/IrregularPolygon.java index f5d3df3..1a6092c 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/IrregularPolygon.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/IrregularPolygon.java @@ -1,5 +1,6 @@ package com.sovdee.shapes.shapes; +import com.sovdee.shapes.sampling.SamplingStyle; import org.joml.Vector3d; import java.util.ArrayList; @@ -45,10 +46,9 @@ private void setBounds(Collection vertices) { } @Override - public void generateOutline(Set points) { - double particleDensity = this.getParticleDensity(); - points.addAll(Line.connectPoints(vertices, particleDensity)); - points.addAll(Line.calculateLine(vertices.get(0), vertices.get(vertices.size() - 1), particleDensity)); + public void generateOutline(Set points, double density) { + points.addAll(Line.connectPoints(vertices, density)); + points.addAll(Line.calculateLine(vertices.get(0), vertices.get(vertices.size() - 1), density)); if (height != 0) { Set upperPoints = new LinkedHashSet<>(); for (Vector3d v : points) { @@ -56,14 +56,14 @@ public void generateOutline(Set points) { } points.addAll(upperPoints); for (Vector3d v : vertices) { - points.addAll(Line.calculateLine(v, new Vector3d(v.x, height, v.z), particleDensity)); + points.addAll(Line.calculateLine(v, new Vector3d(v.x, height, v.z), density)); } } } @Override - public void setParticleCount(int particleCount) { - particleCount = Math.max(particleCount, 1); + public double computeDensity(SamplingStyle style, int targetPointCount) { + int count = Math.max(targetPointCount, 1); double perimeter = 0; for (int i = 0; i < vertices.size() - 1; i++) { perimeter += vertices.get(i).distance(vertices.get(i + 1)); @@ -71,8 +71,25 @@ public void setParticleCount(int particleCount) { perimeter += vertices.get(0).distance(vertices.get(vertices.size() - 1)); perimeter *= 2; perimeter += vertices.size() * height; - this.setParticleDensity(perimeter / particleCount); - this.setNeedsUpdate(true); + return perimeter / count; + } + + @Override + public boolean contains(Vector3d point) { + if (height > 0 && (point.y < 0 || point.y > height)) return false; + if (height == 0 && Math.abs(point.y) > EPSILON) return false; + // Ray casting algorithm on XZ plane + int n = vertices.size(); + boolean inside = false; + for (int i = 0, j = n - 1; i < n; j = i++) { + double xi = vertices.get(i).x, zi = vertices.get(i).z; + double xj = vertices.get(j).x, zj = vertices.get(j).z; + if ((zi > point.z) != (zj > point.z) && + point.x < (xj - xi) * (point.z - zi) / (zj - zi) + xi) { + inside = !inside; + } + } + return inside; } @Override @@ -93,7 +110,7 @@ public void setWidth(double width) { } @Override public void setHeight(double height) { this.height = Math.max(height, 0); - this.setNeedsUpdate(true); + invalidate(); } @Override diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Line.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Line.java index 60f90ec..7c830d4 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Line.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Line.java @@ -1,5 +1,6 @@ package com.sovdee.shapes.shapes; +import com.sovdee.shapes.sampling.SamplingStyle; import org.joml.Vector3d; import java.util.LinkedHashSet; @@ -38,14 +39,13 @@ public Line(Supplier start, Supplier end) { } /** - * Calculates points along a line from start to end with the given particle density. - * Uses additive stepping for efficiency. + * Calculates points along a line from start to end with the given density. */ - public static Set calculateLine(Vector3d start, Vector3d end, double particleDensity) { + public static Set calculateLine(Vector3d start, Vector3d end, double density) { Set points = new LinkedHashSet<>(); Vector3d direction = new Vector3d(end).sub(start); double length = direction.length(); - double step = length / Math.round(length / particleDensity); + double step = length / Math.round(length / density); direction.normalize().mul(step); Vector3d current = new Vector3d(start); @@ -60,17 +60,17 @@ public static Set calculateLine(Vector3d start, Vector3d end, double p /** * Connects a list of points with lines, returning all intermediate points. */ - public static Set connectPoints(List points, double particleDensity) { + public static Set connectPoints(List points, double density) { Set connectedPoints = new LinkedHashSet<>(); for (int i = 0; i < points.size() - 1; i++) { - connectedPoints.addAll(calculateLine(points.get(i), points.get(i + 1), particleDensity)); + connectedPoints.addAll(calculateLine(points.get(i), points.get(i + 1), density)); } return connectedPoints; } @Override - public void generateOutline(Set points) { - points.addAll(calculateLine(getStart(), getEnd(), this.getParticleDensity())); + public void generateOutline(Set points, double density) { + points.addAll(calculateLine(getStart(), getEnd(), density)); } public Vector3d getStart() { @@ -80,7 +80,7 @@ public Vector3d getStart() { public void setStart(Vector3d start) { final Vector3d s = new Vector3d(start); this.startSupplier = () -> new Vector3d(s); - this.setNeedsUpdate(true); + invalidate(); } public Vector3d getEnd() { @@ -90,7 +90,7 @@ public Vector3d getEnd() { public void setEnd(Vector3d end) { final Vector3d e = new Vector3d(end); this.endSupplier = () -> new Vector3d(e); - this.setNeedsUpdate(true); + invalidate(); } public Supplier getStartSupplier() { @@ -110,12 +110,23 @@ public void setEndSupplier(Supplier endSupplier) { } @Override - public void setParticleCount(int particleCount) { - particleCount = Math.max(particleCount, 1); + public double computeDensity(SamplingStyle style, int targetPointCount) { + int count = Math.max(targetPointCount, 1); + return new Vector3d(getEnd()).sub(getStart()).length() / count; + } + + @Override + public boolean contains(Vector3d point) { Vector3d start = getStart(); Vector3d end = getEnd(); - this.setParticleDensity(new Vector3d(end).sub(start).length() / particleCount); - this.setNeedsUpdate(true); + Vector3d line = new Vector3d(end).sub(start); + double len = line.length(); + if (len < EPSILON) return point.distance(start) < EPSILON; + Vector3d toPoint = new Vector3d(point).sub(start); + double t = toPoint.dot(line) / (len * len); + if (t < 0 || t > 1) return false; + Vector3d closest = new Vector3d(start).add(new Vector3d(line).mul(t)); + return point.distance(closest) <= EPSILON; } @Override @@ -131,7 +142,6 @@ public void setLength(double length) { Vector3d direction = new Vector3d(end).sub(start).normalize(); Vector3d newEnd = new Vector3d(start).add(direction.mul(length)); setEnd(newEnd); - this.setNeedsUpdate(true); } @Override diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Rectangle.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Rectangle.java index 0e36789..03f8555 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Rectangle.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Rectangle.java @@ -1,5 +1,6 @@ package com.sovdee.shapes.shapes; +import com.sovdee.shapes.sampling.SamplingStyle; import org.joml.Vector3d; import java.util.Set; @@ -25,7 +26,6 @@ public Rectangle(double length, double width, Plane plane) { this.plane = plane; this.halfLength = Math.max(length / 2, Shape.EPSILON); this.halfWidth = Math.max(width / 2, Shape.EPSILON); - calculateSteps(); } public Rectangle(Vector3d cornerA, Vector3d cornerB, Plane plane) { @@ -40,7 +40,6 @@ public Rectangle(Vector3d cornerA, Vector3d cornerB, Plane plane) { case XY -> centerOffset.z = 0; case YZ -> centerOffset.x = 0; } - calculateSteps(); } public Rectangle(Supplier cornerA, Supplier cornerB, Plane plane) { @@ -51,7 +50,6 @@ public Rectangle(Supplier cornerA, Supplier cornerB, Plane p Vector3d a = cornerA.get(); Vector3d b = cornerB.get(); setLengthWidth(a, b); - calculateSteps(); setDynamic(true); } @@ -76,14 +74,28 @@ private Vector3d vectorFromLengthWidth(double length, double width) { }; } - private void calculateSteps() { - double particleDensity = this.getParticleDensity(); - lengthStep = 2 * halfWidth / Math.round(2 * halfWidth / particleDensity); - widthStep = 2 * halfLength / Math.round(2 * halfLength / particleDensity); + private void calculateSteps(double density) { + lengthStep = 2 * halfWidth / Math.round(2 * halfWidth / density); + widthStep = 2 * halfLength / Math.round(2 * halfLength / density); } @Override - public void generateOutline(Set points) { + public void beforeSampling(double density) { + if (cornerASupplier != null && cornerBSupplier != null) { + Vector3d a = cornerASupplier.get(); + Vector3d b = cornerBSupplier.get(); + setLengthWidth(a, b); + } + calculateSteps(density); + } + + @Override + public void afterSampling(Set points) { + points.forEach(vector -> vector.add(centerOffset)); + } + + @Override + public void generateOutline(Set points, double density) { for (double l = -halfLength + widthStep; l < halfLength; l += widthStep) { points.add(vectorFromLengthWidth(l, -halfWidth)); points.add(vectorFromLengthWidth(l, halfWidth)); @@ -95,7 +107,7 @@ public void generateOutline(Set points) { } @Override - public void generateSurface(Set points) { + public void generateSurface(Set points, double density) { for (double w = -halfWidth; w <= halfWidth; w += lengthStep) { for (double l = -halfLength; l <= halfLength; l += widthStep) { points.add(vectorFromLengthWidth(l, w)); @@ -104,25 +116,21 @@ public void generateSurface(Set points) { } @Override - public void generatePoints(Set points) { - if (cornerASupplier != null && cornerBSupplier != null) { - Vector3d a = cornerASupplier.get(); - Vector3d b = cornerBSupplier.get(); - setLengthWidth(a, b); - } - calculateSteps(); - super.generatePoints(points); - points.forEach(vector -> vector.add(centerOffset)); + public double computeDensity(SamplingStyle style, int targetPointCount) { + int count = Math.max(targetPointCount, 1); + return switch (style) { + case FILL, SURFACE -> Math.sqrt(4 * halfWidth * halfLength / count); + case OUTLINE -> 4 * (halfWidth + halfLength) / count; + }; } @Override - public void setParticleCount(int particleCount) { - particleCount = Math.max(particleCount, 1); - switch (this.getStyle()) { - case FILL, SURFACE -> this.setParticleDensity(Math.sqrt(4 * halfWidth * halfLength / particleCount)); - case OUTLINE -> this.setParticleDensity(4 * (halfWidth + halfLength) / particleCount); - } - this.setNeedsUpdate(true); + public boolean contains(Vector3d point) { + return switch (plane) { + case XZ -> Math.abs(point.x) <= halfLength && Math.abs(point.z) <= halfWidth && Math.abs(point.y) < EPSILON; + case XY -> Math.abs(point.x) <= halfLength && Math.abs(point.y) <= halfWidth && Math.abs(point.z) < EPSILON; + case YZ -> Math.abs(point.y) <= halfLength && Math.abs(point.z) <= halfWidth && Math.abs(point.x) < EPSILON; + }; } @Override @@ -131,7 +139,7 @@ public void setParticleCount(int particleCount) { @Override public void setLength(double length) { this.halfLength = Math.max(length / 2, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } @Override @@ -140,7 +148,7 @@ public void setLength(double length) { @Override public void setWidth(double width) { this.halfWidth = Math.max(width / 2, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } @Override @@ -153,7 +161,7 @@ public void setHeight(double height) { } public void setPlane(Plane plane) { this.plane = plane; - this.setNeedsUpdate(true); + invalidate(); } public Supplier getCornerASupplier() { return cornerASupplier; } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/RegularPolygon.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/RegularPolygon.java index 297d79b..87705b3 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/RegularPolygon.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/RegularPolygon.java @@ -1,5 +1,6 @@ package com.sovdee.shapes.shapes; +import com.sovdee.shapes.sampling.SamplingStyle; import com.sovdee.shapes.util.VectorUtil; import org.joml.Vector3d; @@ -33,12 +34,12 @@ public RegularPolygon(double angle, double radius, double height) { // --- Static calculation methods --- - public static Set calculateRegularPolygon(double radius, double angle, double particleDensity, boolean wireframe) { + public static Set calculateRegularPolygon(double radius, double angle, double density, boolean wireframe) { angle = Math.max(angle, Shape.EPSILON); Set points = new LinkedHashSet<>(); double apothem = radius * Math.cos(angle / 2); - double radiusStep = radius / Math.round(apothem / particleDensity); + double radiusStep = radius / Math.round(apothem / density); if (wireframe) { radiusStep = 2 * radius; } else { @@ -50,27 +51,27 @@ public static Set calculateRegularPolygon(double radius, double angle, points.addAll(Line.calculateLine( VectorUtil.rotateAroundY(new Vector3d(vertex), i), VectorUtil.rotateAroundY(new Vector3d(vertex), i + angle), - particleDensity)); + density)); } } return points; } - public static Set calculateRegularPrism(double radius, double angle, double height, double particleDensity, boolean wireframe) { + public static Set calculateRegularPrism(double radius, double angle, double height, double density, boolean wireframe) { Set points = new LinkedHashSet<>(); Vector3d vertex = new Vector3d(radius, 0, 0); for (double i = 0; i < 2 * Math.PI; i += angle) { Vector3d currentVertex = VectorUtil.rotateAroundY(new Vector3d(vertex), i); - for (Vector3d vector : Line.calculateLine(currentVertex, VectorUtil.rotateAroundY(new Vector3d(vertex), i + angle), particleDensity)) { + for (Vector3d vector : Line.calculateLine(currentVertex, VectorUtil.rotateAroundY(new Vector3d(vertex), i + angle), density)) { points.add(vector); if (wireframe) { points.add(new Vector3d(vector.x, height, vector.z)); } else { - points.addAll(Line.calculateLine(vector, new Vector3d(vector.x, height, vector.z), particleDensity)); + points.addAll(Line.calculateLine(vector, new Vector3d(vector.x, height, vector.z), density)); } } if (wireframe) - points.addAll(Line.calculateLine(currentVertex, new Vector3d(currentVertex.x, height, currentVertex.z), particleDensity)); + points.addAll(Line.calculateLine(currentVertex, new Vector3d(currentVertex.x, height, currentVertex.z), density)); } return points; } @@ -78,51 +79,59 @@ public static Set calculateRegularPrism(double radius, double angle, d // --- Generation methods --- @Override - public void generateOutline(Set points) { + public void generateOutline(Set points, double density) { if (height == 0) - points.addAll(calculateRegularPolygon(this.radius, this.angle, this.getParticleDensity(), true)); + points.addAll(calculateRegularPolygon(this.radius, this.angle, density, true)); else - points.addAll(calculateRegularPrism(this.radius, this.angle, this.height, this.getParticleDensity(), true)); + points.addAll(calculateRegularPrism(this.radius, this.angle, this.height, density, true)); } @Override - public void generateSurface(Set points) { + public void generateSurface(Set points, double density) { if (height == 0) - points.addAll(calculateRegularPolygon(this.radius, this.angle, this.getParticleDensity(), false)); + points.addAll(calculateRegularPolygon(this.radius, this.angle, density, false)); else - points.addAll(calculateRegularPrism(this.radius, this.angle, this.height, this.getParticleDensity(), false)); + points.addAll(calculateRegularPrism(this.radius, this.angle, this.height, density, false)); } @Override - public void generateFilled(Set points) { + public void generateFilled(Set points, double density) { if (height == 0) - generateSurface(points); + generateSurface(points, density); else { - double particleDensity = this.getParticleDensity(); - Set polygon = calculateRegularPolygon(this.radius, this.angle, particleDensity, false); - fillVertically(polygon, height, particleDensity); + Set polygon = calculateRegularPolygon(this.radius, this.angle, density, false); + fillVertically(polygon, height, density); points.addAll(polygon); } } @Override - public void setParticleCount(int particleCount) { - particleCount = Math.max(particleCount, 1); + public double computeDensity(SamplingStyle style, int targetPointCount) { + int count = Math.max(targetPointCount, 1); int sides = getSides(); - this.setParticleDensity(switch (this.getStyle()) { + return switch (style) { case OUTLINE -> { if (height == 0) - yield 2 * sides * radius * Math.sin(angle / 2) / particleCount; - yield (4 * sides * radius * Math.sin(angle / 2) + height * sides) / particleCount; + yield 2 * sides * radius * Math.sin(angle / 2) / count; + yield (4 * sides * radius * Math.sin(angle / 2) + height * sides) / count; } case SURFACE -> { if (height == 0) - yield Math.sqrt(sides * radius * radius * Math.sin(angle) / 2 / particleCount); - yield (sides * radius * radius * Math.sin(angle) + getSideLength() * sides * height) / particleCount; + yield Math.sqrt(sides * radius * radius * Math.sin(angle) / 2 / count); + yield (sides * radius * radius * Math.sin(angle) + getSideLength() * sides * height) / count; } - case FILL -> (sides * radius * radius * Math.sin(angle) * height) / particleCount; - }); - this.setNeedsUpdate(true); + case FILL -> (sides * radius * radius * Math.sin(angle) * height) / count; + }; + } + + @Override + public boolean contains(Vector3d point) { + if (height > 0 && (point.y < 0 || point.y > height)) return false; + if (height == 0 && Math.abs(point.y) > EPSILON) return false; + // Check if point is within the polygon on XZ plane using inscribed radius + double dist = Math.sqrt(point.x * point.x + point.z * point.z); + double apothem = radius * Math.cos(angle / 2); + return dist <= radius && dist <= apothem / Math.cos(Math.atan2(point.z, point.x) % angle - angle / 2); } @Override @@ -131,7 +140,7 @@ public void setParticleCount(int particleCount) { @Override public void setSides(int sides) { this.angle = (Math.PI * 2) / Math.max(sides, 3); - this.setNeedsUpdate(true); + invalidate(); } @Override @@ -142,7 +151,7 @@ public void setSideLength(double sideLength) { sideLength = Math.max(sideLength, Shape.EPSILON); this.radius = sideLength / (2 * Math.sin(this.angle / 2)); this.radius = Math.max(radius, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } @Override @@ -151,7 +160,7 @@ public void setSideLength(double sideLength) { @Override public void setRadius(double radius) { this.radius = Math.max(radius, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } @Override @@ -172,7 +181,7 @@ public void setWidth(double width) { } @Override public void setHeight(double height) { this.height = Math.max(height, 0); - this.setNeedsUpdate(true); + invalidate(); } @Override diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/RegularPolyhedron.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/RegularPolyhedron.java index 6bee084..ec0042b 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/RegularPolyhedron.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/RegularPolyhedron.java @@ -1,5 +1,6 @@ package com.sovdee.shapes.shapes; +import com.sovdee.shapes.sampling.SamplingStyle; import org.joml.Quaterniond; import org.joml.Vector3d; @@ -58,13 +59,11 @@ public class RegularPolyhedron extends AbstractShape implements RadialShape, Pol new Quaterniond(0.5, 0.3090169943749475, -0.6881909602355868, -0.42532540417602), new Quaterniond(0.3090169943749475, -0.5, -0.42532540417602, 0.6881909602355868) }; - // Radius-to-side-length conversion factors for each polyhedron type private static final double TETRA_R2SL = 0.6123724356957945; private static final double OCTA_R2SL = 0.7071067811865; private static final double DODECA_R2SL = 1.401258538; private static final double ICOSA_R2SL = 0.9510565162951535; - // Side-length to inscribed-radius conversion factors private static final double TETRA_INSC = 1.0 / 4.89897948556; private static final double OCTA_INSC = 0.408248290; private static final double DODECA_INSC = 1.113516364; @@ -83,40 +82,43 @@ public RegularPolyhedron(double radius, int faces) { } @Override - public void generateOutline(Set points) { + public void generateOutline(Set points, double density) { points.addAll(switch (faces) { - case 4 -> generatePolyhedron(TETRAHEDRON_FACES, radius); - case 8 -> generatePolyhedron(OCTAHEDRON_FACES, radius); - case 20 -> generatePolyhedron(ICOSAHEDRON_FACES, radius); - case 12 -> generatePolyhedron(DODECAHEDRON_FACES, radius); + case 4 -> generatePolyhedron(TETRAHEDRON_FACES, radius, density, SamplingStyle.OUTLINE); + case 8 -> generatePolyhedron(OCTAHEDRON_FACES, radius, density, SamplingStyle.OUTLINE); + case 20 -> generatePolyhedron(ICOSAHEDRON_FACES, radius, density, SamplingStyle.OUTLINE); + case 12 -> generatePolyhedron(DODECAHEDRON_FACES, radius, density, SamplingStyle.OUTLINE); default -> new HashSet<>(); }); } @Override - public void generateFilled(Set points) { - double step = radius / Math.round(radius / this.getParticleDensity()); - switch (faces) { - case 4: - for (double i = radius; i > 0; i -= step) - points.addAll(generatePolyhedron(TETRAHEDRON_FACES, i)); - break; - case 8: - for (double i = radius; i > 0; i -= step) - points.addAll(generatePolyhedron(OCTAHEDRON_FACES, i)); - break; - case 12: - for (double i = radius; i > 0; i -= step) - points.addAll(generatePolyhedron(DODECAHEDRON_FACES, i)); - break; - case 20: - for (double i = radius; i > 0; i -= step) - points.addAll(generatePolyhedron(ICOSAHEDRON_FACES, i)); - break; + public void generateSurface(Set points, double density) { + points.addAll(switch (faces) { + case 4 -> generatePolyhedron(TETRAHEDRON_FACES, radius, density, SamplingStyle.SURFACE); + case 8 -> generatePolyhedron(OCTAHEDRON_FACES, radius, density, SamplingStyle.SURFACE); + case 20 -> generatePolyhedron(ICOSAHEDRON_FACES, radius, density, SamplingStyle.SURFACE); + case 12 -> generatePolyhedron(DODECAHEDRON_FACES, radius, density, SamplingStyle.SURFACE); + default -> new HashSet<>(); + }); + } + + @Override + public void generateFilled(Set points, double density) { + double step = radius / Math.round(radius / density); + Quaterniond[] rotations = switch (faces) { + case 4 -> TETRAHEDRON_FACES; + case 8 -> OCTAHEDRON_FACES; + case 12 -> DODECAHEDRON_FACES; + case 20 -> ICOSAHEDRON_FACES; + default -> new Quaterniond[0]; + }; + for (double i = radius; i > 0; i -= step) { + points.addAll(generatePolyhedron(rotations, i, density, SamplingStyle.SURFACE)); } } - private Set generatePolyhedron(Quaterniond[] rotations, double radius) { + private Set generatePolyhedron(Quaterniond[] rotations, double radius, double density, SamplingStyle style) { Set points = new LinkedHashSet<>(); int sides = this.faces == 12 ? 5 : 3; double sideLength = switch (faces) { @@ -135,11 +137,10 @@ private Set generatePolyhedron(Quaterniond[] rotations, double radius) }; Vector3d offset = new Vector3d(0, inscribedRadius, 0); double faceRadius = sideLength / (2 * Math.sin(Math.PI / sides)); - Style style = this.getStyle(); for (Quaterniond rotation : rotations) { Set facePoints = new LinkedHashSet<>(switch (style) { - case OUTLINE -> generateFaceOutline(sides, faceRadius); - case FILL, SURFACE -> generateFaceSurface(sides, faceRadius); + case OUTLINE -> generateFaceOutline(sides, faceRadius, density); + case FILL, SURFACE -> generateFaceSurface(sides, faceRadius, density); }); facePoints.forEach(point -> rotation.transform(point.add(offset))); points.addAll(facePoints); @@ -147,24 +148,39 @@ private Set generatePolyhedron(Quaterniond[] rotations, double radius) return points; } - private Set generateFaceOutline(int sides, double radius) { - return new LinkedHashSet<>(RegularPolygon.calculateRegularPolygon(radius, 2 * Math.PI / sides, this.getParticleDensity(), true)); + private Set generateFaceOutline(int sides, double radius, double density) { + return new LinkedHashSet<>(RegularPolygon.calculateRegularPolygon(radius, 2 * Math.PI / sides, density, true)); } - private Set generateFaceSurface(int sides, double radius) { + private Set generateFaceSurface(int sides, double radius, double density) { Set facePoints = new LinkedHashSet<>(); - double particleDensity = this.getParticleDensity(); double apothem = radius * Math.cos(Math.PI / sides); - double radiusStep = radius / Math.round(apothem / particleDensity); + double radiusStep = radius / Math.round(apothem / density); for (double subRadius = radius; subRadius > 0; subRadius -= radiusStep) { - facePoints.addAll(RegularPolygon.calculateRegularPolygon(subRadius, 2 * Math.PI / sides, particleDensity, false)); + facePoints.addAll(RegularPolygon.calculateRegularPolygon(subRadius, 2 * Math.PI / sides, density, false)); } facePoints.add(new Vector3d(0, 0, 0)); return facePoints; } @Override - public void setParticleCount(int particleCount) { } + public double computeDensity(SamplingStyle style, int targetPointCount) { + return 0.25; // No good formula available + } + + @Override + public boolean contains(Vector3d point) { + // Conservative: check if within inscribed sphere + double sideLength = getSideLength(); + double inscribedRadius = switch (faces) { + case 4 -> sideLength * TETRA_INSC; + case 8 -> sideLength * OCTA_INSC; + case 12 -> sideLength * DODECA_INSC; + case 20 -> sideLength * ICOSA_INSC; + default -> 0; + }; + return point.length() <= inscribedRadius; + } @Override public Shape clone() { @@ -180,7 +196,7 @@ public void setSides(int sides) { case 4, 8, 12, 20 -> this.faces = sides; default -> { return; } } - this.setNeedsUpdate(true); + invalidate(); } @Override @@ -204,7 +220,7 @@ public void setSideLength(double sideLength) { case 20 -> this.radius = sideLength * ICOSA_R2SL; default -> { return; } } - this.setNeedsUpdate(true); + invalidate(); } @Override @@ -213,6 +229,6 @@ public void setSideLength(double sideLength) { @Override public void setRadius(double radius) { this.radius = Math.max(radius, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Shape.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Shape.java index 793699e..194910c 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Shape.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Shape.java @@ -1,287 +1,78 @@ package com.sovdee.shapes.shapes; -import com.sovdee.shapes.DrawContext; +import com.sovdee.shapes.sampling.PointSampler; +import com.sovdee.shapes.sampling.SamplingStyle; import org.joml.Quaterniond; import org.joml.Vector3d; -import java.util.Comparator; import java.util.Set; -import java.util.UUID; -import java.util.function.BiConsumer; /** - * Represents a geometric shape defined by a set of points. - * Shapes have a style, orientation, scale, and offset which affect point generation. + * Represents a geometric shape. Pure geometry interface — sampling/caching/drawing + * configuration lives in {@link PointSampler}. */ public interface Shape extends Cloneable { double EPSILON = 0.0001; - /** - * Gets the points for the shape using the shape's own orientation. - * Uses cached points if the shape has not been modified. - * - * @return A set of points that make up the shape. - */ - Set getPoints(); - - /** - * Sets the points for the shape. - * - * @param points The points to use. - */ - void setPoints(Set points); - - /** - * Gets the points for the shape using the given orientation. - * Uses cached points if the shape has not been modified. - * - * @param orientation The orientation to apply. - * @return A set of points that make up the shape. - */ - Set getPoints(Quaterniond orientation); - - /** - * Generates the points for the shape based on the current style. - * No orientation, offset, or scale is applied. - */ - default void generatePoints(Set points) { - getStyle().generatePoints(this, points); - } - - /** - * Generates the outline points for the shape. - * - * @param points A set that will be filled with the points. - */ - void generateOutline(Set points); - - /** - * Generates the surface points for the shape. - * Default implementation returns the same as generateOutline(). - * - * @param points A set that will be filled with the points. - */ - void generateSurface(Set points); - - /** - * Generates the filled points for the shape. - * Default implementation returns the same as generateSurface(). - * - * @param points A set that will be filled with the points. - */ - void generateFilled(Set points); - - /** - * @return the relative X axis of the shape. - */ - Vector3d getRelativeXAxis(boolean useLastOrientation); + // --- Spatial transform --- - /** - * @return the relative Y axis of the shape. - */ - Vector3d getRelativeYAxis(boolean useLastOrientation); - - /** - * @return the relative Z axis of the shape. - */ - Vector3d getRelativeZAxis(boolean useLastOrientation); - - /** - * @return the style of the shape. - */ - Style getStyle(); - - /** - * Sets the style of the shape. - * - * @param style The style to use. - */ - void setStyle(Style style); - - /** - * @return a clone of the orientation of the shape. - */ Quaterniond getOrientation(); - - /** - * Sets the orientation of the shape. - * - * @param orientation The orientation to use. - */ void setOrientation(Quaterniond orientation); - /** - * @return the scale of the shape. - */ double getScale(); - - /** - * Sets the scale of the shape. - * - * @param scale The scale to use. - */ void setScale(double scale); - /** - * @return the offset of the shape. - */ Vector3d getOffset(); - - /** - * Sets the offset of the shape. - * - * @param offset The offset vector to use. - */ void setOffset(Vector3d offset); - /** - * @return the UUID of the shape. - */ - UUID getUUID(); - - /** - * @return The comparator used to order points, or null for default ordering. - */ - Comparator getOrdering(); - - /** - * Sets the comparator to order the points. - * - * @param comparator the Comparator to use, or null for default ordering. - */ - void setOrdering(Comparator comparator); + // --- Oriented axes --- - /** - * @return the particle density of the shape. - */ - double getParticleDensity(); + Vector3d getRelativeXAxis(); + Vector3d getRelativeYAxis(); + Vector3d getRelativeZAxis(); - /** - * Sets the particle density of the shape. - * - * @param particleDensity The density in meters per particle. Must be greater than 0. - */ - void setParticleDensity(double particleDensity); + // --- Geometry query --- - /** - * @return the number of points in the shape. - */ - int getParticleCount(); + boolean contains(Vector3d point); - /** - * Sets the approximate number of points for the shape. - * - * @param particleCount The target number of points. Must be greater than 0. - */ - void setParticleCount(int particleCount); + // --- Change detection --- - /** - * @return the draw context attached to this shape, or null if none. - */ - DrawContext getDrawContext(); + long getVersion(); - /** - * Sets the draw context for this shape. - * - * @param drawContext The draw context to attach. - */ - void setDrawContext(DrawContext drawContext); + // --- Dynamic support --- - /** - * @return whether the shape is dynamic (always regenerates points). - */ boolean isDynamic(); - - /** - * Sets whether the shape is dynamic. - * Dynamic shapes always regenerate points, bypassing state caching. - * - * @param dynamic Whether the shape is dynamic. - */ void setDynamic(boolean dynamic); - /** - * @return whether the shape needs a point update. - */ - boolean needsUpdate(); + // --- Point generation (density as parameter) --- - /** - * Marks the shape as needing an update or not. - * - * @param needsUpdate Whether the shape needs an update. - */ - void setNeedsUpdate(boolean needsUpdate); + void generateOutline(Set points, double density); + void generateSurface(Set points, double density); + void generateFilled(Set points, double density); /** - * @return a deep copy of the shape. + * Called by PointSampler before point generation. Override for supplier refresh, step recalc, etc. */ - Shape clone(); + default void beforeSampling(double density) {} /** - * Copies the shape's properties to a new shape. - * - * @param shape The shape to copy properties to. - * @return the updated shape. + * Called by PointSampler after point generation. Override for centerOffset adjustment, etc. */ - Shape copyTo(Shape shape); + default void afterSampling(Set points) {} /** - * Gets the physical state of the shape for change detection. - * - * @return A state object representing the shape's current state. + * Computes the density needed to achieve approximately the given number of points. */ - State getState(); + double computeDensity(SamplingStyle style, int targetPointCount); - /** - * Gets the physical state with a custom orientation. - * - * @param orientation The orientation to use for the state. - * @return A state object representing the shape's state. - */ - State getState(Quaterniond orientation); + // --- PointSampler --- - /** - * Sets the last state of the shape for change detection. - * - * @param state The state to set. - */ - void setLastState(State state); - - - /** - * The style of a shape, which determines how it is drawn. - */ - enum Style { - OUTLINE((shape, points) -> shape.generateOutline(points)), - SURFACE((shape, points) -> shape.generateSurface(points)), - FILL((shape, points) -> shape.generateFilled(points)); - - private final BiConsumer> generatePoints; + PointSampler getPointSampler(); + void setPointSampler(PointSampler sampler); - Style(BiConsumer> generatePoints) { - this.generatePoints = generatePoints; - } + // --- Replication --- - public void generatePoints(Shape shape, Set points) { - generatePoints.accept(shape, points); - } - - public String toString() { - return name().toLowerCase(); - } - } - - /** - * A state object for checking if a shape has changed. - */ - record State(Style style, int orientationHash, double scale, int offsetHash, double particleDensity) { - public boolean equals(State state) { - return state.style() == style && - state.orientationHash() == orientationHash && - state.scale() == scale && - state.offsetHash() == offsetHash && - state.particleDensity() == particleDensity; - } - } + Shape clone(); + Shape copyTo(Shape target); } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Sphere.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Sphere.java index df384c3..b1b9bb6 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Sphere.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Sphere.java @@ -1,5 +1,6 @@ package com.sovdee.shapes.shapes; +import com.sovdee.shapes.sampling.SamplingStyle; import org.joml.Vector3d; import java.util.LinkedHashSet; @@ -27,7 +28,7 @@ public Sphere(double radius) { this.radius = Math.max(radius, Shape.EPSILON); this.cutoffAngle = Math.PI; this.cutoffAngleCos = -1.0; - this.setStyle(Style.SURFACE); + this.getPointSampler().setStyle(SamplingStyle.SURFACE); } // --- Static calculation methods --- @@ -69,38 +70,39 @@ private static Set calculateFibonacciSphere(int pointCount, double rad // --- Generation methods --- @Override - public void generateOutline(Set points) { - this.generateSurface(points); + public void generateOutline(Set points, double density) { + this.generateSurface(points, density); } @Override - public void generateSurface(Set points) { - double particleDensity = this.getParticleDensity(); - int pointCount = 4 * (int) (Math.PI * radius * radius / (particleDensity * particleDensity)); + public void generateSurface(Set points, double density) { + int pointCount = 4 * (int) (Math.PI * radius * radius / (density * density)); points.addAll(calculateFibonacciSphere(pointCount, radius, cutoffAngle)); } @Override - public void generateFilled(Set points) { - double particleDensity = this.getParticleDensity(); - int subSpheres = (int) (radius / particleDensity) - 1; + public void generateFilled(Set points, double density) { + int subSpheres = (int) (radius / density) - 1; double radiusStep = radius / subSpheres; for (int i = 1; i < subSpheres; i++) { double subRadius = i * radiusStep; - int pointCount = 4 * (int) (Math.PI * subRadius * subRadius / (particleDensity * particleDensity)); + int pointCount = 4 * (int) (Math.PI * subRadius * subRadius / (density * density)); points.addAll(calculateFibonacciSphere(pointCount, subRadius, cutoffAngle)); } } @Override - public void setParticleCount(int particleCount) { - particleCount = Math.max(particleCount, 1); - this.setParticleDensity(switch (this.getStyle()) { - case OUTLINE, SURFACE -> Math.sqrt(2 * Math.PI * radius * radius * (1 - cutoffAngleCos) / particleCount); - case FILL -> - Math.cbrt(Math.PI / 3 * radius * radius * radius * (2 + cutoffAngleCos) * (1 - cutoffAngleCos) * (1 - cutoffAngleCos) / particleCount); - }); - this.setNeedsUpdate(true); + public double computeDensity(SamplingStyle style, int targetPointCount) { + int count = Math.max(targetPointCount, 1); + return switch (style) { + case OUTLINE, SURFACE -> Math.sqrt(2 * Math.PI * radius * radius * (1 - cutoffAngleCos) / count); + case FILL -> Math.cbrt(Math.PI / 3 * radius * radius * radius * (2 + cutoffAngleCos) * (1 - cutoffAngleCos) * (1 - cutoffAngleCos) / count); + }; + } + + @Override + public boolean contains(Vector3d point) { + return point.x * point.x + point.y * point.y + point.z * point.z <= radius * radius; } @Override @@ -109,7 +111,7 @@ public void setParticleCount(int particleCount) { @Override public void setRadius(double radius) { this.radius = Math.max(radius, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } @Override @@ -118,6 +120,6 @@ public Shape clone() { } public String toString() { - return this.getStyle() + " sphere with radius " + this.radius; + return this.getPointSampler().getStyle() + " sphere with radius " + this.radius; } } diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/SphericalCap.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/SphericalCap.java index 45b3bc1..fc290a0 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/SphericalCap.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/SphericalCap.java @@ -1,5 +1,7 @@ package com.sovdee.shapes.shapes; +import org.joml.Vector3d; + public class SphericalCap extends Sphere implements CutoffShape { public SphericalCap(double radius, double cutoffAngle) { @@ -17,7 +19,15 @@ public double getCutoffAngle() { public void setCutoffAngle(double cutoffAngle) { this.cutoffAngle = Math.clamp(cutoffAngle, 0, Math.PI); this.cutoffAngleCos = Math.cos(this.cutoffAngle); - this.setNeedsUpdate(true); + invalidate(); + } + + @Override + public boolean contains(Vector3d point) { + if (!super.contains(point)) return false; + // Check if within the cap's polar angle + double y = point.y / getRadius(); + return y >= cutoffAngleCos; } @Override diff --git a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Star.java b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Star.java index b6633ee..c0a262d 100644 --- a/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Star.java +++ b/shapes-lib/src/main/java/com/sovdee/shapes/shapes/Star.java @@ -1,5 +1,6 @@ package com.sovdee.shapes.shapes; +import com.sovdee.shapes.sampling.SamplingStyle; import com.sovdee.shapes.util.VectorUtil; import org.joml.Vector3d; @@ -19,52 +20,59 @@ public Star(double innerRadius, double outerRadius, double angle) { this.angle = Math.clamp(angle, Shape.EPSILON, Math.PI); } - private static Set calculateStar(double innerRadius, double outerRadius, double angle, double particleDensity) { + private static Set calculateStar(double innerRadius, double outerRadius, double angle, double density) { Set points = new LinkedHashSet<>(); Vector3d outerVertex = new Vector3d(outerRadius, 0, 0); Vector3d innerVertex = new Vector3d(innerRadius, 0, 0); for (double theta = 0; theta < 2 * Math.PI; theta += angle) { Vector3d currentVertex = VectorUtil.rotateAroundY(new Vector3d(outerVertex), theta); - points.addAll(Line.calculateLine(currentVertex, VectorUtil.rotateAroundY(new Vector3d(innerVertex), theta + angle / 2), particleDensity)); - points.addAll(Line.calculateLine(currentVertex, VectorUtil.rotateAroundY(new Vector3d(innerVertex), theta - angle / 2), particleDensity)); + points.addAll(Line.calculateLine(currentVertex, VectorUtil.rotateAroundY(new Vector3d(innerVertex), theta + angle / 2), density)); + points.addAll(Line.calculateLine(currentVertex, VectorUtil.rotateAroundY(new Vector3d(innerVertex), theta - angle / 2), density)); } return points; } @Override - public void generateOutline(Set points) { - points.addAll(calculateStar(innerRadius, outerRadius, angle, this.getParticleDensity())); + public void generateOutline(Set points, double density) { + points.addAll(calculateStar(innerRadius, outerRadius, angle, density)); } @Override - public void generateSurface(Set points) { + public void generateSurface(Set points, double density) { double minRadius = Math.min(innerRadius, outerRadius); - double particleDensity = this.getParticleDensity(); - for (double r = 0; r < minRadius; r += particleDensity) { - points.addAll(calculateStar(innerRadius - r, outerRadius - r, angle, particleDensity)); + for (double r = 0; r < minRadius; r += density) { + points.addAll(calculateStar(innerRadius - r, outerRadius - r, angle, density)); } } @Override - public void setParticleCount(int particleCount) { - particleCount = Math.max(particleCount, 1); + public double computeDensity(SamplingStyle style, int targetPointCount) { + int count = Math.max(targetPointCount, 1); double sideLength = Math.sqrt(Math.pow(innerRadius, 2) + Math.pow(outerRadius, 2) - 2 * innerRadius * outerRadius * Math.cos(angle)); double perimeter = sideLength * getStarPoints() * 2; - this.setParticleDensity(perimeter / particleCount); + return perimeter / count; + } + + @Override + public boolean contains(Vector3d point) { + if (Math.abs(point.y) > EPSILON) return false; + // Check if within outer radius bounding circle + double dist = Math.sqrt(point.x * point.x + point.z * point.z); + return dist <= Math.max(innerRadius, outerRadius); } public double getInnerRadius() { return innerRadius; } public void setInnerRadius(double innerRadius) { this.innerRadius = Math.max(innerRadius, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } public double getOuterRadius() { return outerRadius; } public void setOuterRadius(double outerRadius) { this.outerRadius = Math.max(outerRadius, Shape.EPSILON); - this.setNeedsUpdate(true); + invalidate(); } public int getStarPoints() { @@ -74,7 +82,7 @@ public int getStarPoints() { public void setStarPoints(int starPoints) { starPoints = Math.max(starPoints, 2); this.angle = Math.PI * 2 / starPoints; - this.setNeedsUpdate(true); + invalidate(); } @Override diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffSetOrdering.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffSetOrdering.java index a2b5c7e..c22004b 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffSetOrdering.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/effects/EffSetOrdering.java @@ -58,7 +58,7 @@ protected void execute(Event event) { default -> null; }; for (Shape shape : shapes.getArray(event)) { - shape.setOrdering(order); + shape.getPointSampler().setOrdering(order); } } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprArc.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprArc.java index 3629f64..b137d0c 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprArc.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprArc.java @@ -13,7 +13,7 @@ import ch.njol.util.Kleenean; import com.sovdee.shapes.shapes.Arc; import com.sovdee.shapes.shapes.Shape; -import com.sovdee.shapes.shapes.Shape.Style; +import com.sovdee.shapes.sampling.SamplingStyle; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; @@ -97,8 +97,8 @@ protected Shape[] get(Event event) { Arc shape = new Arc(radius.doubleValue(), height.doubleValue(), angle.doubleValue()); if (isSector) - shape.setStyle(Style.FILL); - shape.setDrawContext(new DrawData()); + shape.getPointSampler().setStyle(SamplingStyle.FILL); + shape.getPointSampler().setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprBezierCurve.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprBezierCurve.java index d06629c..5816811 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprBezierCurve.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprBezierCurve.java @@ -78,7 +78,7 @@ public boolean init(Expression[] exprs, int matchedPattern, @NonNull Kleenean result.add(VectorConversion.toJOML(endPt.getVector(origin))); return result; }); - curve.setDrawContext(new DrawData()); + curve.getPointSampler().setDrawContext(new DrawData()); return curve; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCircle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCircle.java index efcebc8..a71f273 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCircle.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCircle.java @@ -13,7 +13,7 @@ import ch.njol.util.Kleenean; import com.sovdee.shapes.shapes.Circle; import com.sovdee.shapes.shapes.Shape; -import com.sovdee.shapes.shapes.Shape.Style; +import com.sovdee.shapes.sampling.SamplingStyle; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; @@ -40,7 +40,7 @@ public class ExprCircle extends SimpleExpression { private Expression radius; private Expression height; - private Style style; + private SamplingStyle style; private boolean isCylinder; @Override @@ -63,12 +63,12 @@ public boolean init(Expression[] exprs, int matchedPattern, @NonNull Kleenean } style = switch (parseResult.mark) { - case 0 -> Style.SURFACE; - case 1 -> Style.OUTLINE; - default -> Style.FILL; + case 0 -> SamplingStyle.SURFACE; + case 1 -> SamplingStyle.OUTLINE; + default -> SamplingStyle.FILL; }; } else { - style = parseResult.hasTag("disc") ? Style.SURFACE : Style.OUTLINE; + style = parseResult.hasTag("disc") ? SamplingStyle.SURFACE : SamplingStyle.OUTLINE; } return true; } @@ -85,8 +85,8 @@ protected Shape[] get(@NonNull Event event) { height = Math.max(height.doubleValue(), 0); Circle shape = new Circle(radius.doubleValue(), height.doubleValue()); - shape.setStyle(style); - shape.setDrawContext(new DrawData()); + shape.getPointSampler().setStyle(style); + shape.getPointSampler().setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCuboid.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCuboid.java index be82e4e..e0f0d21 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCuboid.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprCuboid.java @@ -12,7 +12,7 @@ import ch.njol.util.Kleenean; import com.sovdee.shapes.shapes.Cuboid; import com.sovdee.shapes.shapes.Shape; -import com.sovdee.shapes.shapes.Shape.Style; +import com.sovdee.shapes.sampling.SamplingStyle; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.DynamicLocation; import com.sovdee.skriptparticles.util.MathUtil; @@ -51,7 +51,7 @@ public class ExprCuboid extends SimpleExpression { private Expression corner2; private int matchedPattern = 0; - private Style style; + private SamplingStyle style; @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { @@ -68,11 +68,11 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye } this.matchedPattern = matchedPattern; if (parseResult.hasTag("hollow")) { - style = Style.SURFACE; + style = SamplingStyle.SURFACE; } else if (parseResult.hasTag("solid")) { - style = Style.FILL; + style = SamplingStyle.FILL; } else { - style = Style.OUTLINE; + style = SamplingStyle.OUTLINE; } return true; } @@ -116,8 +116,8 @@ protected Shape[] get(Event event) { ); } } - shape.setStyle(style); - shape.setDrawContext(new DrawData()); + shape.getPointSampler().setStyle(style); + shape.getPointSampler().setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipse.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipse.java index e04e24c..40f2a55 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipse.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipse.java @@ -13,7 +13,7 @@ import ch.njol.util.Kleenean; import com.sovdee.shapes.shapes.Ellipse; import com.sovdee.shapes.shapes.Shape; -import com.sovdee.shapes.shapes.Shape.Style; +import com.sovdee.shapes.sampling.SamplingStyle; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; @@ -43,22 +43,22 @@ public class ExprEllipse extends SimpleExpression { private Expression xRadius; private Expression zRadius; private Expression height; - private Style style; + private SamplingStyle style; @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { xRadius = (Expression) exprs[0]; zRadius = (Expression) exprs[1]; - style = Style.OUTLINE; + style = SamplingStyle.OUTLINE; if (parseResult.hasTag("surface")) { - style = Style.SURFACE; + style = SamplingStyle.SURFACE; } if (matchedPattern == 1) { height = (Expression) exprs[2]; style = switch (parseResult.mark) { - case 0 -> Style.SURFACE; - case 1 -> Style.OUTLINE; - default -> Style.FILL; + case 0 -> SamplingStyle.SURFACE; + case 1 -> SamplingStyle.OUTLINE; + default -> SamplingStyle.FILL; }; } @@ -97,8 +97,8 @@ protected Shape[] get(Event event) { height = Math.max(height.doubleValue(), 0); Ellipse shape = new Ellipse(xRadius.doubleValue(), zRadius.doubleValue(), height.doubleValue()); - shape.setStyle(style); - shape.setDrawContext(new DrawData()); + shape.getPointSampler().setStyle(style); + shape.getPointSampler().setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipsoid.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipsoid.java index c33e5bd..7a5c822 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipsoid.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipsoid.java @@ -13,7 +13,7 @@ import ch.njol.util.Kleenean; import com.sovdee.shapes.shapes.Ellipsoid; import com.sovdee.shapes.shapes.Shape; -import com.sovdee.shapes.shapes.Shape.Style; +import com.sovdee.shapes.sampling.SamplingStyle; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; @@ -44,7 +44,7 @@ public class ExprEllipsoid extends SimpleExpression { private Expression xRadius; private Expression yRadius; private Expression zRadius; - private Style style; + private SamplingStyle style; @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { @@ -70,7 +70,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye return false; } - style = (parseResult.hasTag("hollow") ? Style.SURFACE : (parseResult.hasTag("fill") ? Style.FILL : Style.OUTLINE)); + style = (parseResult.hasTag("hollow") ? SamplingStyle.SURFACE : (parseResult.hasTag("fill") ? SamplingStyle.FILL : SamplingStyle.OUTLINE)); return true; } @@ -88,8 +88,8 @@ protected Shape[] get(Event event) { zRadius = Math.max(zRadius.doubleValue(), MathUtil.EPSILON); Ellipsoid shape = new Ellipsoid(xRadius.doubleValue(), yRadius.doubleValue(), zRadius.doubleValue()); - shape.setStyle(style); - shape.setDrawContext(new DrawData()); + shape.getPointSampler().setStyle(style); + shape.getPointSampler().setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipticalArc.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipticalArc.java index f68ab22..29ef979 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipticalArc.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprEllipticalArc.java @@ -13,7 +13,7 @@ import ch.njol.util.Kleenean; import com.sovdee.shapes.shapes.EllipticalArc; import com.sovdee.shapes.shapes.Shape; -import com.sovdee.shapes.shapes.Shape.Style; +import com.sovdee.shapes.sampling.SamplingStyle; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; @@ -44,7 +44,7 @@ public class ExprEllipticalArc extends SimpleExpression { private Expression zRadius; private Expression height; private Expression angle; - private Style style; + private SamplingStyle style; private boolean isRadians; @Override @@ -86,7 +86,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye } } - style = parseResult.hasTag("sector") ? Style.FILL : Style.OUTLINE; + style = parseResult.hasTag("sector") ? SamplingStyle.FILL : SamplingStyle.OUTLINE; isRadians = parseResult.hasTag("radians"); return true; } @@ -109,8 +109,8 @@ protected Shape[] get(Event event) { angle = MathUtil.clamp(angle.doubleValue(), 0, Math.PI * 2); EllipticalArc shape = new EllipticalArc(xRadius.doubleValue(), zRadius.doubleValue(), height.doubleValue(), angle.doubleValue()); - shape.setStyle(style); - shape.setDrawContext(new DrawData()); + shape.getPointSampler().setStyle(style); + shape.getPointSampler().setDrawContext(new DrawData()); return new Shape[]{shape}; } @@ -126,7 +126,7 @@ public Class getReturnType() { @Override public String toString(@Nullable Event event, boolean debug) { - return (this.style == Style.FILL ? "sector" : "arc") + + return (this.style == SamplingStyle.FILL ? "sector" : "arc") + " with radius " + xRadius.toString(event, debug) + " and " + zRadius.toString(event, debug) + (height == null ? "" : " and height " + height.toString(event, debug)) + " and angle " + angle.toString(event, debug) + diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHeart.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHeart.java index d315ae3..c807907 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHeart.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHeart.java @@ -13,7 +13,7 @@ import ch.njol.util.Kleenean; import com.sovdee.shapes.shapes.Heart; import com.sovdee.shapes.shapes.Shape; -import com.sovdee.shapes.shapes.Shape.Style; +import com.sovdee.shapes.sampling.SamplingStyle; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; @@ -86,9 +86,9 @@ protected Shape[] get(Event event) { Heart shape = new Heart(width.doubleValue(), length.doubleValue(), eccentricity.doubleValue()); if (isSolid) { - shape.setStyle(Style.SURFACE); + shape.getPointSampler().setStyle(SamplingStyle.SURFACE); } - shape.setDrawContext(new DrawData()); + shape.getPointSampler().setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHelix.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHelix.java index 85d4a8a..83c8cae 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHelix.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprHelix.java @@ -13,7 +13,7 @@ import ch.njol.util.Kleenean; import com.sovdee.shapes.shapes.Helix; import com.sovdee.shapes.shapes.Shape; -import com.sovdee.shapes.shapes.Shape.Style; +import com.sovdee.shapes.sampling.SamplingStyle; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; @@ -43,7 +43,7 @@ public class ExprHelix extends SimpleExpression { @Nullable private Expression windingRate; private boolean isClockwise = true; - private Style style; + private SamplingStyle style; @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { @@ -52,7 +52,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye if (exprs.length > 2) windingRate = (Expression) exprs[2]; isClockwise = parseResult.mark == 0; - style = parseResult.hasTag("solid") ? Style.SURFACE : Style.OUTLINE; + style = parseResult.hasTag("solid") ? SamplingStyle.SURFACE : SamplingStyle.OUTLINE; if (radius instanceof Literal literal && literal.getSingle().doubleValue() <= 0) { Skript.error("The radius of a helix must be greater than 0. (radius: " + @@ -89,8 +89,8 @@ protected Shape[] get(Event event) { double slope = 1.0 / Math.max(windingRate.doubleValue(), MathUtil.EPSILON); int direction = isClockwise ? 1 : -1; Helix shape = new Helix(radius.doubleValue(), height.doubleValue(), slope / (2 * Math.PI), direction); - shape.setStyle(style); - shape.setDrawContext(new DrawData()); + shape.getPointSampler().setStyle(style); + shape.getPointSampler().setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprIrregularPolygon.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprIrregularPolygon.java index 58cd16c..864fdc0 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprIrregularPolygon.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprIrregularPolygon.java @@ -87,7 +87,7 @@ protected Shape[] get(Event event) { } else { shape = new IrregularPolygon(vertices); } - shape.setDrawContext(new DrawData()); + shape.getPointSampler().setDrawContext(new DrawData()); if (locationOffset != null) { DrawData.of(shape).setLocation(new DynamicLocation((Location) points[0])); } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprLine.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprLine.java index 3b0a7cd..0b9ba2d 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprLine.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprLine.java @@ -89,7 +89,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye } private Shape attachDrawData(Shape shape) { - shape.setDrawContext(new DrawData()); + shape.getPointSampler().setDrawContext(new DrawData()); return shape; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRectangle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRectangle.java index cf79587..6bebf24 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRectangle.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRectangle.java @@ -13,7 +13,7 @@ import com.sovdee.shapes.shapes.Rectangle; import com.sovdee.shapes.shapes.Rectangle.Plane; import com.sovdee.shapes.shapes.Shape; -import com.sovdee.shapes.shapes.Shape.Style; +import com.sovdee.shapes.sampling.SamplingStyle; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.DynamicLocation; import com.sovdee.skriptparticles.util.MathUtil; @@ -51,7 +51,7 @@ public class ExprRectangle extends SimpleExpression { private Expression lengthExpr; private Expression widthExpr; - private Style style; + private SamplingStyle style; private Expression corner1Expr; private Expression corner2Expr; private int matchedPattern; @@ -59,7 +59,7 @@ public class ExprRectangle extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { - style = parseResult.hasTag("solid") ? Style.SURFACE : Style.OUTLINE; + style = parseResult.hasTag("solid") ? SamplingStyle.SURFACE : SamplingStyle.OUTLINE; this.matchedPattern = matchedPattern; if (matchedPattern == 0) { lengthExpr = (Expression) exprs[0]; @@ -108,8 +108,8 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye ); } } - shape.setStyle(style); - shape.setDrawContext(new DrawData()); + shape.getPointSampler().setStyle(style); + shape.getPointSampler().setDrawContext(new DrawData()); return new Shape[]{shape}; } @@ -125,7 +125,7 @@ public Class getReturnType() { @Override public String toString(@Nullable Event event, boolean debug) { - return (style == Style.SURFACE ? "solid " : "") + + return (style == SamplingStyle.SURFACE ? "solid " : "") + switch (plane) { case XZ -> " xz "; case XY -> " xy "; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolygon.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolygon.java index 44fcd12..5254a76 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolygon.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolygon.java @@ -13,7 +13,7 @@ import ch.njol.util.Kleenean; import com.sovdee.shapes.shapes.RegularPolygon; import com.sovdee.shapes.shapes.Shape; -import com.sovdee.shapes.shapes.Shape.Style; +import com.sovdee.shapes.sampling.SamplingStyle; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; @@ -45,12 +45,12 @@ public class ExprRegularPolygon extends SimpleExpression { private Expression radius; private Expression sideLength; private Expression sides; - private Style style; + private SamplingStyle style; private int matchedPattern; @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - style = parseResult.hasTag("solid") ? Style.SURFACE : Style.OUTLINE; + style = parseResult.hasTag("solid") ? SamplingStyle.SURFACE : SamplingStyle.OUTLINE; this.matchedPattern = matchedPattern; switch (matchedPattern) { case 0 -> { @@ -112,8 +112,8 @@ protected Shape[] get(Event event) { sides = Math.max(sides.intValue(), 3); radius = Math.max(radius.doubleValue(), MathUtil.EPSILON); RegularPolygon shape = new RegularPolygon(sides.intValue(), radius.doubleValue()); - shape.setStyle(style); - shape.setDrawContext(new DrawData()); + shape.getPointSampler().setStyle(style); + shape.getPointSampler().setDrawContext(new DrawData()); return new Shape[]{shape}; } @@ -129,7 +129,7 @@ public Class getReturnType() { @Override public String toString(@Nullable Event event, boolean debug) { - return (style == Style.SURFACE ? "filled" : "outlined") + + return (style == SamplingStyle.SURFACE ? "filled" : "outlined") + switch (matchedPattern) { case 0, 2 -> " regular polygon with " + sides.toString(event, debug) + " sides and radius " + radius.toString(event, debug); diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolyhedron.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolyhedron.java index 24c9c39..a00c78f 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolyhedron.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprRegularPolyhedron.java @@ -12,7 +12,7 @@ import ch.njol.util.Kleenean; import com.sovdee.shapes.shapes.RegularPolyhedron; import com.sovdee.shapes.shapes.Shape; -import com.sovdee.shapes.shapes.Shape.Style; +import com.sovdee.shapes.sampling.SamplingStyle; import com.sovdee.skriptparticles.shapes.DrawData; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -37,13 +37,13 @@ public class ExprRegularPolyhedron extends SimpleExpression { private Expression radius; private int faces; - private Style style; + private SamplingStyle style; @Override public boolean init(Expression[] expressions, int i, Kleenean kleenean, SkriptParser.ParseResult parseResult) { radius = (Expression) expressions[0]; faces = parseResult.hasTag("tetra") ? 4 : parseResult.hasTag("octa") ? 8 : parseResult.hasTag("dodeca") ? 12 : 20; - style = parseResult.hasTag("hollow") ? Style.SURFACE : parseResult.hasTag("solid") ? Style.FILL : Style.OUTLINE; + style = parseResult.hasTag("hollow") ? SamplingStyle.SURFACE : parseResult.hasTag("solid") ? SamplingStyle.FILL : SamplingStyle.OUTLINE; if (radius instanceof Literal literal && literal.getSingle().doubleValue() <= 0) { Skript.error("The radius of the polyhedron must be greater than 0. (radius: " + @@ -59,8 +59,8 @@ public boolean init(Expression[] expressions, int i, Kleenean kleenean, Skrip if (radius.getSingle(event) == null) return new Shape[0]; RegularPolyhedron shape = new RegularPolyhedron(radius.getSingle(event).doubleValue(), faces); - shape.setStyle(style); - shape.setDrawContext(new DrawData()); + shape.getPointSampler().setStyle(style); + shape.getPointSampler().setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphere.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphere.java index 69956d8..7de571a 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphere.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphere.java @@ -12,7 +12,7 @@ import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; import com.sovdee.shapes.shapes.Shape; -import com.sovdee.shapes.shapes.Shape.Style; +import com.sovdee.shapes.sampling.SamplingStyle; import com.sovdee.shapes.shapes.Sphere; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; @@ -59,8 +59,8 @@ protected Shape[] get(@NotNull Event event) { radius = Math.max(radius.doubleValue(), MathUtil.EPSILON); Sphere shape = new Sphere(radius.doubleValue()); - if (isSolid) shape.setStyle(Style.FILL); - shape.setDrawContext(new DrawData()); + if (isSolid) shape.getPointSampler().setStyle(SamplingStyle.FILL); + shape.getPointSampler().setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphericalCap.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphericalCap.java index ff8f3d4..81f38a9 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphericalCap.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprSphericalCap.java @@ -12,7 +12,7 @@ import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; import com.sovdee.shapes.shapes.Shape; -import com.sovdee.shapes.shapes.Shape.Style; +import com.sovdee.shapes.sampling.SamplingStyle; import com.sovdee.shapes.shapes.SphericalCap; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; @@ -79,8 +79,8 @@ protected Shape[] get(Event event) { SphericalCap shape = new SphericalCap(radius.doubleValue(), angle.doubleValue()); if (isSector) - shape.setStyle(Style.FILL); - shape.setDrawContext(new DrawData()); + shape.getPointSampler().setStyle(SamplingStyle.FILL); + shape.getPointSampler().setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprStar.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprStar.java index c0cb3ec..4772343 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprStar.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/constructors/ExprStar.java @@ -11,7 +11,7 @@ import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; import com.sovdee.shapes.shapes.Shape; -import com.sovdee.shapes.shapes.Shape.Style; +import com.sovdee.shapes.sampling.SamplingStyle; import com.sovdee.shapes.shapes.Star; import com.sovdee.skriptparticles.shapes.DrawData; import com.sovdee.skriptparticles.util.MathUtil; @@ -79,8 +79,8 @@ protected Shape[] get(Event event) { Star shape = new Star(innerRadius.doubleValue(), outerRadius.doubleValue(), angle); if (isSolid) - shape.setStyle(Style.SURFACE); - shape.setDrawContext(new DrawData()); + shape.getPointSampler().setStyle(SamplingStyle.SURFACE); + shape.getPointSampler().setDrawContext(new DrawData()); return new Shape[]{shape}; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLocations.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLocations.java index ee6191b..72b56f2 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLocations.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLocations.java @@ -62,7 +62,7 @@ protected Location[] get(Event event) { ArrayList locations = new ArrayList<>(); for (Shape shape : shapes) { - locations.addAll(VectorConversion.toBukkit(shape.getPoints()).stream().map(point -> center.clone().add(point)).toList()); + locations.addAll(VectorConversion.toBukkit(shape.getPointSampler().getPoints(shape)).stream().map(point -> center.clone().add(point)).toList()); } return locations.toArray(new Location[0]); } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeNormal.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeNormal.java index 2f23b22..96f4b97 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeNormal.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeNormal.java @@ -35,7 +35,7 @@ public class ExprShapeNormal extends SimplePropertyExpression { @Override public Vector convert(Shape shape) { - return VectorConversion.toBukkit(shape.getRelativeYAxis(false)); + return VectorConversion.toBukkit(shape.getRelativeYAxis()); } @Override diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticleDensity.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticleDensity.java index 977c2ad..59661cc 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticleDensity.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticleDensity.java @@ -10,6 +10,7 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser; import ch.njol.util.Kleenean; +import com.sovdee.shapes.sampling.PointSampler; import com.sovdee.shapes.shapes.Shape; import com.sovdee.skriptparticles.util.MathUtil; import org.bukkit.event.Event; @@ -54,10 +55,13 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override public @Nullable Number convert(Shape shape) { + PointSampler sampler = shape.getPointSampler(); if (isDensity) - return 1 / shape.getParticleDensity(); - else - return shape.getParticleCount(); + return 1 / sampler.getDensity(); + else { + // Return approximate point count based on current density + return (int) Math.round(1 / sampler.getDensity()); + } } @Override @@ -80,12 +84,13 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { change = -change; case ADD: for (Shape shape : shapes) { + PointSampler sampler = shape.getPointSampler(); if (isDensity) { // clamp to 0.001 and 1000, enough to kill the client but not enough to cause an actual error - shape.setParticleDensity(MathUtil.clamp(1 / (1 / shape.getParticleDensity() + change), 0.001, 1000)); + sampler.setDensity(MathUtil.clamp(1 / (1 / sampler.getDensity() + change), 0.001, 1000)); } else // clamp to 1, the minimum amount of particles - shape.setParticleCount(Math.max(1, shape.getParticleCount() + (int) change)); + sampler.setParticleCount(shape, Math.max(1, (int) (1 / sampler.getDensity()) + (int) change)); } break; case DELETE: @@ -93,10 +98,11 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { case SET: change = isDensity ? MathUtil.clamp(1 / change, 0.001, 1000) : Math.max(1, (int) change); for (Shape shape : shapes) { + PointSampler sampler = shape.getPointSampler(); if (isDensity) { - shape.setParticleDensity(change); + sampler.setDensity(change); } else { - shape.setParticleCount((int) change); + sampler.setParticleCount(shape, (int) change); } } break; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapePoints.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapePoints.java index 86f1948..dff9e4a 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapePoints.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapePoints.java @@ -45,7 +45,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye protected Vector[] get(Event event, Shape[] source) { List points = new ArrayList<>(); for (Shape shape : source) { - points.addAll(VectorConversion.toBukkit(shape.getPoints())); + points.addAll(VectorConversion.toBukkit(shape.getPointSampler().getPoints(shape))); } return points.toArray(new Vector[0]); } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRelativeAxis.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRelativeAxis.java index 562cb30..cf3c0ff 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRelativeAxis.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRelativeAxis.java @@ -45,9 +45,9 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override public @Nullable Vector convert(Shape shape) { return switch (axis) { - case 0 -> VectorConversion.toBukkit(shape.getRelativeXAxis(false)); - case 1 -> VectorConversion.toBukkit(shape.getRelativeYAxis(false)); - case 2 -> VectorConversion.toBukkit(shape.getRelativeZAxis(false)); + case 0 -> VectorConversion.toBukkit(shape.getRelativeXAxis()); + case 1 -> VectorConversion.toBukkit(shape.getRelativeYAxis()); + case 2 -> VectorConversion.toBukkit(shape.getRelativeZAxis()); default -> null; }; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeStyle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeStyle.java index fdbb8c5..f90c45a 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeStyle.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeStyle.java @@ -7,6 +7,7 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.PropertyExpression; import ch.njol.skript.expressions.base.SimplePropertyExpression; +import com.sovdee.shapes.sampling.SamplingStyle; import com.sovdee.shapes.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -22,23 +23,23 @@ "set style of {_shape} to hollow" }) @Since("1.0.0") -public class ExprShapeStyle extends SimplePropertyExpression { +public class ExprShapeStyle extends SimplePropertyExpression { static { - PropertyExpression.register(ExprShapeStyle.class, Shape.Style.class, "style", "shapes"); + PropertyExpression.register(ExprShapeStyle.class, SamplingStyle.class, "style", "shapes"); } @Override @Nullable - public Shape.Style convert(Shape shape) { - return shape.getStyle(); + public SamplingStyle convert(Shape shape) { + return shape.getPointSampler().getStyle(); } @Override @Nullable public Class[] acceptChange(ChangeMode mode) { return switch (mode) { - case SET -> new Class[]{Shape.Style.class}; + case SET -> new Class[]{SamplingStyle.class}; case ADD, REMOVE, REMOVE_ALL, DELETE, RESET -> new Class[0]; }; } @@ -46,15 +47,15 @@ public Class[] acceptChange(ChangeMode mode) { @Override public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { if (delta == null || delta.length != 1) return; - Shape.Style style = (Shape.Style) delta[0]; + SamplingStyle style = (SamplingStyle) delta[0]; for (Shape shape : getExpr().getArray(event)) { - shape.setStyle(style); + shape.getPointSampler().setStyle(style); } } @Override - public Class getReturnType() { - return Shape.Style.class; + public Class getReturnType() { + return SamplingStyle.class; } @Override diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java index ef95726..ccd1977 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java @@ -4,6 +4,7 @@ import ch.njol.skript.classes.Parser; import ch.njol.skript.lang.ParseContext; import ch.njol.skript.registrations.Classes; +import com.sovdee.shapes.sampling.SamplingStyle; import com.sovdee.shapes.shapes.Shape; import org.jetbrains.annotations.Nullable; @@ -33,27 +34,27 @@ public String toString(Shape o, int flags) { @Override public String toVariableNameString(Shape shape) { - return "shape:" + shape.getUUID(); + return "shape:" + shape.getPointSampler().getUUID(); } }) .cloner(Shape::clone) ); - // Style — use library Style directly (no duplicate enum) - Classes.registerClass(new ClassInfo<>(Shape.Style.class, "shapestyle") + // Style — use standalone SamplingStyle enum + Classes.registerClass(new ClassInfo<>(SamplingStyle.class, "shapestyle") .user("shape ?styles?") .name("Shape Style") .description("Represents the way the shape is drawn. Outlined is a wireframe representation, Surface is filling in all the surfaces of the shape, and Filled is filling in the entire shape.") .parser(new Parser<>() { @Override - public @Nullable Shape.Style parse(String s, ParseContext context) { + public @Nullable SamplingStyle parse(String s, ParseContext context) { s = s.toUpperCase(); if (s.matches("OUTLINE(D)?") || s.matches("WIREFRAME")) { - return Shape.Style.OUTLINE; + return SamplingStyle.OUTLINE; } else if (s.matches("SURFACE") || s.matches("HOLLOW")) { - return Shape.Style.SURFACE; + return SamplingStyle.SURFACE; } else if (s.matches("FILL(ED)?") || s.matches("SOLID")) { - return Shape.Style.FILL; + return SamplingStyle.FILL; } return null; } @@ -64,12 +65,12 @@ public boolean canParse(ParseContext context) { } @Override - public String toString(Shape.Style style, int i) { + public String toString(SamplingStyle style, int i) { return style.toString(); } @Override - public String toVariableNameString(Shape.Style style) { + public String toVariableNameString(SamplingStyle style) { return "shapestyle:" + style; } })); diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawData.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawData.java index cc70a35..7362f9e 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawData.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawData.java @@ -1,6 +1,6 @@ package com.sovdee.skriptparticles.shapes; -import com.sovdee.shapes.DrawContext; +import com.sovdee.shapes.sampling.DrawContext; import com.sovdee.shapes.shapes.Shape; import com.sovdee.skriptparticles.particles.Particle; import com.sovdee.skriptparticles.util.DynamicLocation; @@ -27,13 +27,13 @@ public DrawData() { } /** - * Gets the DrawData attached to a shape, creating and attaching one if missing. + * Gets the DrawData attached to a shape's PointSampler, creating and attaching one if missing. */ public static DrawData of(Shape shape) { - DrawContext ctx = shape.getDrawContext(); + DrawContext ctx = shape.getPointSampler().getDrawContext(); if (ctx instanceof DrawData dd) return dd; DrawData dd = new DrawData(); - shape.setDrawContext(dd); + shape.getPointSampler().setDrawContext(dd); return dd; } diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawManager.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawManager.java index a351827..7e349e4 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawManager.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/shapes/DrawManager.java @@ -72,7 +72,7 @@ public static void draw(Shape shape, DynamicLocation location, Quaternion baseOr // Get points from library shape using the last orientation Quaterniond lastOrientationD = new Quaterniond(dd.getLastOrientation().x, dd.getLastOrientation().y, dd.getLastOrientation().z, dd.getLastOrientation().w); - Set jomlPoints = shape.getPoints(lastOrientationD); + Set jomlPoints = shape.getPointSampler().getPoints(shape, lastOrientationD); Collection toDraw = VectorConversion.toBukkit(jomlPoints); long animationDuration = dd.getAnimationDuration(); From 0efb4f441ed673f225927ea853359326d533ad05 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Sun, 8 Feb 2026 01:26:42 -0800 Subject: [PATCH 6/6] re-add interface types --- .../properties/ExprShapeCutoffAngle.java | 21 ++---- .../expressions/properties/ExprShapeLWH.java | 39 ++++------ .../properties/ExprShapeRadius.java | 23 +++--- .../properties/ExprShapeSideLength.java | 21 ++---- .../properties/ExprShapeSides.java | 21 ++---- .../elements/types/ShapeTypes.java | 75 +++++++++++++++++++ 6 files changed, 124 insertions(+), 76 deletions(-) diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeCutoffAngle.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeCutoffAngle.java index 8ad19ff..30b1373 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeCutoffAngle.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeCutoffAngle.java @@ -7,7 +7,6 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; import com.sovdee.shapes.shapes.CutoffShape; -import com.sovdee.shapes.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -23,17 +22,15 @@ "reset {_shape}'s cutoff angle" }) @Since("1.0.0") -public class ExprShapeCutoffAngle extends SimplePropertyExpression { +public class ExprShapeCutoffAngle extends SimplePropertyExpression { static { - register(ExprShapeCutoffAngle.class, Number.class, "cutoff angle", "shapes"); + register(ExprShapeCutoffAngle.class, Number.class, "cutoff angle", "cutoffshapes"); } @Override - public @Nullable Number convert(Shape shape) { - if (shape instanceof CutoffShape cs) - return cs.getCutoffAngle(); - return null; + public @Nullable Number convert(CutoffShape shape) { + return shape.getCutoffAngle(); } @Override @@ -55,17 +52,15 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { case REMOVE: angle = -angle; case ADD: - for (Shape shape : getExpr().getArray(event)) { - if (shape instanceof CutoffShape cs) - cs.setCutoffAngle(cs.getCutoffAngle() + angle); + for (CutoffShape shape : getExpr().getArray(event)) { + shape.setCutoffAngle(shape.getCutoffAngle() + angle); } break; case DELETE: case RESET: case SET: - for (Shape shape : getExpr().getArray(event)) { - if (shape instanceof CutoffShape cs) - cs.setCutoffAngle(angle); + for (CutoffShape shape : getExpr().getArray(event)) { + shape.setCutoffAngle(angle); } break; case REMOVE_ALL: diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLWH.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLWH.java index 810af6e..f9b2d3d 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLWH.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeLWH.java @@ -10,7 +10,6 @@ import ch.njol.skript.lang.SkriptParser; import ch.njol.util.Kleenean; import com.sovdee.shapes.shapes.LWHShape; -import com.sovdee.shapes.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -27,10 +26,10 @@ "add 6 to {_shape}'s shape height" }) @Since("1.0.0") -public class ExprShapeLWH extends SimplePropertyExpression { +public class ExprShapeLWH extends SimplePropertyExpression { static { - register(ExprShapeLWH.class, Number.class, "shape (:length|:width|:height)", "shapes"); + register(ExprShapeLWH.class, Number.class, "shape (:length|:width|:height)", "lwhshapes"); } private int lwh; @@ -43,13 +42,11 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override @Nullable - public Number convert(Shape shape) { - if (!(shape instanceof LWHShape lwhShape)) - return null; + public Number convert(LWHShape shape) { return switch (lwh) { - case 0 -> lwhShape.getLength(); - case 1 -> lwhShape.getWidth(); - case 2 -> lwhShape.getHeight(); + case 0 -> shape.getLength(); + case 1 -> shape.getWidth(); + case 2 -> shape.getHeight(); default -> null; }; } @@ -73,26 +70,22 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { case REMOVE: value = -value; case ADD: - for (Shape shape : getExpr().getArray(event)) { - if (shape instanceof LWHShape lwhShape) { - switch (lwh) { - case 0 -> lwhShape.setLength(lwhShape.getLength() + value); - case 1 -> lwhShape.setWidth(lwhShape.getWidth() + value); - case 2 -> lwhShape.setHeight(lwhShape.getHeight() + value); - } + for (LWHShape shape : getExpr().getArray(event)) { + switch (lwh) { + case 0 -> shape.setLength(shape.getLength() + value); + case 1 -> shape.setWidth(shape.getWidth() + value); + case 2 -> shape.setHeight(shape.getHeight() + value); } } break; case DELETE: case RESET: case SET: - for (Shape shape : getExpr().getArray(event)) { - if (shape instanceof LWHShape lwhShape) { - switch (lwh) { - case 0 -> lwhShape.setLength(value); - case 1 -> lwhShape.setWidth(value); - case 2 -> lwhShape.setHeight(value); - } + for (LWHShape shape : getExpr().getArray(event)) { + switch (lwh) { + case 0 -> shape.setLength(value); + case 1 -> shape.setWidth(value); + case 2 -> shape.setHeight(value); } } break; diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRadius.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRadius.java index d0fca8b..53e633e 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRadius.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeRadius.java @@ -8,7 +8,6 @@ import ch.njol.skript.expressions.base.PropertyExpression; import ch.njol.skript.expressions.base.SimplePropertyExpression; import com.sovdee.shapes.shapes.RadialShape; -import com.sovdee.shapes.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -24,18 +23,16 @@ "reset {_shape}'s radius" }) @Since("1.0.0") -public class ExprShapeRadius extends SimplePropertyExpression { +public class ExprShapeRadius extends SimplePropertyExpression { static { - PropertyExpression.register(ExprShapeRadius.class, Number.class, "radius", "shapes"); + PropertyExpression.register(ExprShapeRadius.class, Number.class, "radius", "radialshapes"); } @Override @Nullable - public Number convert(Shape shape) { - if (shape instanceof RadialShape rs) - return rs.getRadius(); - return null; + public Number convert(RadialShape shape) { + return shape.getRadius(); } @Override @@ -49,7 +46,7 @@ public Class[] acceptChange(ChangeMode mode) { @Override public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { - Shape[] shapes = getExpr().getArray(event); + RadialShape[] shapes = getExpr().getArray(event); if (shapes.length == 0) return; @@ -58,18 +55,16 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { case REMOVE: deltaValue = -deltaValue; case ADD: - for (Shape shape : shapes) { - if (shape instanceof RadialShape rs) - rs.setRadius(Math.max(0.001, rs.getRadius() + deltaValue)); + for (RadialShape shape : shapes) { + shape.setRadius(Math.max(0.001, shape.getRadius() + deltaValue)); } break; case RESET: case DELETE: case SET: deltaValue = Math.max(0.001, deltaValue); - for (Shape shape : shapes) { - if (shape instanceof RadialShape rs) - rs.setRadius(deltaValue); + for (RadialShape shape : shapes) { + shape.setRadius(deltaValue); } break; case REMOVE_ALL: diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSideLength.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSideLength.java index 552bc4f..7f7c56e 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSideLength.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSideLength.java @@ -7,7 +7,6 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; import com.sovdee.shapes.shapes.PolyShape; -import com.sovdee.shapes.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -24,18 +23,16 @@ "send side length of {_shape}" }) @Since("1.0.0") -public class ExprShapeSideLength extends SimplePropertyExpression { +public class ExprShapeSideLength extends SimplePropertyExpression { static { - register(ExprShapeSideLength.class, Number.class, "side length", "shapes"); + register(ExprShapeSideLength.class, Number.class, "side length", "polyshapes"); } @Override @Nullable - public Number convert(Shape shape) { - if (shape instanceof PolyShape ps) - return ps.getSideLength(); - return null; + public Number convert(PolyShape shape) { + return shape.getSideLength(); } @Override @@ -55,18 +52,16 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { case REMOVE: change = -change; case ADD: - for (Shape shape : getExpr().getArray(event)) { - if (shape instanceof PolyShape ps) - ps.setSideLength(Math.max(0.001, ps.getSideLength() + change)); + for (PolyShape shape : getExpr().getArray(event)) { + shape.setSideLength(Math.max(0.001, shape.getSideLength() + change)); } break; case RESET: case DELETE: case SET: change = Math.max(0.001, change); - for (Shape shape : getExpr().getArray(event)) { - if (shape instanceof PolyShape ps) - ps.setSideLength(change); + for (PolyShape shape : getExpr().getArray(event)) { + shape.setSideLength(change); } break; case REMOVE_ALL: diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSides.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSides.java index 01cb659..f93736a 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSides.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeSides.java @@ -7,7 +7,6 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; import com.sovdee.shapes.shapes.PolyShape; -import com.sovdee.shapes.shapes.Shape; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -24,18 +23,16 @@ "send sides of {_shape}" }) @Since("1.0.0") -public class ExprShapeSides extends SimplePropertyExpression { +public class ExprShapeSides extends SimplePropertyExpression { static { - register(ExprShapeSides.class, Integer.class, "side(s| count)", "shapes"); + register(ExprShapeSides.class, Integer.class, "side(s| count)", "polyshapes"); } @Override @Nullable - public Integer convert(Shape shape) { - if (shape instanceof PolyShape ps) - return ps.getSides(); - return null; + public Integer convert(PolyShape shape) { + return shape.getSides(); } @Override @@ -55,18 +52,16 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { case REMOVE: change = -change; case ADD: - for (Shape shape : getExpr().getArray(event)) { - if (shape instanceof PolyShape ps) - ps.setSides(Math.max(3, ps.getSides() + change)); + for (PolyShape shape : getExpr().getArray(event)) { + shape.setSides(Math.max(3, shape.getSides() + change)); } break; case RESET: case DELETE: case SET: change = Math.max(3, change); - for (Shape shape : getExpr().getArray(event)) { - if (shape instanceof PolyShape ps) - ps.setSides(change); + for (PolyShape shape : getExpr().getArray(event)) { + shape.setSides(change); } break; case REMOVE_ALL: diff --git a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java index ccd1977..57fecf5 100644 --- a/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java +++ b/skript-particle/src/main/java/com/sovdee/skriptparticles/elements/types/ShapeTypes.java @@ -5,6 +5,10 @@ import ch.njol.skript.lang.ParseContext; import ch.njol.skript.registrations.Classes; import com.sovdee.shapes.sampling.SamplingStyle; +import com.sovdee.shapes.shapes.CutoffShape; +import com.sovdee.shapes.shapes.LWHShape; +import com.sovdee.shapes.shapes.PolyShape; +import com.sovdee.shapes.shapes.RadialShape; import com.sovdee.shapes.shapes.Shape; import org.jetbrains.annotations.Nullable; @@ -40,6 +44,77 @@ public String toVariableNameString(Shape shape) { .cloner(Shape::clone) ); + // RadialShape + Classes.registerClass(new ClassInfo<>(RadialShape.class, "radialshape") + .user("radial ?shapes?") + .name("Radial Shape") + .description("Represents an abstract particle shape that has a radius. E.g. circle, sphere, etc.") + .parser(new Parser<>() { + @Override + public RadialShape parse(String input, ParseContext context) { return null; } + @Override + public boolean canParse(ParseContext context) { return false; } + @Override + public String toString(RadialShape o, int flags) { return o.toString(); } + @Override + public String toVariableNameString(RadialShape shape) { return "shape:" + shape.getPointSampler().getUUID(); } + }) + ); + + // LWHShape + Classes.registerClass(new ClassInfo<>(LWHShape.class, "lwhshape") + .user("lwh ?shapes?") + .name("Length/Width/Height Shape") + .description("Represents an abstract particle shape that has a length, width, and/or height. E.g. cube, cylinder, ellipse, etc.") + .parser(new Parser<>() { + @Override + public LWHShape parse(String input, ParseContext context) { return null; } + @Override + public boolean canParse(ParseContext context) { return false; } + @Override + public String toString(LWHShape o, int flags) { return o.toString(); } + @Override + public String toVariableNameString(LWHShape shape) { return "shape:" + shape.getPointSampler().getUUID(); } + }) + ); + + // CutoffShape + Classes.registerClass(new ClassInfo<>(CutoffShape.class, "cutoffshape") + .user("cutoff ?shapes?") + .name("Cutoff Shape") + .description("Represents an abstract particle shape that has a cutoff angle. E.g. arc, spherical cap, etc.") + .parser(new Parser<>() { + @Override + public CutoffShape parse(String input, ParseContext context) { return null; } + @Override + public boolean canParse(ParseContext context) { return false; } + @Override + public String toString(CutoffShape o, int flags) { return o.toString(); } + @Override + public String toVariableNameString(CutoffShape shape) { return "shape:" + shape.getPointSampler().getUUID(); } + }) + ); + + // PolyShape + Classes.registerClass(new ClassInfo<>(PolyShape.class, "polyshape") + .user("poly ?shapes?") + .name("Polygonal/Polyhedral Shape") + .description( + "Represents an abstract particle shape that is a polygon or polyhedron, with a side length and side count.\n" + + "Irregular shapes are included in this category, but do not support changing either side count or side length." + ) + .parser(new Parser<>() { + @Override + public PolyShape parse(String input, ParseContext context) { return null; } + @Override + public boolean canParse(ParseContext context) { return false; } + @Override + public String toString(PolyShape o, int flags) { return o.toString(); } + @Override + public String toVariableNameString(PolyShape shape) { return "shape:" + shape.getPointSampler().getUUID(); } + }) + ); + // Style — use standalone SamplingStyle enum Classes.registerClass(new ClassInfo<>(SamplingStyle.class, "shapestyle") .user("shape ?styles?")