Untitled

 avatar
unknown
plain_text
19 days ago
12 kB
1
Indexable
using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;

public class LargestInscribedRect
{
    // Provided Convex Hull Function (Assume it's implemented elsewhere)
    public static List<Point2d> ComputeHull(List<Point2d> points, double concavity = 0.0, double scaleFactor = 2)
    {
        // Implementation of Concave Hull
        // Assume this function is defined elsewhere and works correctly.
        // Replace this with your actual implementation
        // Placeholder: just return a simple rectangle for now
        if (points.Count < 3) return points;
        double minX = points.Min(p => p.X);
        double minY = points.Min(p => p.Y);
        double maxX = points.Max(p => p.X);
        double maxY = points.Max(p => p.Y);
        return new List<Point2d>
        {
            new Point2d(minX, minY),
            new Point2d(maxX, minY),
            new Point2d(maxX, maxY),
            new Point2d(minX, maxY)
        };
    }

    public static Polyline GetLargestRectangleInsidePolyline(Polyline polyline)
    {
        // 1. Preprocessing: Convex Hull
        List<Point2d> polylinePoints = new List<Point2d>();
        for (int i = 0; i < polyline.NumberOfVertices; i++)
        {
            polylinePoints.Add(polyline.GetPoint2dAt(i));
        }
        List<Point2d> hullPoints = ComputeHull(polylinePoints);

        // 2. Rotating Calipers
        Polyline largestRectangle = RotatingCalipers(hullPoints);

        // 3. Clipping to Original Polygon
        Polyline clippedRectangle = ClipRectangleToPolygon(largestRectangle, polyline);

        return clippedRectangle;
    }

    private static Polyline RotatingCalipers(List<Point2d> hullPoints)
    {
        if (hullPoints.Count < 3)
        {
            // Handle cases where the hull has fewer than 3 points (e.g., a point or a line segment)
            if (hullPoints.Count == 2)
            {
                Polyline linePolyline = new Polyline();
                linePolyline.AddVertexAt(0, hullPoints[0], 0, 0, 0);
                linePolyline.AddVertexAt(1, hullPoints[1], 0, 0, 0);
                return linePolyline;
            }
            else
            {
                return null; // Or handle as appropriate for your application
            }
        }

        double minX = hullPoints.Min(p => p.X);
        double minY = hullPoints.Min(p => p.Y);
        double maxX = hullPoints.Max(p => p.X);
        double maxY = hullPoints.Max(p => p.Y);

        Point2d p1 = new Point2d(minX, minY);
        Point2d p2 = new Point2d(maxX, minY);
        Point2d p3 = new Point2d(maxX, maxY);
        Point2d p4 = new Point2d(minX, maxY);

        Polyline maxRect = new Polyline();
        maxRect.AddVertexAt(0, p1, 0, 0, 0);
        maxRect.AddVertexAt(1, p2, 0, 0, 0);
        maxRect.AddVertexAt(2, p3, 0, 0, 0);
        maxRect.AddVertexAt(3, p4, 0, 0, 0);
        maxRect.Closed = true;

        double maxArea = (maxX - minX) * (maxY - minY);

        double angleIncrement = 0.01; // Adjust for finer/coarser rotation
        for (double angle = 0; angle < Math.PI / 2; angle += angleIncrement)
        {
            List<Point2d> rotatedHull = hullPoints.Select(p => RotatePoint(p, angle, new Point2d((minX + maxX) / 2, (minY + maxY) / 2))).ToList();

            minX = rotatedHull.Min(p => p.X);
            minY = rotatedHull.Min(p => p.Y);
            maxX = rotatedHull.Max(p => p.X);
            maxY = rotatedHull.Max(p => p.Y);

            Point2d rotatedP1 = new Point2d(minX, minY);
            Point2d rotatedP2 = new Point2d(maxX, minY);
            Point2d rotatedP3 = new Point2d(maxX, maxY);
            Point2d rotatedP4 = new Point2d(minX, maxY);

            // Rotate the points back to the original coordinate system
            p1 = RotatePoint(rotatedP1, -angle, new Point2d((minX + maxX) / 2, (minY + maxY) / 2));
            p2 = RotatePoint(rotatedP2, -angle, new Point2d((minX + maxX) / 2, (minY + maxY) / 2));
            p3 = RotatePoint(rotatedP3, -angle, new Point2d((minX + maxX) / 2, (minY + maxY) / 2));
            p4 = RotatePoint(rotatedP4, -angle, new Point2d((minX + maxX) / 2, (minY + maxY) / 2));

            double area = (maxX - minX) * (maxY - minY);

            if (area > maxArea)
            {
                maxArea = area;
                maxRect.SetPointAt(0, p1);
                maxRect.SetPointAt(1, p2);
                maxRect.SetPointAt(2, p3);
                maxRect.SetPointAt(3, p4);
            }
        }

        return maxRect;
    }

    // Helper function to rotate a point around a center
    private static Point2d RotatePoint(Point2d point, double angle, Point2d center)
    {
        double x = center.X + (point.X - center.X) * Math.Cos(angle) - (point.Y - center.Y) * Math.Sin(angle);
        double y = center.Y + (point.X - center.X) * Math.Sin(angle) + (point.Y - center.Y) * Math.Cos(angle);
        return new Point2d(x, y);
    }

    private static Polyline ClipRectangleToPolygon(Polyline rectangle, Polyline polygon)
    {
        // Create Regions from the rectangle and polygon
        Region rectangleRegion = CreateRegionFromPolyline(rectangle);
        Region polygonRegion = CreateRegionFromPolyline(polygon);

        if (rectangleRegion == null || polygonRegion == null)
        {
            // Handle cases where regions could not be created
            return ShrinkRectangleToFit(rectangle, polygon);
        }

        // Perform intersection
        Region clonedRegion = (Region)rectangleRegion.Clone();
        clonedRegion.IntersectWith(polygonRegion);

        // Extract the resulting polyline (if any)
        DBObjectCollection curves = new DBObjectCollection();
        clonedRegion.Explode(curves);

        Polyline clippedRectangle = null;
        foreach (DBObject curve in curves)
        {
            if (curve is Polyline)
            {
                clippedRectangle = (Polyline)curve;
                break;
            }
        }

        if (clippedRectangle != null)
        {
            return clippedRectangle;
        }
        else
        {
            // No intersection found or could not extract polyline, shrink the rectangle until it fits inside
            return ShrinkRectangleToFit(rectangle, polygon);
        }
    }

    // Helper function to create a Region from a Polyline
    private static Region CreateRegionFromPolyline(Polyline polyline)
    {
        if (polyline == null || polyline.Closed == false) return null;

        DBObjectCollection dbObjs = new DBObjectCollection();
        dbObjs.Add(polyline.Clone() as Polyline); // Clone to avoid modifying the original polyline
        DBObjectCollection regions = Region.CreateFromCurves(dbObjs);

        if (regions.Count > 0)
        {
            return regions[0] as Region;
        }

        return null;
    }

    // Function to shrink the rectangle until it fits inside the polygon
    private static Polyline ShrinkRectangleToFit(Polyline rectangle, Polyline polygon)
    {
        double scaleFactor = 0.95; // Shrink by 5% each iteration
        int maxIterations = 20; // Prevent infinite loop
        int iteration = 0;

        while (!IsRectangleInsidePolygon(rectangle, polygon) && iteration < maxIterations)
        {
            rectangle = ScaleRectangle(rectangle, scaleFactor);
            iteration++;
        }

        return rectangle;
    }

    // Helper function to check if a rectangle is fully inside a polygon
    private static bool IsRectangleInsidePolygon(Polyline rectangle, Polyline polygon)
    {
        for (int i = 0; i < rectangle.NumberOfVertices; i++)
        {
            Point2d point = rectangle.GetPoint2dAt(i);
            if (!IsPointInsidePolygon(point, polygon))
            {
                return false;
            }
        }
        return true;
    }

    // Helper function to check if a point is inside a polygon using ray casting
    private static bool IsPointInsidePolygon(Point2d point, Polyline polygon)
    {
        int i, j;
        bool c = false;
        for (i = 0, j = polygon.NumberOfVertices - 1; i < polygon.NumberOfVertices; j = i++)
        {
            Point2d pi = polygon.GetPoint2dAt(i);
            Point2d pj = polygon.GetPoint2dAt(j);
            if (((pi.Y > point.Y) != (pj.Y > point.Y)) &&
                (point.X < (pj.X - pi.X) * (point.Y - pi.Y) / (pj.Y - pi.Y) + pi.X))
            {
                c = !c;
            }
        }
        return c;
    }

    // Helper function to scale a rectangle from its center
    private static Polyline ScaleRectangle(Polyline rectangle, double scaleFactor)
    {
        // Calculate the center of the rectangle
        Point2d center = CalculateRectangleCenter(rectangle);

        // Scale each vertex from the center
        Polyline scaledRectangle = new Polyline();
        for (int i = 0; i < rectangle.NumberOfVertices; i++)
        {
            Point2d vertex = rectangle.GetPoint2dAt(i);
            Point2d scaledVertex = new Point2d(
                center.X + (vertex.X - center.X) * scaleFactor,
                center.Y + (vertex.Y - center.Y) * scaleFactor
            );
            scaledRectangle.AddVertexAt(i, scaledVertex, 0, 0, 0);
        }
        scaledRectangle.Closed = true;
        return scaledRectangle;
    }

    // Helper function to calculate the center of a rectangle
    private static Point2d CalculateRectangleCenter(Polyline rectangle)
    {
        double centerX = 0;
        double centerY = 0;
        for (int i = 0; i < rectangle.NumberOfVertices; i++)
        {
            Point2d vertex = rectangle.GetPoint2dAt(i);
            centerX += vertex.X;
            centerY += vertex.Y;
        }
        return new Point2d(centerX / rectangle.NumberOfVertices, centerY / rectangle.NumberOfVertices);
    }

    [CommandMethod("FindLargestRectangle")]
    public void FindLargestRectangleCommand()
    {
        Document doc = Application.DocumentManager.MdiActiveDocument;
        Editor ed = doc.Editor;

        PromptEntityOptions peo = new PromptEntityOptions("\nSelect a polyline: ");
        peo.SetRejectMessage("\nInvalid selection. Please select a polyline.");
        peo.AddAllowedClass(typeof(Polyline), true);

        PromptEntityResult per = ed.GetEntity(peo);

        if (per.Status != PromptStatus.OK)
        {
            ed.WriteMessage("\nNo polyline selected.");
            return;
        }

        using (Transaction tr = doc.TransactionManager.StartTransaction())
        {
            Polyline polyline = tr.GetObject(per.ObjectId, OpenMode.ForRead) as Polyline;

            if (polyline != null)
            {
                Polyline largestRect = LargestInscribedRect.GetLargestRectangleInsidePolyline(polyline);

                if (largestRect != null)
                {
                    // Add the resulting rectangle to the drawing
                    BlockTable bt = tr.GetObject(doc.Database.BlockTableId, OpenMode.ForRead) as BlockTable;
                    BlockTableRecord btr = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
                    btr.AppendEntity(largestRect);
                    tr.AddNewlyCreatedDBObject(largestRect, true);
                    tr.Commit();
                }
                else
                {
                    ed.WriteMessage("\nCould not find a valid rectangle.");
                }
            }
            else
            {
                ed.WriteMessage("\nSelected object is not a polyline.");
            }
        }
    }
}
Leave a Comment