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