DREAMFIRE Docs ← Back to site
Loading...
Searching...
No Matches
DreamLocation.java
Go to the documentation of this file.
1/*
2 * MIT License
3 *
4 * Copyright (c) 2025 Dreamfire Studio
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24package com.dreamfirestudios.dreamcore.DreamLocation;
25
26import org.bukkit.Location;
27import org.bukkit.World;
28import org.bukkit.block.Block;
29import org.bukkit.util.Vector;
30
31import java.util.ArrayList;
32import java.util.List;
33import java.util.Locale;
34import java.util.Objects;
35
52public final class DreamLocation {
53
54 private DreamLocation() {}
55
56 // ---------------------------------------------------------------------
57 // CLOSEST
58 // ---------------------------------------------------------------------
59
74 public static Location ReturnClosestLocation(List<Location> locations, Location originLocation) {
75 return closestLocation(locations, originLocation);
76 }
77
78 public static Location closestLocation(List<Location> locations, Location originLocation) {
79 if (locations == null || locations.isEmpty())
80 throw new IllegalArgumentException("Locations list cannot be null or empty.");
81 if (originLocation == null || originLocation.getWorld() == null)
82 throw new IllegalArgumentException("Origin location/world cannot be null.");
83
84 final World world = originLocation.getWorld();
85 double best = Double.POSITIVE_INFINITY;
86 Location bestLoc = null;
87
88 for (Location candidate : locations) {
89 if (candidate == null || candidate.getWorld() == null) continue;
90 if (!world.equals(candidate.getWorld())) continue;
91
92 double d2 = originLocation.distanceSquared(candidate);
93 if (d2 < best) {
94 best = d2;
95 bestLoc = candidate;
96 if (best == 0.0) break;
97 }
98 }
99 return bestLoc;
100 }
101
102 // ---------------------------------------------------------------------
103 // MIDPOINT
104 // ---------------------------------------------------------------------
105
119 public static Location FindMidPointBetween2Locations(Location a, Location b) {
120 return midpoint(a, b);
121 }
122
123 public static Location midpoint(Location a, Location b) {
124 if (a == null || b == null) throw new IllegalArgumentException("Both locations must not be null.");
125 if (!Objects.equals(a.getWorld(), b.getWorld())) return null;
126 return new Location(
127 a.getWorld(),
128 (a.getX() + b.getX()) * 0.5,
129 (a.getY() + b.getY()) * 0.5,
130 (a.getZ() + b.getZ()) * 0.5
131 );
132 }
133
134 // ---------------------------------------------------------------------
135 // BETWEEN
136 // ---------------------------------------------------------------------
137
154 public static boolean IsBlockBetweenLocations(Location location1, Location location2, Block block) {
155 return isBlockBetween(location1, location2, block);
156 }
157
158 public static boolean isBlockBetween(Location location1, Location location2, Block block) {
159 if (location1 == null || location2 == null || block == null)
160 throw new IllegalArgumentException("Locations and block must not be null.");
161 if (location1.getWorld() == null || location2.getWorld() == null || block.getWorld() == null)
162 return false;
163 if (!location1.getWorld().equals(location2.getWorld())) return false;
164 if (!block.getWorld().equals(location1.getWorld())) return false;
165
166 int minX = Math.min(location1.getBlockX(), location2.getBlockX());
167 int maxX = Math.max(location1.getBlockX(), location2.getBlockX());
168 int minY = Math.min(location1.getBlockY(), location2.getBlockY());
169 int maxY = Math.max(location1.getBlockY(), location2.getBlockY());
170 int minZ = Math.min(location1.getBlockZ(), location2.getBlockZ());
171 int maxZ = Math.max(location1.getBlockZ(), location2.getBlockZ());
172
173 int bx = block.getX(), by = block.getY(), bz = block.getZ();
174 return bx >= minX && bx <= maxX
175 && by >= minY && by <= maxY
176 && bz >= minZ && bz <= maxZ;
177 }
178
179 // ---------------------------------------------------------------------
180 // POINTS BETWEEN TWO LOCATIONS
181 // ---------------------------------------------------------------------
182
198 public static Location[] ReturnAllLocationsBetweenTwoLocations(Location start, Location end, double spacing) {
199 return pointsBetween(start, end, spacing);
200 }
201
202 public static Location[] pointsBetween(Location start, Location end, double spacing) {
203 if (start == null || end == null) throw new IllegalArgumentException("Start/end must not be null.");
204 if (start.getWorld() == null || end.getWorld() == null)
205 throw new IllegalArgumentException("Start/end worlds must not be null.");
206 if (!start.getWorld().equals(end.getWorld()))
207 throw new IllegalArgumentException("Start/end must be in the same world.");
208 if (spacing <= 0) throw new IllegalArgumentException("Spacing must be greater than zero.");
209
210 double distance = start.distance(end);
211 if (distance == 0.0) {
212 return new Location[]{start.clone()};
213 }
214
215 int segments = Math.max(1, (int) Math.floor(distance / spacing));
216 int points = segments + 1;
217
218 Vector step = end.toVector().subtract(start.toVector()).multiply(1.0 / segments);
219
220 Location[] out = new Location[points];
221 for (int i = 0; i < points; i++) {
222 Vector v = start.toVector().add(step.clone().multiply(i));
223 out[i] = new Location(start.getWorld(), v.getX(), v.getY(), v.getZ());
224 }
225 return out;
226 }
227
228 // ---------------------------------------------------------------------
229 // CUBE FILL
230 // ---------------------------------------------------------------------
231
243 public static List<Location> GetAllLocationsInCubeArea(Location a, Location b) {
244 return cubeLocations(a, b);
245 }
246
247 public static List<Location> cubeLocations(Location a, Location b) {
248 if (a == null || b == null) throw new IllegalArgumentException("Locations must not be null.");
249 if (a.getWorld() == null || b.getWorld() == null)
250 throw new IllegalArgumentException("Worlds must not be null.");
251 if (!a.getWorld().equals(b.getWorld()))
252 throw new IllegalArgumentException("Locations must be in the same world.");
253
254 List<Location> locations = new ArrayList<>();
255
256 int minX = Math.min(a.getBlockX(), b.getBlockX());
257 int minY = Math.min(a.getBlockY(), b.getBlockY());
258 int minZ = Math.min(a.getBlockZ(), b.getBlockZ());
259
260 int maxX = Math.max(a.getBlockX(), b.getBlockX());
261 int maxY = Math.max(a.getBlockY(), b.getBlockY());
262 int maxZ = Math.max(a.getBlockZ(), b.getBlockZ());
263
264 World w = a.getWorld();
265
266 for (int x = minX; x <= maxX; x++) {
267 for (int y = minY; y <= maxY; y++) {
268 for (int z = minZ; z <= maxZ; z++) {
269 locations.add(new Location(w, x, y, z));
270 }
271 }
272 }
273 return locations;
274 }
275
276 // ---------------------------------------------------------------------
277 // TOTAL DISTANCE
278 // ---------------------------------------------------------------------
279
290 public static double TotalDistance(Location... locations) {
291 return totalDistance(locations);
292 }
293
294 public static double totalDistance(Location... locations) {
295 if (locations == null || locations.length < 2)
296 throw new IllegalArgumentException("At least two locations must be provided.");
297 double total = 0.0;
298 for (int i = 0; i < locations.length - 1; i++) {
299 Location a = Objects.requireNonNull(locations[i], "Location[" + i + "] is null");
300 Location b = Objects.requireNonNull(locations[i + 1], "Location[" + (i + 1) + "] is null");
301 if (!Objects.equals(a.getWorld(), b.getWorld()))
302 throw new IllegalArgumentException("All consecutive locations must be in the same world.");
303 total += a.distance(b);
304 }
305 return total;
306 }
307
308 // ---------------------------------------------------------------------
309 // DIRECTIONAL POSITION
310 // ---------------------------------------------------------------------
311
324 public static Location GetLocationInDirection(Location start, Vector direction, double distance) {
325 return translate(start, direction, distance);
326 }
327
328 public static Location translate(Location start, Vector direction, double distance) {
329 if (start == null || direction == null)
330 throw new IllegalArgumentException("Start location and direction must not be null.");
331 Vector newPos = start.toVector().add(direction.clone().normalize().multiply(distance));
332 return newPos.toLocation(start.getWorld());
333 }
334
335 // ---------------------------------------------------------------------
336 // ANGLES
337 // ---------------------------------------------------------------------
338
357 public static double CalculateAngleBetweenLocations(Location a, Location b, String axis) {
358 return axisAngle(a, b, axis);
359 }
360
361 public static double axisAngle(Location a, Location b, String axis) {
362 if (a == null || b == null) throw new IllegalArgumentException("Locations must not be null.");
363 double dx = b.getX() - a.getX();
364 double dy = b.getY() - a.getY();
365 double dz = b.getZ() - a.getZ();
366
367 String ax = Objects.requireNonNull(axis, "axis").trim().toUpperCase(Locale.ROOT);
368 switch (ax) {
369 case "X": return Math.atan2(dy, dz);
370 case "Y": return Math.atan2(dx, dz);
371 case "Z": return Math.atan2(dy, dx);
372 default: throw new IllegalArgumentException("Invalid axis: " + axis + " (expected X, Y, or Z)");
373 }
374 }
375}
Location and vector helper utilities with safe null checks, world consistency checks,...
static Location GetLocationInDirection(Location start, Vector direction, double distance)
Computes a location offset from start by direction.normalized * distance.
static boolean IsBlockBetweenLocations(Location location1, Location location2, Block block)
Checks if a block lies within the axis-aligned box defined by two locations.
static Location ReturnClosestLocation(List< Location > locations, Location originLocation)
Returns the closest location to the provided origin from a list.
static Location FindMidPointBetween2Locations(Location a, Location b)
Finds the midpoint between two locations (same world required).
static Location[] ReturnAllLocationsBetweenTwoLocations(Location start, Location end, double spacing)
Returns evenly spaced points (including endpoints) between two locations.
static Location translate(Location start, Vector direction, double distance)
static boolean isBlockBetween(Location location1, Location location2, Block block)
static List< Location > GetAllLocationsInCubeArea(Location a, Location b)
Returns all block locations inside the axis-aligned box defined by two points.
static double TotalDistance(Location... locations)
Calculates the total path length through provided locations in order.
static double CalculateAngleBetweenLocations(Location a, Location b, String axis)
Calculates angle (radians) of vector a->b around a given axis.
static Location closestLocation(List< Location > locations, Location originLocation)
static Location[] pointsBetween(Location start, Location end, double spacing)
static double axisAngle(Location a, Location b, String axis)
static List< Location > cubeLocations(Location a, Location b)