Files
VS--Improved-Aerodynamics/src/main/java/dev/lumareth/improvedAerodynamics/flight/WingShape.java
T

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);
}
}