Esempio n. 1
0
        public Floor Build(InDoorGeneratorSettings settings)
        {
            var intFootPrint = settings.ObjectPool.NewList <IntPoint>(settings.Footprint.Count);

            for (int i = 0; i < settings.Footprint.Count; i++)
            {
                var point = settings.Footprint[i];
                intFootPrint.Add(new IntPoint(point.X * Scale, point.Y * Scale));
            }

            // return null if footprint area is too small
            if (Clipper.Area(intFootPrint) < MinArea)
            {
                return(null);
            }

            var floor = new Floor(settings.ObjectPool);

            var walls = CreateWalls(settings);

            InsertEntrances(settings, floor, walls);

            List <List <IntPoint> > transitPolygons;

            var connectedWalls = CreateConnectedAndTransit(settings, walls, out transitPolygons);

            var transitArea = CreateTransitArea(settings, floor, walls, transitPolygons);

            CreateApartments(settings, floor, intFootPrint, transitArea, connectedWalls);

            return(floor);
        }
Esempio n. 2
0
        private static void InsertEntrances(InDoorGeneratorSettings settings, Floor floor,
                                            List <SkeletonEdge> walls)
        {
            var footprint = settings.Footprint;

            // doors are not set
            if (settings.Doors == null)
            {
                FindDoorPosition(settings);
            }

            foreach (var door in settings.Doors)
            {
                var start = footprint[door.Key];
                var end   = footprint[(door.Key + 1) % footprint.Count];

                var centerOffset = door.Value;
                var vec          = (end - start).Normalized();
                var startOfDoor  = start + vec * (centerOffset - settings.HalfTransitAreaWidth);
                var endOfDoor    = start + vec * (centerOffset + settings.HalfTransitAreaWidth);
                floor.Entrances.Add(new LineSegment2d(startOfDoor, endOfDoor));

                var centerOfDoor = start + vec * centerOffset;
                vec.Negate();
                var doorRay = new LineParametric2d(centerOfDoor, Vector2dUtils.OrthogonalRight(vec));
                InsertEntrance(settings, walls, doorRay, centerOffset + settings.HalfTransitAreaWidth);
            }
        }
Esempio n. 3
0
        /// <summary> Creates skeleton lines connected to outer walls and transit polygons. </summary>
        private static List <Wall> CreateConnectedAndTransit(InDoorGeneratorSettings settings,
                                                             List <SkeletonEdge> walls, out List <List <IntPoint> > transitPolygons)
        {
            var offset    = new ClipperOffset();
            var connected = new List <Wall>();
            var paths     = new List <List <IntPoint> >();
            var skeleton  = settings.Skeleton;

            foreach (var wall in walls)
            {
                var start = new IntPoint(wall.Start.X * Scale, wall.Start.Y * Scale);
                var end   = new IntPoint(wall.End.X * Scale, wall.End.Y * Scale);
                if (!wall.IsOuter)
                {
                    paths.Add(new List <IntPoint> {
                        start, end
                    });
                }
                else if (wall.IsSkeleton &&
                         (skeleton.Distances[wall.Start] < settings.HalfTransitAreaWidth ||
                          skeleton.Distances[wall.End] < settings.HalfTransitAreaWidth))
                {
                    var undesired = new Wall(wall.Start, wall.End, false);
                    // TODO do I need this check here?
                    //if (!connected.Contains(undesired))
                    connected.Add(undesired);
                }
            }

            transitPolygons = new List <List <IntPoint> >(1);
            offset.AddPaths(paths, JoinType.jtMiter, EndType.etClosedLine);
            offset.Execute(ref transitPolygons, settings.HalfTransitAreaWidth * Scale);
            return(connected);
        }
Esempio n. 4
0
        private static void AddOuterWalls(InDoorGeneratorSettings settings, Floor floor, FloorContext context,
                                          Apartment apartment, List <Wall> extrudedWalls, Vector2d outerWallSplitPoint, int lastOuterIndex)
        {
            // this is last apartment
            if (outerWallSplitPoint == Vector2d.Empty)
            {
                context.OuterWallIndex = context.LastOuterWallIndex;
                outerWallSplitPoint    = extrudedWalls[context.LastOuterWallIndex].End;
            }

            // this is first apartment
            if (!floor.OuterWalls.Any())
            {
                // copy all previous walls without splitting
                if (context.OuterWallIndex != context.FirstOuterWallIndex)
                {
                    for (var i = context.FirstOuterWallIndex; i < context.OuterWallIndex;
                         i = (i + 1) % extrudedWalls.Count)
                    {
                        var wall = extrudedWalls[i];
                        AddOuterWall(floor, apartment, wall.Start, wall.End);
                    }
                }
                // add part of current wall
                AddOuterWall(floor, apartment, extrudedWalls[context.OuterWallIndex].Start, outerWallSplitPoint);
            }
            // this is intermediate or last apartment
            else
            {
                if (context.OuterWallIndex != lastOuterIndex)
                {
                    var index = lastOuterIndex;
                    while (index != context.OuterWallIndex)
                    {
                        var wall = extrudedWalls[index];

                        var startPoint = index == lastOuterIndex
                            ? floor.OuterWalls.Last().End
                            : wall.Start;

                        AddOuterWall(floor, apartment, startPoint, wall.End);
                        index = (index + 1) % extrudedWalls.Count;
                    }
                }
                AddOuterWall(floor, apartment, floor.OuterWalls.Last().End, outerWallSplitPoint);
            }
        }
Esempio n. 5
0
        private static void AddTransitWalls(InDoorGeneratorSettings settings, Floor floor, FloorContext context,
                                            Apartment apartment, List <Wall> extrudedWalls, Vector2d transitWallSplitPoint, int lastTransitIndex)
        {
            // this is last apartment
            if (transitWallSplitPoint == Vector2d.Empty)
            {
                context.TransitWallIndex = context.FirstTransitWallIndex;
                transitWallSplitPoint    = extrudedWalls[context.FirstTransitWallIndex].Start;
            }

            // this is first apartment
            if (!floor.TransitWalls.Any())
            {
                // copy all next walls without splitting
                if (context.TransitWallIndex != context.LastTransitWallIndex)
                {
                    for (var i = context.LastTransitWallIndex; i > context.TransitWallIndex;
                         i = --i < 0 ? extrudedWalls.Count + i : i)
                    {
                        var wall = extrudedWalls[i];
                        AddTransitWall(floor, apartment, wall.End, wall.Start);
                    }
                }
                AddTransitWall(floor, apartment, extrudedWalls[context.TransitWallIndex].End, transitWallSplitPoint);
            }
            // this is intermediate or last apartment
            else
            {
                if (context.TransitWallIndex != lastTransitIndex)
                {
                    var index = lastTransitIndex;
                    while (index != context.TransitWallIndex)
                    {
                        var wall = extrudedWalls[index];
                        AddTransitWall(floor, apartment, floor.TransitWalls.Last().End, wall.Start);

                        index = --index < 0 ? extrudedWalls.Count + index : index;
                        if (index == context.LastOuterWallIndex)
                        {
                            index = context.LastTransitWallIndex;
                        }
                    }
                }
                AddTransitWall(floor, apartment, floor.TransitWalls.Last().End, transitWallSplitPoint);
            }
        }
Esempio n. 6
0
        /// <summary> Creates walls from skeleton. </summary>
        private static List <SkeletonEdge> CreateWalls(InDoorGeneratorSettings settings)
        {
            var edges = new List <SkeletonEdge>();

            foreach (var edgeOutput in settings.Skeleton.Edges)
            {
                var lastIndex = edgeOutput.Polygon.Count - 1;
                for (var i = 0; i <= lastIndex; i++)
                {
                    var start = edgeOutput.Polygon[i];
                    var end   = edgeOutput.Polygon[i == lastIndex ? 0 : i + 1];

                    var sDist = settings.Skeleton.Distances[start];
                    var eDist = settings.Skeleton.Distances[end];
                    edges.Add(new SkeletonEdge(start, end, sDist != 0 || eDist != 0,
                                               Math.Min(sDist, eDist), settings.HalfTransitAreaWidth));
                }
            }
            return(edges);
        }
Esempio n. 7
0
        /// <summary>
        ///     Inserts Vertical Access Area (VAA): stairs, elevators, etc.
        ///     and merges it with corridors.
        /// </summary>
        private static List <List <IntPoint> > CreateTransitArea(InDoorGeneratorSettings settings,
                                                                 Floor floor, List <SkeletonEdge> walls, List <List <IntPoint> > transitPolygons)
        {
            foreach (var wall in walls)
            {
                if (wall.Size - settings.TransitAreaWidth > settings.VaaSizeWidth &&
                    wall.Distance > settings.VaaSizeHeight)
                {
                    var startOfVaa1 = wall.Start + (wall.End - wall.Start).Normalized() * settings.HalfTransitAreaWidth;

                    var orthogonalLeft = Vector2dUtils.OrthogonalLeft(wall.Start - wall.End).Normalized();
                    var endOfVaa1      = startOfVaa1 + orthogonalLeft * settings.VaaSizeHeight;
                    var startOfVaa2    = startOfVaa1 + (wall.End - startOfVaa1).Normalized() * settings.VaaSizeWidth;
                    var endOfVaa2      = startOfVaa2 + orthogonalLeft * settings.VaaSizeHeight;

                    settings.Clipper.AddPath(new List <IntPoint>
                    {
                        new IntPoint(startOfVaa1.X * Scale, startOfVaa1.Y * Scale),
                        new IntPoint(endOfVaa1.X * Scale, endOfVaa1.Y * Scale),
                        new IntPoint(endOfVaa2.X * Scale, endOfVaa2.Y * Scale),
                        new IntPoint(startOfVaa2.X * Scale, startOfVaa2.Y * Scale)
                    }, PolyType.ptClip, true);

                    floor.Stairs.Add(startOfVaa1);
                    floor.Stairs.Add(endOfVaa1);
                    floor.Stairs.Add(endOfVaa2);
                    floor.Stairs.Add(startOfVaa2);

                    break;
                }
            }

            settings.Clipper.AddPaths(transitPolygons, PolyType.ptSubject, true);
            var transitArea = new List <List <IntPoint> >(1);

            settings.Clipper.Execute(ClipType.ctUnion, transitArea);
            settings.Clipper.Clear();

            return(transitArea);
        }
Esempio n. 8
0
        private static void InsertEntrance(InDoorGeneratorSettings settings, List <SkeletonEdge> walls,
                                           LineParametric2d ray, double minimalSideSize)
        {
            var doorEdge       = default(SkeletonEdge);
            var distance       = double.MaxValue;
            var intersectPoint = Vector2d.Empty;

            int i = -1;

            foreach (var edge in walls)
            {
                i++;
                if (edge.IsOuter && !edge.IsSkeleton)
                {
                    continue;
                }

                var currIntersect = LineParametric2d.Collide(ray, edge.Line, 0.00001);
                if (currIntersect == Vector2d.Empty ||
                    !Vector2dUtils.IsPointOnSegment(edge.Start, edge.End, currIntersect))
                {
                    continue;
                }

                var currDistance = currIntersect.DistanceTo(ray.A);
                if (distance > currDistance /* && currDistance > minimalSideSize*/)
                {
                    doorEdge       = edge;
                    distance       = currDistance;
                    intersectPoint = currIntersect;
                }
            }

            var startDist   = Math.Min(distance, settings.Skeleton.Distances[doorEdge.Start]);
            var endDistance = Math.Min(distance, settings.Skeleton.Distances[doorEdge.End]);

            walls.Add(new SkeletonEdge(doorEdge.Start, intersectPoint, false, startDist, settings.HalfTransitAreaWidth));
            walls.Add(new SkeletonEdge(intersectPoint, doorEdge.End, false, endDistance, settings.HalfTransitAreaWidth));
            walls.Add(new SkeletonEdge(ray.A, intersectPoint, false, distance, settings.HalfTransitAreaWidth));
        }
Esempio n. 9
0
        private static void InsertAparment(InDoorGeneratorSettings settings, Floor floor, FloorContext context,
                                           List <Wall> extrudedWalls, Vector2d outerWallSplitPoint, Vector2d transitWallSplitPoint,
                                           ref int lastOuterIndex, ref int lastTransitIntex)
        {
            var apartment = new Apartment(settings.ObjectPool);

            AddOuterWalls(settings, floor, context, apartment, extrudedWalls,
                          outerWallSplitPoint, lastOuterIndex);

            AddTransitWalls(settings, floor, context, apartment, extrudedWalls,
                            transitWallSplitPoint, lastTransitIntex);

            if (outerWallSplitPoint != Vector2d.Empty)
            {
                floor.PartitionWalls.Add(new LineSegment2d(outerWallSplitPoint, transitWallSplitPoint));
            }

            floor.Apartments.Add(apartment);

            lastOuterIndex   = context.OuterWallIndex;
            lastTransitIntex = context.TransitWallIndex;
        }
Esempio n. 10
0
        private static void FindDoorPosition(InDoorGeneratorSettings settings)
        {
            var footprint   = settings.Footprint;
            var index       = 0;
            var maxDistance = 0d;

            for (int i = 0; i < footprint.Count; i++)
            {
                var start    = footprint[i];
                var end      = footprint[i == footprint.Count - 1 ? 0 : i + 1];
                var distance = start.DistanceTo(end);
                if (distance > maxDistance)
                {
                    index       = i;
                    maxDistance = distance;
                }
            }
            settings.Doors = new List <KeyValuePair <int, double> >
            {
                new KeyValuePair <int, double>(index, maxDistance / 2d)
            };
        }
Esempio n. 11
0
        private static void CreateAparments(InDoorGeneratorSettings settings, Floor floor,
                                            FloorContext context, List <Wall> extrudedWalls, List <Wall> connectedSkeletonWalls,
                                            double outerWallLength)
        {
            var currentWidthStep         = settings.PreferedWidthStep;
            var remainingOuterWallLength = outerWallLength;
            var lastUsedOuterIndex       = InvalidIndex;
            var usedTransitIndex         = InvalidIndex;

            var stopIterationIndex = context.FirstOuterWallIndex + context.OuterWallCount;

            for (var i = context.FirstOuterWallIndex; i < stopIterationIndex; i++)
            {
                var index = i == extrudedWalls.Count ? 0 : i % extrudedWalls.Count;

                var wall  = extrudedWalls[index];
                var start = wall.Start;
                var end   = wall.End;

                var distance = start.DistanceTo(end);

                remainingOuterWallLength -= distance;

                // last apartment
                if (remainingOuterWallLength < currentWidthStep)
                {
                    InsertAparment(settings, floor, context, extrudedWalls, Vector2d.Empty,
                                   Vector2d.Empty, ref lastUsedOuterIndex, ref usedTransitIndex);
                    break;
                }

                var remainingCurrentWallLength = distance;
                do
                {
                    if (remainingCurrentWallLength < currentWidthStep)
                    {
                        currentWidthStep = settings.MinimalWidthStep;
                        break;
                    }
                    remainingCurrentWallLength -= currentWidthStep;

                    // get intermediate point
                    var vec = (end - start).Normalized();
                    var outerWallSplitPoint = start + vec * currentWidthStep;

                    var ortogonalRight          = new Vector2d(-vec.Y, vec.X);
                    var someFarPointOnOuterWall = outerWallSplitPoint + ortogonalRight * 1000;

                    Vector2d transitWallSplitPoint;
                    int      transitWallIndex;
                    if (HasIntersectPoint(extrudedWalls, connectedSkeletonWalls, outerWallSplitPoint,
                                          someFarPointOnOuterWall, out transitWallSplitPoint, out transitWallIndex))
                    {
                        context.OuterWallIndex   = index;
                        context.TransitWallIndex = transitWallIndex;

                        InsertAparment(settings, floor, context, extrudedWalls, outerWallSplitPoint,
                                       transitWallSplitPoint, ref lastUsedOuterIndex, ref usedTransitIndex);
                        currentWidthStep = settings.PreferedWidthStep;
                    }
                    start = outerWallSplitPoint;
                } while (true);
            }
        }
Esempio n. 12
0
        private static void CreateApartments(InDoorGeneratorSettings settings, Floor floor,
                                             List <IntPoint> footprint, List <List <IntPoint> > transitArea, List <Wall> connected)
        {
            var extrudedPolygons = new List <List <IntPoint> >(4);

            settings.Clipper.AddPaths(transitArea, PolyType.ptClip, true);
            settings.Clipper.AddPath(footprint, PolyType.ptSubject, true);
            settings.Clipper.Execute(ClipType.ctDifference, extrudedPolygons);
            settings.Clipper.Clear();

            //SVGBuilder.SaveToFile(extrudedPolygons, "regions_ex.svg", 0.01, 100);

            foreach (var extrudedPolygon in extrudedPolygons)
            {
                // Clipper may produce small polygons on building offsets
                if (Clipper.Area(extrudedPolygon) / DoubleScale < settings.MinimalArea)
                {
                    continue;
                }

                var    firstOuterWallIndex   = InvalidIndex;
                var    firstTransitWallIndex = InvalidIndex;
                var    outerWallCount        = 0;
                double outerWallLength       = 0;
                var    extrudedWalls         = new List <Wall>();
                var    lastItemIndex         = extrudedPolygon.Count - 1;

                var    mergePoint      = Vector2d.Empty;
                double skippedDistance = 0;
                for (var i = 0; i <= lastItemIndex; i++)
                {
                    var start = extrudedPolygon[i];
                    var end   = extrudedPolygon[i == lastItemIndex ? 0 : i + 1];

                    var isOuterWall = ClipperUtils.CalcMinDistance(start, footprint) < IntPrecisionError &&
                                      ClipperUtils.CalcMinDistance(end, footprint) < IntPrecisionError;
                    var p1 = new Vector2d(start.X / Scale, start.Y / Scale);
                    var p2 = new Vector2d(end.X / Scale, end.Y / Scale);

                    // NOTE this allows to skip artifacts of clipper offset library
                    // which I don't know to avoid by clipper API.
                    var distance = p1.DistanceTo(p2);
                    if (distance < settings.HalfTransitAreaWidth &&
                        skippedDistance < settings.TransitAreaWidth)
                    {
                        skippedDistance += distance;
                        if (mergePoint != Vector2d.Empty)
                        {
                            continue;
                        }
                        mergePoint = p1;
                        continue;
                    }
                    if (mergePoint != Vector2d.Empty)
                    {
                        p1              = mergePoint;
                        mergePoint      = Vector2d.Empty;
                        skippedDistance = 0;
                    }

                    if (isOuterWall)
                    {
                        outerWallCount++;
                        outerWallLength += ClipperUtils.Distance(start, end) / Scale;
                        if (firstOuterWallIndex == InvalidIndex)
                        {
                            firstOuterWallIndex = extrudedWalls.Count;
                        }
                    }

                    if (!isOuterWall && firstTransitWallIndex == InvalidIndex)
                    {
                        firstTransitWallIndex = extrudedWalls.Count;
                    }

                    extrudedWalls.Add(new Wall(p1, p2, isOuterWall));
                }

                firstOuterWallIndex = firstOuterWallIndex != 0
                    ? firstOuterWallIndex
                    : firstTransitWallIndex + extrudedWalls.Count - outerWallCount;

                var context = new FloorContext(outerWallCount, extrudedWalls.Count - outerWallCount,
                                               firstOuterWallIndex, firstTransitWallIndex);
                CreateAparments(settings, floor, context, extrudedWalls, connected, outerWallLength);
            }
        }