private void CollectSimilarContiguousAdjacentSurfaces(LevelEntity_Side centralSide, LevelEntity_Side.DataSources centralDataSource, bool left)
        {
            var centralLine = LevelEntity_Level.Instance.Level.Lines[centralSide.NativeObject.LineIndex];

            var centralEndpointIndex = centralSide.NativeObject.EndpointIndex(LevelEntity_Level.Instance.Level, centralLine, left);
            var neighborLines        = LevelEntity_Level.Instance.Level.EndpointLines[centralEndpointIndex];

            foreach (var neighborLine in neighborLines)
            {
                if (neighborLine == centralLine)
                {
                    continue;
                }

                var neighborFlowsOutward = neighborLine.EndpointIndexes[0] == centralEndpointIndex;
                var neighborIsClockwise  = neighborFlowsOutward != left;

                var neighborSide = neighborLine.GetRuntimeSide(LevelEntity_Level.Instance.Level, neighborIsClockwise);

                if (neighborSide == null)
                {
                    continue;
                }

                if (CheckIfSimilarAndContiguous(centralSide, centralDataSource,
                                                neighborSide, LevelEntity_Side.DataSources.Primary,
                                                neighborFlowsOutward, left,
                                                out var alignmentGroupeePrimary) &&
                    !alignmentGroup.Any(surface => surface.DestinationSurface == alignmentGroupeePrimary.DestinationSurface) &&
                    alignmentGroupeePrimary.DestinationSurface != this)
                {
                    alignmentGroup.Add(alignmentGroupeePrimary);
                }

                if (CheckIfSimilarAndContiguous(centralSide, centralDataSource,
                                                neighborSide, LevelEntity_Side.DataSources.Secondary,
                                                neighborFlowsOutward, left,
                                                out var alignmentGroupeeSecondary) &&
                    !alignmentGroup.Any(surface => surface.DestinationSurface == alignmentGroupeeSecondary.DestinationSurface) &&
                    alignmentGroupeeSecondary.DestinationSurface != this)
                {
                    alignmentGroup.Add(alignmentGroupeeSecondary);
                }

                if (CheckIfSimilarAndContiguous(centralSide, centralDataSource,
                                                neighborSide, LevelEntity_Side.DataSources.Transparent,
                                                neighborFlowsOutward, left,
                                                out var alignmentGroupeeTransparent) &&
                    !alignmentGroup.Any(surface => surface.DestinationSurface == alignmentGroupeeTransparent.DestinationSurface) &&
                    alignmentGroupeeTransparent.DestinationSurface != this)
                {
                    alignmentGroup.Add(alignmentGroupeeTransparent);
                }
            }
        }
        private bool CheckIfSimilarAndContiguous(LevelEntity_Side sourceSide, LevelEntity_Side.DataSources sourceDataSource,
                                                 LevelEntity_Side destinationSide, LevelEntity_Side.DataSources destinationDataSource,
                                                 bool destinationFlowsOutward, bool destinationIsLeftOfSource,
                                                 out AlignmentGroupee alignmentGroupee)
        {
            short           sourceLowHeight       = 0;
            short           sourceHighHeight      = 0;
            ShapeDescriptor sourceShapeDescriptor = ShapeDescriptor.Empty;

            switch (sourceDataSource)
            {
            case LevelEntity_Side.DataSources.Primary:
                sourceLowHeight       = sourceSide.PrimaryLowElevation;
                sourceHighHeight      = sourceSide.PrimaryHighElevation;
                sourceShapeDescriptor = sourceSide.NativeObject.Primary.Texture;
                break;

            case LevelEntity_Side.DataSources.Secondary:
                sourceLowHeight       = sourceSide.SecondaryLowElevation;
                sourceHighHeight      = sourceSide.SecondaryHighElevation;
                sourceShapeDescriptor = sourceSide.NativeObject.Secondary.Texture;
                break;

            case LevelEntity_Side.DataSources.Transparent:
                sourceLowHeight       = sourceSide.TransparentLowElevation;
                sourceHighHeight      = sourceSide.TransparentHighElevation;
                sourceShapeDescriptor = sourceSide.NativeObject.Transparent.Texture;
                break;
            }

            alignmentGroupee                           = new AlignmentGroupee();
            alignmentGroupee.SourceSide                = sourceSide;
            alignmentGroupee.SourceDataSource          = sourceDataSource;
            alignmentGroupee.DestinationSide           = destinationSide;
            alignmentGroupee.DestinationDataSource     = destinationDataSource;
            alignmentGroupee.DestinationFlowsOutward   = destinationFlowsOutward;
            alignmentGroupee.DestinationIsLeftOfSource = destinationIsLeftOfSource;

            switch (destinationDataSource)
            {
            case LevelEntity_Side.DataSources.Primary:
                if (destinationSide.PrimarySurface &&
                    sourceLowHeight <= destinationSide.PrimaryHighElevation &&
                    destinationSide.PrimaryLowElevation <= sourceHighHeight &&
                    destinationSide.NativeObject.Primary.Texture.Equals(sourceShapeDescriptor))
                {
                    alignmentGroupee.DestinationSurface = destinationSide.PrimarySurface;
                    return(true);
                }

                break;

            case LevelEntity_Side.DataSources.Secondary:
                if (destinationSide.SecondarySurface &&
                    sourceLowHeight <= destinationSide.SecondaryHighElevation &&
                    destinationSide.SecondaryLowElevation <= sourceHighHeight &&
                    destinationSide.NativeObject.Secondary.Texture.Equals(sourceShapeDescriptor))
                {
                    alignmentGroupee.DestinationSurface = destinationSide.SecondarySurface;
                    return(true);
                }

                break;

            case LevelEntity_Side.DataSources.Transparent:
                if (destinationSide.TransparentSurface &&
                    sourceLowHeight <= destinationSide.TransparentHighElevation &&
                    destinationSide.TransparentLowElevation <= sourceHighHeight &&
                    destinationSide.NativeObject.Transparent.Texture.Equals(sourceShapeDescriptor))
                {
                    alignmentGroupee.DestinationSurface = destinationSide.TransparentSurface;
                    return(true);
                }

                break;
            }

            alignmentGroupee = new AlignmentGroupee();
            return(false);
        }
        private void CollectSimilarContiguousAdjacentSurfaces(LevelEntity_Side centralSide, LevelEntity_Side.DataSources centralDataSource)
        {
            CollectSimilarContiguousAdjacentSurfaces(centralSide, centralDataSource, left: true);

            CollectSimilarContiguousAdjacentSurfaces(centralSide, centralDataSource, left: false);
        }
        private void AlignDestinationToSource(LevelEntity_Side sourceSide, LevelEntity_Side.DataSources sourceDataSource, LevelEntity_Side destinationSide, LevelEntity_Side.DataSources destinationDataSource, bool destinationFlowsOutward, bool destinationIsLeftOfSource, bool rebatch)
        {
            short sourceX;
            short sourceY;
            short destinationHeight;
            short sourceHeight;

            switch (destinationDataSource)
            {
            case LevelEntity_Side.DataSources.Primary:
                destinationHeight = destinationSide.PrimaryHighElevation;
                break;

            case LevelEntity_Side.DataSources.Secondary:
                destinationHeight = destinationSide.SecondaryHighElevation;
                break;

            case LevelEntity_Side.DataSources.Transparent:
                destinationHeight = destinationSide.TransparentHighElevation;
                break;

            default:
                return;
            }

            switch (sourceDataSource)
            {
            case LevelEntity_Side.DataSources.Primary:
                sourceX = sourceSide.NativeObject.Primary.X;
                sourceY = sourceSide.NativeObject.Primary.Y;

                sourceHeight = sourceSide.PrimaryHighElevation;

                break;

            case LevelEntity_Side.DataSources.Secondary:
                sourceX = sourceSide.NativeObject.Secondary.X;
                sourceY = sourceSide.NativeObject.Secondary.Y;

                sourceHeight = sourceSide.SecondaryHighElevation;

                break;

            case LevelEntity_Side.DataSources.Transparent:
                sourceX = sourceSide.NativeObject.Transparent.X;
                sourceY = sourceSide.NativeObject.Transparent.Y;

                sourceHeight = sourceSide.TransparentHighElevation;

                break;

            default:
                return;
            }

            short horizontalOffset = destinationIsLeftOfSource ?
                                     (short)-LevelEntity_Level.Instance.Lines[destinationSide.NativeObject.LineIndex].NativeObject.Length :
                                     LevelEntity_Level.Instance.Lines[sourceSide.NativeObject.LineIndex].NativeObject.Length;

            short newX = (short)(sourceX + horizontalOffset);
            short newY = (short)(sourceHeight - destinationHeight + sourceY);

            destinationSide.SetOffset(destinationDataSource,
                                      newX,
                                      newY,
                                      rebatch);
        }