Per
Per

Reputation: 39

How do i get a useful median for 3D positions?

I have made a useful median for 2D positions by taking the average median of the x positions of the positions rotated in every 360° direction. The angleDiffs are the degrees of freedom, that the rotation around the point has, before hitting another point. I don't know how to calculate that in 3d as it has 2 axis of rotation, which make it complicated. Does anyone have an idea on how to calculate that?

    internal static Vector2 Median(IEnumerable<Vector2> positions) {
        List<float> angles = new(positions.Count() - 1);
        (Vector2 pos, FloatRef angle)[] points =
            positions.Select(p => (p, new FloatRef())).ToArray();
        foreach((Vector2 pos, FloatRef angle) in points) {
            angles.Clear();
            foreach(Vector2 p in positions) {
                if(pos == p) continue;
                angles.Add(Vector2.SignedAngle(pos - p, Vector2.up));
            }
            angles.Sort((a, b) => a == b ? 0 : a > b ? 1 : -1);
            List<(bool? positive, float angleDif)> AngleDiffs = GenerateAngleDiffs(angles);
            int positives = AngleDiffs.Count(a => a.positive.HasValue && a.positive.Value);
            float score = 0f;
            float halfPosition = angles.Count / 2f;
            foreach((bool? p, float angleDif) in AngleDiffs) {
                score += angleDif * Mathf.Abs(halfPosition - positives);
                positives -= p.HasValue ? p.Value ? 1 : -1 : 0;
            } angle.f = score;
        }
        float minCount = points.Min(p => p.angle.f);
        return Average(points.Where(p => p.angle.f == minCount).Select(p => p.pos));
    }

    static List<(bool? positive, float angleDif)> GenerateAngleDiffs(List<float> angles) {
        int count = angles.Count;
        int otherIndex = 0;
        while(otherIndex < count && angles[otherIndex] <= 0) otherIndex++;
        int otherCount = otherIndex;
        int index = 0;
        float prevAngle = -180;
        List<(bool? positive, float angleDif)> diffs = new(count + 1);
        while(index < otherCount || otherIndex < count) {
            float angle = index >= otherCount ? float.PositiveInfinity : angles[index];
            float otherAngle = otherIndex >= count ? float.PositiveInfinity : angles[otherIndex] - 180;
            AddToAngles(otherAngle, angle, ref index, true);
            AddToAngles(angle, otherAngle, ref otherIndex, false);
            void AddToAngles(float a1, float a2, ref int i, bool b) {
                if(a1 < a2) return;
                diffs.Add((b, a2 - prevAngle));
                prevAngle = a2;
                i++;
            }
        }
        diffs.Add((null, 0 - prevAngle));
        return diffs;
    }

    internal static Vector2 Average(IEnumerable<Vector2> positions)
        => positions.Aggregate((p1, p2) => p1 + p2) / positions.Count();

    class FloatRef {
        internal float f;
    }

Upvotes: 0

Views: 35

Answers (0)

Related Questions