public PlacementEngine(List <SmSpace> spaces, double leaseDepth, List <SmLevel> levels, double splitInterval, IList <Polygon> corePolys = null)
        {
            _leaseOffset   = leaseDepth * _worldScale;
            medOffset      = _leaseOffset * 0.5;
            splitInterval *= _worldScale;

            inLvls = levels;

            coreCrvs = new List <Polygon>();

            if (corePolys != null)
            {
                foreach (var p in corePolys)
                {
                    var c = p;
                    coreCrvs.Add(c);
                }
            }

            firstLevel        = inLvls.OrderBy(l => l._elevation).ToList()[0];
            firstLevel._index = 0;
            boundary          = firstLevel._boundaries[0].mainPoly;

            //    System.IO.File.WriteAllText( "D:/Hypar/offsetTest.json", Newtonsoft.Json.JsonConvert.SerializeObject(boundary));

            // var orientation = boundary.ClosedCurveOrientation(Vector3d.ZAxis);
            var ssspaces = SmSpace.Jitter(spaces, 0.99).ToList();

            distinctSpaces = ssspaces.GroupBy(x => x.type).Select(y => y.First()).ToList();

            _areas  = ssspaces.OrderBy(s => s.sorter).Select(s => s.designArea).ToList();
            _spaces = ssspaces.OrderBy(s => s.sorter).Select(s => s.roomNumber.ToString()).ToList();

            _PlaceableSpaces = ssspaces;
            Polyline tempPoly = boundary.ToPolyline();

            _boundaryPoly  = tempPoly;
            _BoundaryCurve = boundary.ToPolyline();

            Console.WriteLine(boundary.ToString());

            _Core = InitCoreCrv(boundary);

            _SplitInterval = splitInterval;
            _GlobalIndex   = 0;

            _MainFace = boundary.Difference(_Core);

            InitWalls();

            _PlacedProgramSpaces = new List <SmSpace>();
        }
        public bool TrimKeep(Polygon trimCrv, Polygon toTrim, SmLevel level, out Polygon crvOut)
        {
            bool trimmed = false;

            crvOut = null;

            var diffResults = toTrim.Intersection(trimCrv);

            if (diffResults == null)
            {
                return(false);
            }

            var findInsidePoly = ReturnInsidePoly(diffResults.ToList(), trimCrv);

            if (findInsidePoly != null)
            {
                trimmed = true;
                crvOut  = findInsidePoly;
            }

            return(trimmed);
        }
        /// <summary>
        ///  Adds 'placed spaces' to PlacedSpaces list.
        /// </summary>
        /// <param name="firstLvlUnits"></param>
        /// <param name="offCrv"></param>
        /// <param name="mainCrv"></param>
        /// <param name="level"></param>
        /// <returns></returns>
        public bool TryProject(List <SmSpace> firstLvlUnits, Polygon offCrv, Polygon mainCrv, SmLevel level)
        {
            bool worked = false;

            for (int i = 0; i < firstLvlUnits.Count; i++)
            {
                var dupCrv = new Polygon(firstLvlUnits[i].poly.Vertices);

                var movedCrv = dupCrv.TransformedPolygon(new Transform(new Vector3(0, 0, offCrv.Centroid().Z)));

                var    pts = movedCrv.Vertices.ToList(); // getting unit crv poly pts
                string mess;
                bool   inBool = AllPtsIn(offCrv, pts, out mess);

                int s        = -1;
                var newRmNum = firstLvlUnits[i].roomNumber.ToString().Remove(0, 1).Insert(0, level._index.ToString());

                int parsedRmNum;
                if (Int32.TryParse(newRmNum, out parsedRmNum))
                {
                    s = parsedRmNum;
                }

                if (inBool)
                {
                    var unitN = new SmSpace(firstLvlUnits[i].type, s, true, firstLvlUnits[i].designArea, movedCrv);
                    unitN.roomLevel = level;
                    PlacedSpaces.Add(unitN);
                    worked = true;
                }
                else if (inBool == false && mess == "trim")
                {
                    Polygon crvOut;
                    if (TrimKeep(mainCrv, movedCrv, level, out crvOut))
                    {
                        var designArea = firstLvlUnits[i].designArea;

                        //if it is roughly the same size as the planned unit:
                        if (Math.Abs(crvOut.Area() / designArea) >= 0.75)
                        {
                            var unitN = new SmSpace(firstLvlUnits[i].type, s, true, firstLvlUnits[i].designArea, crvOut);
                            unitN.roomLevel = level;
                            PlacedSpaces.Add(unitN);
                            worked = true;
                        }
                        // else //try finding a closest best unit fit
                        // {
                        //     var closestDistinctUnit = FindClosestUnitType(crvOut, distinctSpaces);

                        //     if (Math.Abs(crvOut.Area() / closestDistinctUnit.designArea) >= 0.75)
                        //     {
                        //         var unitN = new SmSpace(closestDistinctUnit.type, s, true, closestDistinctUnit.designArea, crvOut);
                        //         unitN.roomLevel = level;
                        //         PlacedSpaces.Add(unitN);
                        //         worked = true;
                        //     }
                        // }
                    }
                }
            }
            return(worked);
        }
        /// <summary>
        /// Places an apartment mix in a procedurally generated mass.
        /// </summary>
        /// <param name="model">The input model.</param>
        /// <param name="input">The arguments to the execution.</param>
        /// <returns>A MJProceduralApartmentPlacerOutputs instance containing computed results and the model with any new elements.</returns>
        public static MJProceduralApartmentPlacerOutputs Execute(Dictionary <string, Model> inputModels, MJProceduralApartmentPlacerInputs input)
        {
            if (!inputModels.TryGetValue("Floors", out var levelsModel))
            {
                throw new Exception("No floors created. Please create those first.");
            }

            if (!inputModels.TryGetValue("Envelope", out var envelopesss))
            {
                throw new Exception("No envelopes available. Please make sure MJ_ProceduralMass is outputting envelopes.");
            }

            var proceduralMassData = envelopesss.AllElementsOfType <ProceduralMassData>().ToArray()[0];

            var proceduralCellSize = proceduralMassData.CellSize;

            //debuggin/ viz things
            List <ModelCurve> coreSketch = new List <ModelCurve>();

            List <SmSpace>  placedSpaces = new List <SmSpace>();
            List <SmLevel>  _levels      = new List <SmLevel>();
            PlacementEngine engine;

            //process levels & floor boundary crvs
            var allFloorProfiles = levelsModel.AllElementsOfType <Floor>().OrderBy(f => f.Elevation).ToList();

            var distinctHeights = allFloorProfiles.Select(s => s.Elevation).Distinct();

            foreach (var h in distinctHeights)
            {
                var lvl        = new SmLevel(h);
                var boundaries = new List <SmFloorBoundary>();
                foreach (var fl in allFloorProfiles)
                {
                    var sBoundary = new SmFloorBoundary(fl.Profile.Perimeter);
                    if (fl.Elevation == h)
                    {
                        boundaries.Add(sBoundary);
                    }
                }
                lvl._boundaries = boundaries;
                _levels.Add(lvl);
            }

            ///create unplaced spaces
            List <SmSpace> allUnitsPreplaced = new List <SmSpace>();
            int            count             = 0;

            for (int i = 0; i < input.UnitMix.Nodes.Count; i++)
            {
                for (int j = 0; j < input.UnitMix.Nodes[i].UnitCount; j++)
                {
                    allUnitsPreplaced.Add(new SmSpace(i, count, false, input.UnitMix.Nodes[i].UnitArea)); //check to see if 'i' corresponds to the right unit type...
                    count++;
                }
            }

            try
            {
                engine = new PlacementEngine(allUnitsPreplaced, (proceduralCellSize - 2.0) * 0.5, _levels, 1.0, input.CorePolygons);

                Console.WriteLine("cell size: " + proceduralCellSize);

                var wallCrvs = engine._Walls.Select(s => new ModelCurve(s._curve)).ToList();

                var coreCrvs = engine.coreLinesViz.Select(s => new ModelCurve(s._curve)).ToList();

                coreSketch.AddRange(coreCrvs);

                string feedbackString = "No feedback yet...";



                engine.RunFirstFloor(input.Seam, out feedbackString);

                Console.WriteLine($"Main feedback: {feedbackString}");

                List <string> debugStack;
                engine.TryStackBuilding(out debugStack);

                placedSpaces = engine.PlacedSpaces.ToList();
                Console.WriteLine("rooms should be: " + placedSpaces.Count.ToString());
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }


            var output = new MJProceduralApartmentPlacerOutputs(placedSpaces.Count, allUnitsPreplaced.Count - placedSpaces.Count);

            var materials = new Material[input.UnitMix.Nodes.Count];

            for (int i = 0; i < input.UnitMix.Nodes.Count; i++)
            {
                var col = input.UnitMix.Nodes[i].Color;
                col.Alpha    = 1.0;
                materials[i] = new Material(input.UnitMix.Nodes[i].SpaceType, col, 0.0f, 0.0f);
            }

            for (int i = 0; i < placedSpaces.Count; i++)
            {
                var representation = new Representation(new SolidOperation[] { new Extrude(placedSpaces[i].poly.Offset(-0.15)[0], placedSpaces[i].roomLevel._levelHeightToNext - 0.25, Vector3.ZAxis, false) });

                var room = new Room(placedSpaces[i].poly.Offset(-0.15)[0], Vector3.ZAxis, $"Unit {placedSpaces[i].roomNumber}", $"{placedSpaces[i].roomNumber}", $"Type {placedSpaces[i].type}", $"{placedSpaces[i].roomNumber}", placedSpaces[i].designArea, 1.0, 0.0, placedSpaces[i].roomLevel._index.ToString(), placedSpaces[i].roomLevel._elevation, placedSpaces[i].roomLevel._levelHeightToNext - 0.25, placedSpaces[i].area, new Transform(0, 0, placedSpaces[i].roomLevel._elevation), materials[placedSpaces[i].type], representation, false, Guid.NewGuid(), "");

                output.Model.AddElement(room);
            }
            output.Model.AddElements(coreSketch);

            return(output);
        }