Exemplo n.º 1
0
        bool AnalyzePolygonsUnionable(List <Point3d> sourcePolygon, List <Point3d> targetPolygon,
                                      out List <Point3d> unionPolygon)
        {
            unionPolygon = sourcePolygon;
            var unionResult = MinimalLoopSearcher2.ClipperBoolean(new List <List <Point3d> >()
            {
                sourcePolygon
            },
                                                                  new List <List <Point3d> >()
            {
                targetPolygon
            }, ClipType.ctUnion);

            if (unionResult.Count > 1)
            {
                if (unionResult.Count == 2)
                {
                    if ((PolygonIncludeSearcher.AreIdenticalCoordinates(sourcePolygon, unionResult[0]) ||
                         PolygonIncludeSearcher.AreIdenticalCoordinates(sourcePolygon, unionResult[1])))
                    {
                        return(false);
                    }
                }

                unionResult = unionResult
                              .OrderByDescending(it => ComputerGraphics.PolygonArea(it.ToArray()))
                              .ToList();
                var source = new List <List <Point3d> >()
                {
                    unionResult[0]
                };
                // 如果存在多于一个布尔运算结果的情况,需要分析他们是否有包含关系
                for (int i = 1; i < unionResult.Count; i++)
                {
                    var subUnionResult = MinimalLoopSearcher2.ClipperBoolean(
                        source, new List <List <Point3d> >()
                    {
                        unionResult[i]
                    }, ClipType.ctUnion);
                    if (subUnionResult.Count > 1 || subUnionResult.Count <= 0)
                    {
                        return(false);
                    }
                    source = subUnionResult;
                }

                unionResult = source;
            }

            if (unionResult.Count != 1)
            {
                return(false);
            }
            unionPolygon = unionResult[0];
            return(true);
        }
Exemplo n.º 2
0
        public static IEnumerable <KeyValuePair <CurveSegment, CurveSegment> > SearchNearSegments(IEnumerable <ObjectId> selectedObjectIds, double tolerance)
        {
            var segments = GetAllCurveSegments(selectedObjectIds, tolerance, onlyForClosedPolygon: true);
            var vertices = new List <CollisionVertex>();

            foreach (var segment in segments)
            {
                var collisionVertices = segment.MiniBoundingBox.Select(it => new CollisionVertex()
                {
                    Point   = new Point3d(it.X, it.Y, 0.0),
                    Segment = segment
                });
                vertices.AddRange(collisionVertices);
            }

            // Create kd tree
            var kdTree = new CurveVertexKdTree <CollisionVertex>(vertices, it => it.Point.ToArray(), ignoreZ: true);
            // Use kd tree to check collision bounding box's intersection
            var segmentPairs = new HashSet <KeyValuePair <CurveSegment, CurveSegment> >();

            foreach (var segment in segments)
            {
                var extents = segment.GetExtents();
                if (extents == null)
                {
                    continue;
                }
                var minPoint     = extents.Value.MinPoint;
                var maxPoint     = extents.Value.MaxPoint;
                var nearVertices = kdTree.BoxedRange(new double[] { minPoint.X, minPoint.Y, 0.0 },
                                                     new double[] { maxPoint.X, maxPoint.Y, 0.0 });
                foreach (var collisionVertex in nearVertices)
                {
                    if (collisionVertex.Segment.EntityId == segment.EntityId ||
                        segmentPairs.Contains(new KeyValuePair <CurveSegment, CurveSegment>(segment, collisionVertex.Segment)) ||
                        segmentPairs.Contains(new KeyValuePair <CurveSegment, CurveSegment>(collisionVertex.Segment, segment)))
                    {
                        continue;
                    }

                    var boundingBox = segment.MiniBoundingBox.ToList();
                    boundingBox.Add(segment.MiniBoundingBox[0]);
                    if (!ComputerGraphics.IsInPolygon(boundingBox.ToArray(), new Point2d(collisionVertex.Point.X, collisionVertex.Point.Y), 4))
                    {
                        continue;
                    }

                    segmentPairs.Add(new KeyValuePair <CurveSegment, CurveSegment>(segment, collisionVertex.Segment));
                }
            }

            return(segmentPairs);
        }
Exemplo n.º 3
0
        private static bool HaveSomeTextsOnBottom(Polyline polyline, Database database)
        {
            var extent = polyline.GeometricExtents;

            var path      = ExtentsToPoint2ds(extent);
            var textCount = 0;

            using (var transaction = database.TransactionManager.StartTransaction())
            {
                var modelspaceId = SymbolUtilityServices.GetBlockModelSpaceId(database);
                var modelspace   = (BlockTableRecord)transaction.GetObject(modelspaceId, OpenMode.ForRead);

                foreach (ObjectId objId in modelspace)
                {
                    if (textCount >= 5)
                    {
                        break;
                    }

                    var entity = transaction.GetObject(objId, OpenMode.ForRead);
                    var text   = entity as DBText;
                    var mtext  = entity as MText;
                    if (text == null && mtext == null)
                    {
                        continue;
                    }

                    if (!IsVisibleEntity(entity, transaction))
                    {
                        continue;
                    }

                    Point3d textPosition;
                    if (text != null)
                    {
                        textPosition = text.Position;
                    }
                    else
                    {
                        textPosition = mtext.Location;
                    }
                    var pos2d = new Point2d(textPosition.X, textPosition.Y);
                    if (ComputerGraphics.IsInPolygon2(pos2d, path) == 1)
                    {
                        textCount++;
                    }
                }
                transaction.Commit();
            }
            return(textCount >= 5);
        }
Exemplo n.º 4
0
        private bool IsColinear(LineSegment2d source, LineSegment2d target)
        {
            var length = source.Length;

            if (length.EqualsWithTolerance(0.0))
            {
                return(false);
            }

            var leftStart = ComputerGraphics.IsLeft(source.StartPoint, source.EndPoint, target.StartPoint) / length;
            var leftEnd   = ComputerGraphics.IsLeft(source.StartPoint, source.EndPoint, target.EndPoint) / length;

            if (leftStart.EqualsWithTolerance(0.0) && leftEnd.EqualsWithTolerance(0.0))
            {
                return(true);
            }
            return(false);
        }
Exemplo n.º 5
0
        private IEnumerable <IntersectionInfo> GetUnderShootIntersections2(IEnumerable <ObjectId> selectedObjectIds,
                                                                           IEnumerable <CurveVertex> danglingVertices, double tolerance, Transaction transaction)
        {
            if (danglingVertices == null || !danglingVertices.Any())
            {
                return(new List <IntersectionInfo>());
            }

            var intersectMap      = new Dictionary <CurveVertex, List <CurveVertex> >();
            var desireExtendTypes = new Dictionary <CurveVertex, ExtendType>();

            foreach (var vertex in danglingVertices)
            {
                var curve         = transaction.GetObject(vertex.Id, OpenMode.ForRead);
                var desiredExtend = CurveUtils.GetExtendType((Curve)curve, vertex.Point);
                desireExtendTypes[vertex] = desiredExtend;
            }

            // 创建一个kdtree
            var kdTree = new CurveVertexKdTree <CurveVertex>(danglingVertices, it => it.Point.ToArray(), ignoreZ: true);

            foreach (var objectId in selectedObjectIds)
            {
                var curve = transaction.GetObject(objectId, OpenMode.ForRead) as Curve;
                if (curve == null)
                {
                    continue;
                }

                // 预检
                var curveExtents = curve.GeometricExtents;
                var vertices     = kdTree.BoxedRange(curveExtents.MinPoint.ToArray(), curveExtents.MaxPoint.ToArray());
                if (vertices == null || !vertices.Any())
                {
                    continue;
                }

                var segments             = CurveUtils.GetSegment2dsOfCurve(curve, transaction);
                var segmentsForCollision = segments.Select(it => new CurveSegmentForCollision()
                {
                    LineSegment     = it,
                    EntityId        = objectId,
                    MiniBoundingBox = CurveSegmentForCollision.CreateCollisionBoundingBox(it, tolerance)
                });
                foreach (var curveSegmentForCollision in segmentsForCollision)
                {
                    var extents = curveSegmentForCollision.GetExtents();
                    if (extents == null)
                    {
                        continue;
                    }
                    var minPoint     = extents.Value.MinPoint;
                    var maxPoint     = extents.Value.MaxPoint;
                    var nearVertices = kdTree.BoxedRange(new double[] { minPoint.X, minPoint.Y, 0.0 },
                                                         new double[] { maxPoint.X, maxPoint.Y, 0.0 });

                    if (!nearVertices.Any())
                    {
                        continue;
                    }

                    var boundingBox = curveSegmentForCollision.MiniBoundingBox.ToList();
                    boundingBox.Add(curveSegmentForCollision.MiniBoundingBox[0]);
                    foreach (var curveVertex in nearVertices)
                    {
                        if (!ComputerGraphics.IsInPolygon(boundingBox.ToArray(), new Point2d(curveVertex.Point.X, curveVertex.Point.Y), 4))
                        {
                            continue;
                        }

                        var danglingCurve   = (Curve)transaction.GetObject(curveVertex.Id, OpenMode.ForRead);
                        var danglingSegment = GetExtendLineSegment(danglingCurve, curveVertex.Point, transaction);
                        var danglineLine    = new Line2d(danglingSegment.StartPoint, danglingSegment.EndPoint);
                        var intersectPoints = danglineLine.IntersectWith(curveSegmentForCollision.LineSegment);
                        if (intersectPoints == null || intersectPoints.Length <= 0)
                        {
                            continue;
                        }

                        var intersectPoint2D = intersectPoints[0];
                        var param            = danglingSegment.GetParameterOf(intersectPoint2D);
                        var extendType       = CurveIntersectUtils.ParamToExtendTypeForLine(param);
                        if (extendType != desireExtendTypes[curveVertex])
                        {
                            continue;
                        }

                        var intersectPoint = new Point3d(intersectPoint2D.X, intersectPoint2D.Y, 0);
                        var dist           = (intersectPoint - curveVertex.Point).Length;
                        if (dist.Larger(tolerance))
                        {
                            continue;
                        }

                        List <CurveVertex> intersectList;
                        intersectMap.TryGetValue(curveVertex, out intersectList);
                        if (intersectList == null)
                        {
                            intersectList             = new List <CurveVertex>();
                            intersectMap[curveVertex] = intersectList;
                        }

                        intersectList.Add(new CurveVertex(intersectPoint, curveSegmentForCollision.EntityId));
                    }
                }
            }

            // 分析交点
            var result = new List <IntersectionInfo>();

            foreach (var pair in intersectMap)
            {
                var vertex         = pair.Key;
                var extendVertices = pair.Value;
                var nearest        = extendVertices[0];
                var nearsetDist    = (nearest.Point - vertex.Point).Length;
                for (int i = 1; i < extendVertices.Count; i++)
                {
                    var dist = (extendVertices[i].Point - vertex.Point).Length;
                    if (dist < nearsetDist)
                    {
                        nearsetDist = dist;
                        nearest     = extendVertices[i];
                    }
                }

                result.Add(new IntersectionInfo(vertex.Id, desireExtendTypes[vertex], nearest.Id, ExtendType.None, nearest.Point));
            }
            return(result);
        }
Exemplo n.º 6
0
        public static ApartmentContourInfo CalcContour(Document doc, IEnumerable <ObjectId> objectIdsList)
        {
            var newCreatedIds = new List <ObjectId>();
            IEnumerable <ObjectId> curveIds       = new List <ObjectId>();
            IEnumerable <ObjectId> splitSourceIds = new List <ObjectId>();
            List <ObjectId>        duplicateIds   = new List <ObjectId>();

            using (var waitCursor = new WaitCursorSwitcher())
                using (var tolerance = new SafeToleranceOverride())
                {
                    // 1. Break down all lines
                    doc.Editor.WriteMessage("\n打断所有交叉线...\n");
                    var breakCrossingAlgorithm = new BreakCrossingObjectsQuadTree(doc.Editor);
                    breakCrossingAlgorithm.Check(objectIdsList);
                    var breakIdPairs = breakCrossingAlgorithm.Fix(eraseOld: false).ToList();
                    splitSourceIds = breakIdPairs.Select(it => it.Key);
                    var checkIds = objectIdsList.Except(splitSourceIds).ToList();
                    foreach (var idPair in breakIdPairs)
                    {
                        newCreatedIds.AddRange(idPair.Value);
                        checkIds.AddRange(idPair.Value);
                    }
                    // 2. Erase the duplcate curves
                    doc.Editor.WriteMessage("\n排除重复线...\n");
                    var duplicateEraserAlgorithm = new DuplicateEntityEraserKdTree(doc.Editor, 0.0005);
                    duplicateEraserAlgorithm.Check(checkIds);
                    var crossingInfos = duplicateEraserAlgorithm.CrossingInfos;
                    if (crossingInfos != null)
                    {
                        foreach (var curveCrossingInfo in crossingInfos)
                        {
                            checkIds.Remove(curveCrossingInfo.TargetId);
                            duplicateIds.Add(curveCrossingInfo.TargetId);
                        }

                        // Deal with the source duplicate
                        var groups = crossingInfos.GroupBy(it => it.TargetId);
                        foreach (var g in groups)
                        {
                            if (g.Count() <= 1)
                            {
                                continue;
                            }
                            bool first = true;
                            foreach (var curveCrossingInfo in g)
                            {
                                if (first)
                                {
                                    first = false;
                                    continue;
                                }
                                checkIds.Remove(curveCrossingInfo.SourceId);
                                duplicateIds.Add(curveCrossingInfo.SourceId);
                            }
                        }
                    }

                    // 3. Extend undershoot
                    doc.Editor.WriteMessage("\n调整未及点...\n");
                    var extendUndershoots = new ExtendUnderShoots(doc.Editor, 0.001);
                    extendUndershoots.Check(checkIds);
                    var splittedIds = extendUndershoots.Fix(breakTarget: true);
                    foreach (var pair in splittedIds)
                    {
                        checkIds.Remove(pair.Key);
                        checkIds.AddRange(pair.Value);

                        // Also recorded in newCreatedIds which will be removed at the end.
                        if (newCreatedIds.Contains(pair.Key))
                        {
                            newCreatedIds.Remove(pair.Key);
                            newCreatedIds.AddRange(pair.Value);
                        }
                    }

                    // 4. 排除0面积闭合线
                    doc.Editor.WriteMessage("\n排除零面积闭合线...\n");
                    var zeroAreaLoopIds = CurveUtils.GetZeroAreaLoop(checkIds, doc.Database);
                    foreach (var zeroAreaLoopId in zeroAreaLoopIds)
                    {
                        checkIds.Remove(zeroAreaLoopId);
                        duplicateIds.Add(zeroAreaLoopId);
                    }

                    // 5. 删除0长度对象
                    doc.Editor.WriteMessage("\n排除零长度对象...\n");
                    var zeroLengthEraser = new ZeroLengthEraser(doc.Editor);
                    zeroLengthEraser.Check(checkIds);
                    foreach (ObjectId zeroLengthId in zeroLengthEraser.ZerolengthObjectIdCollection)
                    {
                        checkIds.Remove(zeroLengthId);
                        duplicateIds.Add(zeroLengthId);
                    }

                    curveIds = checkIds;

                    //// Test code
                    //using (var transaction = doc.Database.TransactionManager.StartTransaction())
                    //{
                    //    var color = Color.FromColorIndex(ColorMethod.ByAci, 3); // Green
                    //    ObjectId layerId = LayerUtils.AddNewLayer(doc.Database, "temp-poly", "Continuous", color);
                    //    foreach (var polygonId in polygonIds)
                    //    {
                    //        var entity = (Entity)transaction.GetObject(polygonId, OpenMode.ForWrite);
                    //        entity.Color = color;
                    //        entity.LayerId = layerId;
                    //    }
                    //    transaction.Commit();
                    //}
                    //return new List<Point2d>();
                }

            // 2. Make polygons.
            doc.Editor.WriteMessage("开始分析外墙轮廓...");
            var resultIds = new List <ObjectId>();

            if (curveIds.Any())
            {
                var      color   = Color.FromColorIndex(ColorMethod.ByAci, 3); // Green
                ObjectId layerId = LayerUtils.AddNewLayer(doc.Database, "temp-poly", "Continuous", color);

                using (var tolerance = new SafeToleranceOverride())
                    using (var waitCursor = new WaitCursorSwitcher())
                    {
                        //var polygons = MinimalLoopSearcher2.Search(curveIds, doc);
                        //using (var transaction = doc.Database.TransactionManager.StartTransaction())
                        //{
                        //    var modelspaceId = SymbolUtilityServices.GetBlockModelSpaceId(doc.Database);
                        //    var modelspace = (BlockTableRecord)transaction.GetObject(modelspaceId, OpenMode.ForWrite);
                        //    foreach (var polyline in polygons)
                        //    {
                        //        polyline.Color = color;
                        //        polyline.LayerId = layerId;
                        //        var id = modelspace.AppendEntity(polyline);
                        //        resultIds.Add(id);
                        //        transaction.AddNewlyCreatedDBObject(polyline, true);
                        //    }
                        //    transaction.Commit();
                        //}
                        resultIds = NtsUtils.PolygonizeLineStrings(doc.Database, curveIds, "temp-poly", color, 0.0001);
                    }
            }

            // 3. Union all polygons
            var database    = doc.Database;
            var partitioner = new DrawingPartitioner(database);

            partitioner.Check(resultIds);
            // 4. Get largest are polygon which is the apartment's contour
            var polylines = new List <Polyline>();

            foreach (var region in partitioner.IsolatedRegions)
            {
                var polyline = new Polyline(region.Contour.Count);
                int i        = 0;
                foreach (var point in region.Contour)
                {
                    polyline.AddVertexAt(i++, new Point2d(point.X, point.Y), 0, 0, 0);
                }
                polyline.Closed = true;
                polylines.Add(polyline);
            }
            polylines.Sort((poly1, poly2) =>
            {
                if (poly1.Area > poly2.Area)
                {
                    return(-1);
                }
                return(1);
            });
            if (polylines.Count >= 2)
            {
                var first  = polylines[0];
                var second = polylines[1];
                // Exclude the situation if the largest polyline is a drawing frame.
                if (IsRectangle(first) && HaveSomeTextsOnBottom(first, database) &&
                    PolygonIncludeSearcher.IsInclude(first, second, null))
                {
                    polylines.RemoveAt(0);
                }
            }
            Polyline largestPolyline = polylines.FirstOrDefault();
            var      resultPoints    = new List <Point2d>();

            if (largestPolyline != null)
            {
                resultPoints = CurveUtils.GetDistinctVertices2D(largestPolyline, null);
                if (resultPoints[0] != resultPoints[resultPoints.Count - 1])
                {
                    resultPoints.Add(resultPoints[0]);
                }
                var clockwise = ComputerGraphics.ClockWise2(resultPoints.ToArray());
                if (clockwise)
                {
                    resultPoints.Reverse();
                }

                //// Test code !
                //using (var transaction = database.TransactionManager.StartTransaction())
                //{
                //    var modelspaceId = SymbolUtilityServices.GetBlockModelSpaceId(database);
                //    var modelspace = (BlockTableRecord)transaction.GetObject(modelspaceId, OpenMode.ForWrite);

                //    var color = Color.FromColorIndex(ColorMethod.ByAci, 3); // Green
                //    ObjectId layerId = LayerUtils.AddNewLayer(doc.Database, "temp-poly", "Continuous", color);
                //    largestPolyline.Color = color;
                //    largestPolyline.LayerId = layerId;
                //    modelspace.AppendEntity(largestPolyline);
                //    transaction.AddNewlyCreatedDBObject(largestPolyline, add: true);


                //    foreach (var polyline in polylines)
                //    {
                //        if (polyline == largestPolyline)
                //            continue;

                //        polyline.Color = color;
                //        polyline.LayerId = layerId;
                //        modelspace.AppendEntity(polyline);
                //        transaction.AddNewlyCreatedDBObject(polyline, add: true);
                //    }

                //    transaction.Commit();
                //}
            }

            // Get contour linesegments from resultPoints
            var contourSegments = new List <LineSegment3d>();
            var innerSegments   = new List <LineSegment3d>();

            if (resultPoints.Count > 0)
            {
                for (var i = 0; i < resultPoints.Count - 1; i++)
                {
                    var start   = new Point3d(resultPoints[i].X, resultPoints[i].Y, 0);
                    var end     = new Point3d(resultPoints[i + 1].X, resultPoints[i + 1].Y, 0);
                    var segment = new LineSegment3d(start, end);
                    contourSegments.Add(segment);
                }
                // Get inner linesegments
                using (var tr = doc.Database.TransactionManager.StartTransaction())
                {
                    var contourArray = resultPoints.ToArray();
                    foreach (var objId in curveIds)
                    {
                        var point2ds = CurveUtils.GetDistinctVertices2D(objId, tr);

                        for (var i = 0; i < point2ds.Count - 1; i++)
                        {
                            var start = point2ds[i];
                            var end   = point2ds[i + 1];
                            if (start.IsEqualTo(end))
                            {
                                continue;
                            }

                            // TODO: no need to calculate again for startInPoly.
                            var startInPoly = ComputerGraphics.IsInPolygon2(start, contourArray);
                            var endInPoly   = ComputerGraphics.IsInPolygon2(end, contourArray);

                            if ((startInPoly == 0 && endInPoly == 0) ||
                                (startInPoly == -1 && endInPoly == -1))
                            {
                                continue;
                            }
                            var segment = new LineSegment3d(new Point3d(start.X, start.Y, 0),
                                                            new Point3d(end.X, end.Y, 0));
                            innerSegments.Add(segment);
                        }
                    }
                    tr.Commit();
                }
            }

            // 5. Delete the polygons of resultIds
            using (var tr = doc.Database.TransactionManager.StartTransaction())
            {
                foreach (var objId in resultIds)
                {
                    if (objId.IsErased)
                    {
                        continue;
                    }
                    var dbObj = tr.GetObject(objId, OpenMode.ForWrite);
                    dbObj.Erase();
                }

                ////////////////////////////////////////////////////////////////////////////////////////
                tr.Commit();
            }

            // Delete the splited curves
            using (var tr = doc.Database.TransactionManager.StartTransaction())
            {
                foreach (var newCreatedId in newCreatedIds)
                {
                    if (newCreatedId.IsErased)
                    {
                        continue;
                    }
                    var dbObj = tr.GetObject(newCreatedId, OpenMode.ForWrite);
                    dbObj.Erase();
                }

                ////////////////////////////////////////////////////////////////////////////////////////
                tr.Commit();
            }

            var result = new ApartmentContourInfo()
            {
                Contour          = contourSegments,
                InternalSegments = innerSegments
            };

            return(result);
        }
Exemplo n.º 7
0
        public override void Check(IEnumerable <Autodesk.AutoCAD.DatabaseServices.ObjectId> selectedObjectIds)
        {
            if (!selectedObjectIds.Any())
            {
                return;
            }

            var precision = 0.000001;
            // First we need to make sure all intersections are vertices of polygon
            var missingVertexSearcher = new MissingVertexSearcherQuadTree(Editor);

            missingVertexSearcher.Check(selectedObjectIds);
            if (missingVertexSearcher.MissingVertexInfos.Any())
            {
                missingVertexSearcher.FixAll();
            }

            // Use clipper to search holes
            var subject = new List <List <IntPoint> >(1);
            var clipper = new List <List <IntPoint> >(1);

            var       database = Editor.Document.Database;
            Extents3d extents  = new Extents3d(new Point3d(0, 0, 0), new Point3d(1, 1, 0));
            bool      first    = true;

            // Use all polygons to make up clipper.
            using (var transaction = database.TransactionManager.StartTransaction())
            {
                foreach (var objId in selectedObjectIds)
                {
                    var curve = transaction.GetObject(objId, OpenMode.ForRead) as Curve;
                    if (curve == null)
                    {
                        continue;
                    }
                    if (!IsCurveClosed(curve))
                    {
                        continue;
                    }

                    // Calculate its extents.
                    if (first)
                    {
                        extents = curve.GeometricExtents;
                        first   = false;
                    }
                    else
                    {
                        extents.AddExtents(curve.GeometricExtents);
                    }

                    // Add it to the clipper.
                    var vertices = CurveUtils.GetDistinctVertices2D(curve, transaction);
                    // Only for polygon.
                    if (vertices.Count() < 3)
                    {
                        continue;
                    }

                    // That has the same vertex for the first and last array members.
                    if (vertices[0] != vertices[vertices.Count - 1])
                    {
                        vertices.Add(vertices[0]);
                    }
                    var clockwise = ComputerGraphics.ClockWise2(vertices.ToArray());
                    if (!clockwise)
                    {
                        vertices.Reverse();
                    }
                    if (vertices[0] == vertices[vertices.Count - 1])
                    {
                        vertices.RemoveAt(vertices.Count - 1);
                    }

                    clipper.Add(vertices.Select(it => new IntPoint(it.X / precision, it.Y / precision)).ToList());
                }
                transaction.Commit();
            }

            // Create subject rectangle.

            var vector   = (extents.MaxPoint - extents.MinPoint) * 0.1;
            var minPoint = extents.MinPoint - vector;
            var maxPoint = extents.MaxPoint + vector;

            subject.Add(new List <IntPoint>()
            {
                new IntPoint(minPoint.X / precision, minPoint.Y / precision),
                new IntPoint(minPoint.X / precision, maxPoint.Y / precision),
                new IntPoint(maxPoint.X / precision, maxPoint.Y / precision),
                new IntPoint(maxPoint.X / precision, minPoint.Y / precision)
            });


            var result = new List <List <IntPoint> >();
            var cpr    = new Clipper();

            cpr.AddPaths(subject, PolyType.ptSubject, true);
            cpr.AddPaths(clipper, PolyType.ptClip, true);
            cpr.Execute(ClipType.ctXor, result, PolyFillType.pftNonZero, PolyFillType.pftNonZero);
            if (result.Count <= 0)
            {
                return;
            }

            foreach (var path in result)
            {
                // Ignore the outmost loop.
                if (path.Contains(new IntPoint(minPoint.X / precision, minPoint.Y / precision)))
                {
                    continue;
                }

                var points = path.Select(it => new Point2d(it.X * precision, it.Y * precision)).ToList();
                if (points[0] != points[points.Count - 1])
                {
                    points.Add(points[0]);
                }
                var array = points.ToArray();
                if (ComputerGraphics.ClockWise2(array))
                {
                    continue;
                }

                var polyline = CreatePolygon(array);
                if (polyline.Area.Smaller(0.001))
                {
                    polyline.Dispose();
                    continue;
                }

                _holes.Add(polyline);
            }
        }