private void CalculateSortedFrontFaces(
        IEnumerable <Opaque> opaqueObjs, LightViewTriangle lightTriangle)
    {
        Profiler.BeginSample("Calculate shadow front faces");
        sortedFrontFaces.Clear();

        foreach (Opaque opaque in opaqueObjs)
        {
            if (lightTriangle.CalculateFrontFace(opaque) is LineSegment frontFace)
            {
                sortedFrontFaces.Add(System.Tuple.Create(frontFace, opaque));
            }
        }
        sortedFrontFaces.Add(System.Tuple.Create(lightTriangle.FarEdge(), (Opaque)null));
        Profiler.EndSample();
        //foreach (var pair in sortedFrontFaces) {
        //    Debug.Log(pair.Item1 + ", " + pair.Item2);
        //    Debug.Log(
        //        lightTriangle.Angle(pair.Item1.p1) + ", " +
        //        lightTriangle.Angle(pair.Item1.p2));
        //}
        //MinimalUnion<Opaque>.CalculateAndSort(ref sortedFrontFaces, lightTriangle.GetOrigin(), lightTriangle.Angle);
        //MinimalUnionImproved<Opaque>.SortedMinimalUnion(ref sortedFrontFaces, lightTriangle.GetOrigin(), lightTriangle.Angle);
        MinimalUnionImprovedAgain <Opaque> .SortedMinimalUnion(ref sortedFrontFaces, lightTriangle.GetOrigin(), lightTriangle.Angle);

        //foreach (var pair in sortedFrontFaces) {
        //    Debug.Log(pair.Item1 + ", " + pair.Item2);
        //    Debug.Log(
        //        lightTriangle.Angle(pair.Item1.p1) + ", " +
        //        lightTriangle.Angle(pair.Item1.p2));
        //}
    }
    private void CalculateShadowFaces(LightViewTriangle lightTriangle)
    {
        frontFacing.Clear();
        leftFacing.Clear();
        rightFacing.Clear();
        for (int i = 0; i < sortedFrontFaces.Count; i++)
        {
            LineSegment?prevSeg = i == 0 ? (LineSegment?)null : sortedFrontFaces[i - 1].Item1;
            LineSegment?nextSeg = i == sortedFrontFaces.Count - 1 ? (LineSegment?)null : sortedFrontFaces[i + 1].Item1;
            LineSegment seg     = sortedFrontFaces[i].Item1;
            Opaque      opaque  = sortedFrontFaces[i].Item2;

            if (opaque != null)
            {
                if (seg.Length() > .0001)
                {
                    if (!frontFacing.ContainsKey(opaque))
                    {
                        frontFacing.Add(opaque, new List <LineSegment>());
                    }
                    frontFacing[opaque].Add(seg);
                }
                if (prevSeg is LineSegment prev)
                {
                    LineSegment right = new LineSegment(prev.p2, seg.p1);
                    if (right.Length() > .0001 && !right.GoesAwayFrom(lightTriangle.GetOrigin()))
                    {
                        rightFacing[opaque] = right;
                    }
                }
                if (nextSeg is LineSegment next)
                {
                    LineSegment left = new LineSegment(seg.p2, next.p1);
                    if (left.Length() > .0001 && left.GoesAwayFrom(lightTriangle.GetOrigin()))
                    {
                        leftFacing[opaque] = left;
                    }
                }
            }
        }

        var rightExtent = sortedFrontFaces[0].Item1.p1;
        var leftExtent  = sortedFrontFaces[sortedFrontFaces.Count - 1].Item1.p2;

        rightmostFace = new LineSegment(lightTriangle.GetOrigin(), rightExtent);
        leftmostFace  = new LineSegment(lightTriangle.GetOrigin(), leftExtent);
    }