Ejemplo n.º 1
0
        void SetRange(int sectionIndex, int offset, int length, bool desiredFree)
        {
            if (sectionIndex < 0)
            {
                throw new ArgumentException($"{nameof(sectionIndex)} cannot be negative", nameof(sectionIndex));
            }
            if (offset < 0)
            {
                throw new ArgumentException($"{nameof(offset)} cannot be negative", nameof(offset));
            }
            if (length < 0)
            {
                throw new ArgumentException($"{nameof(length)} cannot be negative", nameof(length));
            }
            if (length == 0)
            {
                return;
            }
            if (sectionIndex > sections.Length)
            {
                throw new ArgumentException($"{nameof(sectionIndex)} cannot be higher than number of sections", nameof(sectionIndex));
            }

            // Check if we need to add a new one instead
            if (sectionIndex == sections.Length)
            {
                if (sectionIndex == 0 ||                                // If sections is empty, just add a new section
                    IsSectionFree(sectionIndex - 1) != desiredFree      // If the previous section is different than desiredFree, we need to add a new section too
                    )
                {
                    Debug.Assert(// either this is the first section and offset needs to be 0
                        (sectionIndex == 0 && offset == 0) ||
                        // or it connects to the previous section
                        (sectionIndex > 0 && offset == sections[sectionIndex - 1].start + sections[sectionIndex - 1].length));
                    sections.Add(new Section
                    {
                        start  = offset,
                        length = length
                    });
                    if (sectionIndex == 0)
                    {
                        firstElementFree = desiredFree;
                    }
                    Debug.Assert(IsSectionFree(sectionIndex) == desiredFree);
                }
                else
                {
                    // Otherwise, we can merge our allocation with the previous allocated section ...
                    Debug.Assert(IsSectionFree(sectionIndex - 1) == desiredFree);
                    var previousSection = sections[sectionIndex - 1];
                    Debug.Assert(previousSection.start + previousSection.length == offset);
                    previousSection.length    += length;
                    sections[sectionIndex - 1] = previousSection;
                }
                return;
            }

            if (IsSectionFree(sectionIndex) == desiredFree)
            {
                if (desiredFree)
                {
                    throw new ArgumentException("Cannot free section because it's already free");
                }
                else
                {
                    throw new ArgumentException("Cannot allocate section because it's already allocated");
                }
            }

            var section = sections[sectionIndex];

            if (offset + length > section.start + section.length)
            {
                throw new IndexOutOfRangeException("Length of requested section to free is larger than found section");
            }

            // Note: Free and allocated sections follow each other since they always get merged
            // with allocated or free sections next to them

            // Check if our section is exactly the free range we need
            if (section.start == offset && section.length == length)
            {
                // Check if our section is the last section in the list, which means there's no next section
                if (sectionIndex + 1 == sections.Length)
                {
                    // If we're the first section, then there's no previous section
                    if (sectionIndex == 0)
                    {
                        // This means this is the only section in the list and we can just modify it
                        sections[sectionIndex] = section;
                        if (sectionIndex == 0)
                        {
                            firstElementFree = desiredFree;
                        }
                    }
                    else
                    {
                        // Otherwise, we can merge our allocation with the previous allocated section ...
                        Debug.Assert(IsSectionFree(sectionIndex - 1) == desiredFree);
                        var previousSection = sections[sectionIndex - 1];
                        Debug.Assert(previousSection.start + previousSection.length == section.start);
                        previousSection.length    += length;
                        sections[sectionIndex - 1] = previousSection;

                        // ... and remove the last item in the list (the found section)
                        sections.RemoveAt(sectionIndex);
                    }
                }
                else
                // We know that there's a next section.
                {
                    // If we're the first section, then there's no previous section
                    if (sectionIndex == 0)
                    {
                        // Merge our allocation with the next section ...
                        Debug.Assert(IsSectionFree(sectionIndex + 1) == desiredFree);
                        var nextSection = sections[sectionIndex + 1];
                        Debug.Assert(nextSection.start == section.start + section.length);
                        nextSection.start         -= length;
                        nextSection.length        += length;
                        sections[sectionIndex + 1] = nextSection;

                        // ... and remove the first item in the list (the found section)
                        sections.RemoveAt(sectionIndex);

                        firstElementFree = desiredFree;
                    }
                    else
                    {
                        // We have both a previous and a next section, and we can merge all
                        // three sections together into one section
                        Debug.Assert(IsSectionFree(sectionIndex - 1) == desiredFree);
                        var previousSection = sections[sectionIndex - 1];
                        Debug.Assert(previousSection.start + previousSection.length == section.start);
                        Debug.Assert(IsSectionFree(sectionIndex + 1) == desiredFree);
                        var nextSection = sections[sectionIndex + 1];
                        Debug.Assert(nextSection.start == section.start + section.length);
                        previousSection.length     = previousSection.length + length + nextSection.length;
                        sections[sectionIndex - 1] = previousSection;

                        // ... and we remove the two entries we don't need anymore
                        sections.RemoveRange(sectionIndex, 2);
                    }
                }
            }
            else
            // If our allocation doesn't match the section exactly, we need to keep a leftover
            {
                var firstSectionLength = offset - section.start;
                Debug.Assert(firstSectionLength >= 0);
                var middleSectionLength = length;
                var lastSectionLength   = (section.start + section.length) - (offset + length);
                Debug.Assert(lastSectionLength >= 0);

                if (firstSectionLength == 0)
                {
                    Debug.Assert(lastSectionLength > 0);

                    if (sectionIndex == 0)
                    {
                        // Modify the existing section to hold the middle section
                        sections[sectionIndex] = new Section
                        {
                            start  = offset,
                            length = middleSectionLength
                        };

                        // Insert a new section behind it to hold the leftover
                        sections.InsertAt(sectionIndex + 1, new Section
                        {
                            start  = offset + middleSectionLength,
                            length = lastSectionLength
                        });

                        firstElementFree = desiredFree;
                    }
                    else
                    {
                        // Modify the existing section to hold the left-over
                        sections[sectionIndex] = new Section
                        {
                            start  = offset + middleSectionLength,
                            length = lastSectionLength
                        };

                        // Merge middle section with the previous section
                        Debug.Assert(IsSectionFree(sectionIndex - 1) == desiredFree);
                        var previousSection = sections[sectionIndex - 1];
                        Debug.Assert(previousSection.start + previousSection.length == section.start);
                        previousSection.length    += middleSectionLength;
                        sections[sectionIndex - 1] = previousSection;
                    }
                }
                else
                if (lastSectionLength == 0)
                {
                    Debug.Assert(firstSectionLength > 0);
                    // Modify the existing section to hold the left-over
                    sections[sectionIndex] = new Section
                    {
                        start  = section.start,
                        length = firstSectionLength
                    };
                    if (sectionIndex == 0)
                    {
                        firstElementFree = !desiredFree;
                    }

                    if (sectionIndex + 1 == sections.Length)
                    {
                        // Insert a new section to hold the middle section
                        sections.InsertAt(sectionIndex + 1, new Section
                        {
                            start  = offset,
                            length = middleSectionLength
                        });
                    }
                    else
                    {
                        // Merge middle section with the next section
                        Debug.Assert(IsSectionFree(sectionIndex + 1) == desiredFree);
                        var nextSection = sections[sectionIndex + 1];
                        Debug.Assert(nextSection.start == section.start + section.length);
                        nextSection.start         -= middleSectionLength;
                        nextSection.length        += middleSectionLength;
                        sections[sectionIndex + 1] = nextSection;
                    }
                }
                else
                {
                    // Modify the existing section to hold the first left-over
                    sections[sectionIndex] = new Section
                    {
                        start  = section.start,
                        length = firstSectionLength
                    };
                    if (sectionIndex == 0)
                    {
                        firstElementFree = !desiredFree;
                    }

                    // Add the middle section
                    sections.InsertAt(sectionIndex + 1, new Section
                    {
                        start  = offset,
                        length = middleSectionLength
                    });

                    var lastSection = new Section
                    {
                        start  = offset + middleSectionLength,
                        length = lastSectionLength
                    };

                    // Add the last left-over
                    sections.InsertAt(sectionIndex + 2, lastSection);
                }
            }
        }
Ejemplo n.º 2
0
        internal static void Split2DPolygonAlongOriginXAxis(ref UnsafeList <SegmentVertex> polygonVerticesList, ref UnsafeList <int> polygonVerticesSegments, int defaultSegment = 0)
        {
            const float kEpsilon = CSGConstants.kFatPlaneWidthEpsilon;

            for (int r = polygonVerticesSegments.Length - 1; r >= 0; r--)
            {
                var start = r == 0 ? 0 : polygonVerticesSegments[r - 1];
                var end   = polygonVerticesSegments[r];

                var positiveSide = 0;
                var negativeSide = 0;
                for (int i = start; i < end; i++)
                {
                    var x = polygonVerticesList[i].position.x;
                    if (x < -kEpsilon)
                    {
                        negativeSide++; if (positiveSide > 0)
                        {
                            break;
                        }
                    }
                    if (x > kEpsilon)
                    {
                        positiveSide++; if (negativeSide > 0)
                        {
                            break;
                        }
                    }
                }
                if (negativeSide == 0 ||
                    positiveSide == 0)
                {
                    return;
                }

                using (var polygon = new NativeList <SegmentVertex>(Allocator.Temp))
                {
                    for (int a = end - 1, b = start; b < end; a = b, b++)
                    {
                        var point_a = polygonVerticesList[a];
                        var point_b = polygonVerticesList[b];

                        var x_a = point_a.position.x;
                        var y_a = point_a.position.y;

                        var x_b = point_b.position.x;
                        var y_b = point_b.position.y;

                        if (!(x_a <= kEpsilon && x_b <= kEpsilon) &&
                            !(x_a >= -kEpsilon && x_b >= -kEpsilon))
                        {
                            // *   .
                            //  \  .
                            //   \ .
                            //    \.
                            //     *
                            //     .\
                            //     . \
                            //     .  \
                            //     .   *

                            if (x_b < x_a)
                            {
                                var t = x_a; x_a = x_b; x_b = t; t = y_a; y_a = y_b; y_b = t;
                            }

                            var x_s = (x_a - x_b);
                            var y_s = (y_a - y_b);

                            var intersection = new float2(0, y_b - (y_s * (x_b / x_s)));
                            polygon.Add(new SegmentVertex {
                                position = intersection, segmentIndex = defaultSegment
                            });
                        }
                        polygon.Add(point_b);
                    }

                    polygonVerticesList.RemoveRange(start, (end - start));
                    polygonVerticesSegments.RemoveAt(r);
                    var delta = end - start;
                    for (; r < polygonVerticesSegments.Length; r++)
                    {
                        polygonVerticesSegments[r] -= delta;
                    }

                    const float kAxisCenter = 0.0f;

                    // positive side polygon
                    for (int i = 0; i < polygon.Length; i++)
                    {
                        var v = polygon[i];
                        var p = v.position;
                        if (p.x < -kEpsilon)
                        {
                            continue;
                        }
                        if (p.x > kEpsilon)
                        {
                            polygonVerticesList.Add(v);
                        }
                        else
                        {
                            polygonVerticesList.Add(new SegmentVertex {
                                position = new float2(kAxisCenter, p.y), segmentIndex = defaultSegment
                            });
                        }
                    }
                    polygonVerticesSegments.Add(polygonVerticesList.Length);


                    // negative side polygon (reversed)
                    for (int i = polygon.Length - 1; i >= 0; i--)
                    {
                        var v = polygon[i];
                        var p = v.position;
                        if (p.x > kEpsilon)
                        {
                            continue;
                        }
                        if (p.x < -kEpsilon)
                        {
                            polygonVerticesList.Add(v);
                        }
                        else
                        {
                            polygonVerticesList.Add(new SegmentVertex {
                                position = new float2(-kAxisCenter, p.y), segmentIndex = defaultSegment
                            });
                        }
                    }
                    polygonVerticesSegments.Add(polygonVerticesList.Length);
                }
            }
        }