111 lines
4.4 KiB
Java
111 lines
4.4 KiB
Java
package dev.lumareth.improvedAerodynamics.flight;
|
|
|
|
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
|
|
|
import java.util.Collection;
|
|
|
|
/**
|
|
* Finds the aspect ratio (span squared over area) of every wing in a ship, which sets how well it makes lift and how
|
|
* much drag that lift costs: long narrow wings are efficient, short stubby ones are not.
|
|
* <p>
|
|
* Neighbouring blocks of the same kind and facing form one wing. Horizontal wings also join across gaps of up to
|
|
* {@link #MAX_GAP} blocks along the span, so the two halves of a wing either side of a fuselage count as one wing and
|
|
* the span is the full wingspan. Air brakes are not wings and are left alone.
|
|
*/
|
|
final class WingShape {
|
|
private static final int MAX_GAP = 6;
|
|
private static final double MIN_ASPECT_RATIO = 0.5;
|
|
private static final double MAX_ASPECT_RATIO = 12.0;
|
|
|
|
private WingShape() {
|
|
}
|
|
|
|
static AeroSurface[] withAspectRatios(Collection<AeroSurface> surfaces) {
|
|
AeroSurface[] all = surfaces.toArray(new AeroSurface[0]);
|
|
int n = all.length;
|
|
int[] cell = new int[n * 3];
|
|
Long2IntOpenHashMap index = new Long2IntOpenHashMap(n * 2);
|
|
index.defaultReturnValue(-1);
|
|
int[] parent = new int[n];
|
|
for (int i = 0; i < n; i++) {
|
|
parent[i] = i;
|
|
cell[i * 3] = (int) Math.floor(all[i].x());
|
|
cell[i * 3 + 1] = (int) Math.floor(all[i].y());
|
|
cell[i * 3 + 2] = (int) Math.floor(all[i].z());
|
|
if (all[i].type() != SurfaceType.AIR_BRAKE) {
|
|
index.put(key(all[i], cell[i * 3], cell[i * 3 + 1], cell[i * 3 + 2]), i);
|
|
}
|
|
}
|
|
for (int i = 0; i < n; i++) {
|
|
AeroSurface s = all[i];
|
|
if (s.type() == SurfaceType.AIR_BRAKE) {
|
|
continue;
|
|
}
|
|
int x = cell[i * 3];
|
|
int y = cell[i * 3 + 1];
|
|
int z = cell[i * 3 + 2];
|
|
// Direct neighbours
|
|
for (int d = 0; d < 6; d++) {
|
|
int axis = d >> 1;
|
|
int step = (d & 1) == 0 ? 1 : -1;
|
|
union(parent, i, index.get(key(s, x + (axis == 0 ? step : 0), y + (axis == 1 ? step : 0), z + (axis == 2 ? step : 0))));
|
|
}
|
|
// Gaps along the span of horizontal wings, only in one direction since the other side finds this block
|
|
if (!s.type().vertical) {
|
|
for (int gap = 2; gap <= MAX_GAP + 1; gap++) {
|
|
union(parent, i, index.get(key(s, x + s.rightX() * gap, y, z + s.rightZ() * gap)));
|
|
}
|
|
}
|
|
}
|
|
|
|
int[] count = new int[n];
|
|
int[] minSpan = new int[n];
|
|
int[] maxSpan = new int[n];
|
|
boolean[] hasWing = new boolean[n];
|
|
java.util.Arrays.fill(minSpan, Integer.MAX_VALUE);
|
|
java.util.Arrays.fill(maxSpan, Integer.MIN_VALUE);
|
|
for (int i = 0; i < n; i++) {
|
|
if (all[i].type() == SurfaceType.AIR_BRAKE) {
|
|
continue;
|
|
}
|
|
int root = find(parent, i);
|
|
int span = all[i].type().vertical ? cell[i * 3 + 1] : cell[i * 3] * Math.abs(all[i].rightX()) + cell[i * 3 + 2] * Math.abs(all[i].rightZ());
|
|
count[root]++;
|
|
hasWing[root] |= all[i].type() == SurfaceType.WING;
|
|
minSpan[root] = Math.min(minSpan[root], span);
|
|
maxSpan[root] = Math.max(maxSpan[root], span);
|
|
}
|
|
for (int i = 0; i < n; i++) {
|
|
if (all[i].type() == SurfaceType.AIR_BRAKE) {
|
|
continue;
|
|
}
|
|
int root = find(parent, i);
|
|
double span = maxSpan[root] - minSpan[root] + 1;
|
|
double aspectRatio = span * span / count[root];
|
|
all[i] = all[i].withWingShape(Math.max(MIN_ASPECT_RATIO, Math.min(MAX_ASPECT_RATIO, aspectRatio)), hasWing[root]);
|
|
}
|
|
return all;
|
|
}
|
|
|
|
/** Position plus everything that must match for two blocks to be the same wing. */
|
|
private static long key(AeroSurface s, int x, int y, int z) {
|
|
long kind = (s.type().vertical ? 2 : 0) | (s.facingX() != 0 ? 1 : 0);
|
|
return net.minecraft.core.BlockPos.asLong(x, y, z) * 4 + kind;
|
|
}
|
|
|
|
private static int find(int[] parent, int i) {
|
|
while (parent[i] != i) {
|
|
parent[i] = parent[parent[i]];
|
|
i = parent[i];
|
|
}
|
|
return i;
|
|
}
|
|
|
|
private static void union(int[] parent, int a, int b) {
|
|
if (b < 0) {
|
|
return;
|
|
}
|
|
parent[find(parent, a)] = find(parent, b);
|
|
}
|
|
}
|