public static bool ApplyLockRoom(AdvicePackage advice, ZoneManifest zm, ProgramManifest pm) { var bounds = advice.Bounds; foreach (ZonePackage zone in zm.Zones) { foreach (RoomPackage room in zone.Rooms) { foreach (PlacementPackage item in room.PlacedItems) { PointContainment pc = bounds.Contains(item.Dims.Center); if (pc == PointContainment.Inside) { if (advice.CapturedProgram == null) { advice.CapturedProgram = new List <PlacementPackage>(); } advice.CapturedProgram.Add(item); if (pm.ProgramPackages[item.Program.Priority].Quota > 0) { pm.ProgramPackages[item.Program.Priority].Quota--; } } } } } zm.FloorPlan.ExemptionProfiles.Add(advice.Bounds); return(true); }
public static void MaximumPlacements(RoomPackage room, ZonePackage zone, ProgramManifest pm) { var maxPlacement = room.FillOrder .Select(x => pm.ProgramPackages[x].Quota == 0 ? 999 : zone.RemainingProgramTargets[x]).ToList(); room.MaxPlacement = maxPlacement; }
public static void Popularity(ZoneManifest zm, ProgramManifest pm) { int zoneCount = zm.Zones.Count; for (int i = 0; i < zoneCount; i++) { List <int> popularity = new List <int>(zoneCount); for (int j = 0; j < zoneCount; j++) { int wantedCounter = 0; foreach (ProgramPackage program in pm.ProgramPackages) { if (program.ZonePreference[j] == i) { wantedCounter++; } } popularity.Add(wantedCounter); } zm.Zones[i].Popularity = popularity; } }
/// <summary> /// Parent method for all population methods. /// </summary> /// <param name="zm"></param> /// <param name="pm"></param> public static void Solution(ZoneManifest zm, ProgramManifest pm) { for (int i = 0; i < zm.Zones.Count; i++) { //Prepare zone for population. var activeZone = zm.Zones[i]; activeZone.RemainingProgramTargets = new List <int>(activeZone.ProgramTargets); //TODO: Figure out why population order is running opposite to slicing order so I don't have to do this. activeZone.Rooms.Reverse(); //Loop through and populate every room in active zone. for (int j = 0; j < zm.Zones[i].Rooms.Count; j++) { var activeRoom = activeZone.Rooms[j]; Stage.Terrain.Room(activeRoom, activeZone, pm); } //If zone fails to meet its predefined program targets, pass remainder on to next zone. if (i != zm.Zones.Count - 1) { Update.Zone.PassUnfulfilledTargets(activeZone, zm.Zones[i + 1], pm); } Draw.InputGeometry(activeZone); } Evaluate.QuotasMet(zm, pm); }
public static void ApplyAdvice(AdvicePackage advice, ZoneManifest zm, ProgramManifest pm) { var adviceDictionary = new Dictionary <string, Func <AdvicePackage, ZoneManifest, ProgramManifest, bool> > { { "Lock", ApplyLockRoom } }; adviceDictionary[advice.Type](advice, zm, pm); }
protected override void SolveInstance(IGH_DataAccess DA) { TestFitPackage tf = null; DA.GetData(0, ref tf); bool active = false; DA.GetData(1, ref active); if (!active) { return; } //Parse FloorPlanPackage for main working area(s). List <Brep> baseFloorRegions = Identify.FloorPlateRegions(tf); //Categorize circulation segments into "main" and "option" types. CirculationPackage circ = Identify.CirculationTypes(tf); //Sanitization: generate and remove all obstacles from workable space. (Circulation, Structure, etc.) List <Brep> validFloorRegions = Identify.AvailableFloorSpace(baseFloorRegions, tf, circ); //First-pass subdivision: coarse division based on proximity and proportion. List <Brep> optimizedFloorRegions = Identify.OptimalFloorSpaceConfiguration(validFloorRegions, tf); //Parse ProgramPackage(s) and format information/relationships into manifest. ProgramManifest pm = Identify.ProgramManifest(tf); //Assign program targets to each zone, based on priority + affinity, and subdivide to rooms. ZoneManifest zm = Identify.ZoneManifest(optimizedFloorRegions, pm, tf); //Populate zones and solve test fit. Terrain.Solution(zm, pm); List <string> debugText = new List <string>(); foreach (ZonePackage zone in zm.Zones) { string output = null; foreach (int target in zone.ProgramTargets) { output = output + target + " "; } debugText.Add(output); } DA.SetData(0, pm); DA.SetData(1, zm); DA.SetDataList(2, debugText); }
/// <summary> /// Population routine for debugging. Places one instance of highest priority item in room. /// </summary> /// <param name="room"></param> /// <param name="zone"></param> /// <param name="pm"></param> public static void PlaceOne(RoomPackage room, ZonePackage zone, ProgramManifest pm) { var firstWithTarget = pm.ProgramPackages[zone.ProgramTargets.IndexOf(zone.ProgramTargets.First(x => x > 0))]; var candidate = Stage.Program.InRoom(room, firstWithTarget); if (Collision.PlacementIsValid(room, candidate, pm)) { room.PlacedItems.Add(candidate); room.NumProgramsPlaced[firstWithTarget.Priority] = room.NumProgramsPlaced[firstWithTarget.Priority] + 1; } }
public static void ProgramPriority(ZoneManifest zm, ProgramManifest pm) { int zoneCount = zm.Zones.Count; int programCount = pm.ProgramPackages.Count; for (int i = 0; i < zoneCount; i++) { List <int> programPriority = new List <int>(); for (int j = 0; j < zoneCount; j++) { //Skip if we know zone is unloved at this priority. if (zm.Zones[i].Popularity[j] == 0) { continue; } List <ProgramPackage> suitors = new List <ProgramPackage>(); List <int> programIndex = new List <int>(); //Grab programs that want to be with this zone, in order of preference. for (int k = 0; k < programCount; k++) { if (pm.ProgramPackages[k].ZonePreference[j] == i) { suitors.Add(pm.ProgramPackages[k]); programIndex.Add(k); } } //In the case of multiple suitors, tiebreak based on program priority. if (suitors.Count == 1) { programPriority.Add(programIndex[0]); } else { for (int k = 0; k < programCount; k++) { for (int t = 0; t < suitors.Count; t++) { if (suitors[t].Priority == k) { programPriority.Add(programIndex[t]); } } } } } zm.Zones[i].ProgramPriority = programPriority; } }
public static void ProgramFillOrder(RoomPackage room, ZonePackage zone, ProgramManifest pm) { var fillOrder = Enumerable.Range(0, zone.RemainingProgramTargets.Count) .Where(i => zone.RemainingProgramTargets[i] > 0 && pm.ProgramPackages[i].Quota != 0).ToList() .Select(x => pm.ProgramPackages[x]) .OrderByDescending(x => x.Dims.Height).ToList() .Select(x => x.Priority).ToList(); fillOrder.AddRange(pm.ProgramPackages .Where(x => x.Quota == 0).ToList() .Select(x => x.Priority)); room.FillOrder = fillOrder; }
/// <summary> /// Selects best population strategy for room based on current conditions. /// </summary> /// <param name="room"></param> /// <param name="zone"></param> /// <param name="pm"></param> public static void Room(RoomPackage room, ZonePackage zone, ProgramManifest pm) { //Prepare room for population routine based on current fill progress. room.PlacedItems = new List <PlacementPackage>(); Stage.Room.BaseAnchors(room); Stage.Room.ProgramFillOrder(room, zone, pm); Stage.Room.MaximumPlacements(room, zone, pm); //Select best population strategy based on room geometry and quotas. //TODO: Automate selection process and write base suite of strategies. Populate.ByMostRows(room, zone, pm); //After room is filled, adjust remaining quota for zone. Update.Zone.RemainingProgramTargets(zone, room); }
public static void QuotasMet(ZoneManifest zm, ProgramManifest pm) { var programsPlaced = pm.ProgramPackages.Select(x => 0).ToList(); foreach (ZonePackage zone in zm.Zones) { foreach (RoomPackage room in zone.Rooms) { for (int i = 0; i < room.NumProgramsPlaced.Count; i++) { programsPlaced[i] = programsPlaced[i] + room.NumProgramsPlaced[i]; } } } zm.PlacementTotals = programsPlaced; //Utils.Debug.PrintList(ref programsPlaced); }
public static ZoneManifest ZoneManifest(List <Brep> zonesToParse, ProgramManifest pm, TestFitPackage tf) { List <ZonePackage> zpList = new List <ZonePackage>(); double totalArea = 0; foreach (Brep zone in zonesToParse) { ZonePackage zp = new ZonePackage(zone); if (zp.BaseArea < 10) { //Stopgap solution for micro-zones generated by weird circulation corners. continue; } totalArea = totalArea + zp.BaseArea; EdgeCurves edgeCurvePackage = Select.ZoneEdgeCurves(zone, tf, zonesToParse); Update.Zone.EdgeClassification(zp, edgeCurvePackage); Update.Zone.Adjacencies(zp); Update.Zone.AffinityType(zp); zpList.Add(zp); } ZoneManifest zm = new ZoneManifest(zpList, totalArea) { FloorPlan = tf.FloorPlanPackage }; Update.Program.ZonePreference(pm, zm); Update.Zone.Popularity(zm, pm); Update.Zone.ProgramPriority(zm, pm); Update.Zone.ReservedArea(zm, pm); Update.Zone.ProgramTargets(zm, pm); Update.Zone.RoomConfiguration(zm, pm); return(zm); }
public static ProgramManifest ProgramManifest(TestFitPackage tf) { //Parse base statistics from floor areas. var approximateTotalArea = tf.FloorPlanPackage.BaseArea; //Perform measurements and confirmations. var isPossible = Confirm.Program.RequestIsPossible(tf.ProgramPackages, approximateTotalArea); var maximizedCount = Confirm.Program.MaximizedCount(tf.ProgramPackages); Update.Program.Distribution(tf); Update.Program.Target(tf); Update.Program.Affinity(tf); Update.Program.Enmity(tf); Update.Program.Priority(tf); ProgramManifest pm = new ProgramManifest(tf.ProgramPackages, isPossible, maximizedCount); pm.AllAdvice = tf.AllAdvice; return(pm); }
public static void ReservedArea(ZoneManifest zm, ProgramManifest pm) { int zoneCount = zm.Zones.Count; int programCount = pm.ProgramPackages.Count; for (int i = 0; i < zoneCount; i++) { List <List <double> > reservedArea = new List <List <double> >(); for (int j = 0; j < zoneCount; j++) { List <double> reservedAtThisPriority = new List <double>(); for (int k = 0; k < programCount; k++) { if (zm.Zones[i].Popularity[j] == 0) { reservedAtThisPriority.Add(0); } else { double portion = zm.Zones[i].BaseArea / zm.Zones[i].Popularity[j]; if (pm.ProgramPackages[k].ZonePreference[j] == i) { reservedAtThisPriority.Add(portion); } else { reservedAtThisPriority.Add(0); } } } reservedArea.Add(reservedAtThisPriority); } zm.Zones[i].ReservedArea = reservedArea; } }
protected override void SolveInstance(IGH_DataAccess DA) { ProgramManifest pmOld = null; if (!DA.GetData(0, ref pmOld)) { return; } var pm = Utils.DeepClone(pmOld); ZoneManifest zmOld = null; if (!DA.GetData(1, ref zmOld)) { return; } var zm = Utils.DeepClone(zmOld); var allAdvice = new List <AdvicePackage>(); DA.GetDataList(2, allAdvice); if (pm.AllAdvice != null) { allAdvice.AddRange(pm.AllAdvice); } foreach (AdvicePackage advice in allAdvice) { Logic.Update.Iteration.ApplyAdvice(advice, zm, pm); } var tf = new TestFitPackage(zm.FloorPlan, pm.ProgramPackages, allAdvice); DA.SetData(0, tf); }
protected override void SolveInstance(IGH_DataAccess DA) { ProgramManifest pm = null; if (!DA.GetData(0, ref pm)) { return; } DA.SetDataList(0, pm.ProgramPackages); DA.SetDataList(1, pm.AllAdvice); List <string> zonePreferences = new List <string>(); List <double> distributions = new List <double>(); List <int> priorities = new List <int>(); foreach (ProgramPackage program in pm.ProgramPackages) { string preferenceCache = null; foreach (int val in program.ZonePreference) { preferenceCache = preferenceCache + val + " "; } zonePreferences.Add(preferenceCache); distributions.Add(program.Distribution); priorities.Add(program.Priority); } DA.SetDataList(2, zonePreferences); DA.SetDataList(3, distributions); DA.SetDataList(4, priorities); }
/// <summary> /// Breaks initial zones into generally rectangular pieces. /// Subdivides those pieces into rooms based on program sizes and quotas. /// </summary> /// <param name="zm"></param> /// <param name="pm"></param> public static void RoomConfiguration(ZoneManifest zm, ProgramManifest pm) { foreach (ZonePackage zone in zm.Zones) { zone.Rooms = new List <RoomPackage>(); List <Brep> rectangularizedZoneRegions = Breps.Rectangularize(zone); //Slice rectangularized zone regions into room lanes. foreach (Brep region in rectangularizedZoneRegions) { Update.Room.LaneConfiguration(region, zone, pm); } //Perform final measurements and prepare rooms for population. for (int i = 0; i < zone.Rooms.Count; i++) { var room = zone.Rooms[i]; Update.Room.Orientation(room, zone); Update.Room.ProgramHint(room, zone, i); } } }
/// <summary> /// Since the zone-specific targets are estimates, if a zone is unable to meet its quotas, it passes them along to the next. /// </summary> /// <param name="zone"></param> /// <param name="nextZone"></param> /// <param name="pm"></param> public static void PassUnfulfilledTargets(ZonePackage zone, ZonePackage nextZone, ProgramManifest pm) { for (int i = 0; i < zone.RemainingProgramTargets.Count; i++) { var activeRemainder = zone.RemainingProgramTargets[i]; if (activeRemainder > 0 && pm.ProgramPackages[i].Quota != 0) { nextZone.ProgramTargets[i] = nextZone.ProgramTargets[i] + activeRemainder; } } }
/// <summary> /// Method responsible for estimating the effectiveness of each lane. /// Plan is to use this in the future to segment lanes if needed. /// </summary> /// <param name="lp"></param> /// <param name="region">Region for entire zone.</param> /// <param name="splitters">All currently determined splitters.</param> /// <param name="pm"></param> public static void ProjectedFill(LanePackage lp, Brep region, List <Curve> splitters, ProgramManifest pm) { //Determine approximate depth of lane. var depth = 0.0; if (splitters.Count == 1) { depth = Curves.TrimWithClosedCurve(Utils.GetRegionPerimeter(region), splitters[0]).GetLength(); } else { var depthL = Curves.TrimWithClosedCurve(Utils.GetRegionPerimeter(region), splitters[splitters.Count - 2]).GetLength(); var depthR = Curves.TrimWithClosedCurve(Utils.GetRegionPerimeter(region), splitters[splitters.Count - 1]).GetLength(); depth = (depthL + depthR) / 2; } lp.Depth = depth; lp.ProjectedFill = new List <int>(); foreach (ProgramPackage program in pm.ProgramPackages) { lp.ProjectedFill.Add(0); } for (int i = 0; i < lp.Programs.Count; i++) { var selectedProgramIndex = lp.Programs[i]; var selectedProgram = pm.ProgramPackages[selectedProgramIndex]; var selectedProgramDims = selectedProgram.Dims; var estimatedFill = Convert.ToInt32(Math.Floor(depth / selectedProgramDims.Width)); if (selectedProgram.AccessDirections == "1000") { estimatedFill = estimatedFill * 2; } lp.ProjectedFill[selectedProgramIndex] = estimatedFill; } }
/// <summary> /// Slices each zone into rectangular "lanes" based on dimensions of program targets. /// </summary> /// <param name="region"></param> /// <param name="zone"></param> /// <param name="pm"></param> public static void LaneConfiguration(Brep region, ZonePackage zone, ProgramManifest pm) { var zoneTargets = new List <int>(zone.ProgramTargets); var totalProjectedFill = new List <int>(); foreach (int target in zoneTargets) { totalProjectedFill.Add(0); } var regionInfo = new CurveBounds(Utils.GetBoundingBoxCurve(region)); var splitterCurves = new List <Curve>(); var sliceDirectionIsVertical = Confirm.RegionProportionIsVertical(region); var startVal = sliceDirectionIsVertical ? regionInfo.YMin : regionInfo.XMin; var maxVal = sliceDirectionIsVertical ? regionInfo.YMax : regionInfo.XMax; var activeSliceVal = startVal; var allLanePackages = new List <LanePackage>(); //Preflight checks. var targets = Confirm.Zone.TargetFulfilled(totalProjectedFill, zone.ProgramTargets); var inbounds = activeSliceVal < maxVal; //RhinoApp.WriteLine("Targets fulfilled: {0} | In bounds: {1}", targets, inbounds); while (!Confirm.Zone.TargetFulfilled(totalProjectedFill, zone.ProgramTargets) && activeSliceVal < maxVal) { //Slice lane based on remaining program. LanePackage lp = Select.NextLanePayload(zone, pm, totalProjectedFill, activeSliceVal, maxVal); allLanePackages.Add(lp); //RhinoApp.WriteLine("{0} => {1}", activeSliceVal, lp.SlicePosition); activeSliceVal = lp.SlicePosition; var splitter = sliceDirectionIsVertical ? new LineCurve(new Point2d(regionInfo.XMin, activeSliceVal), new Point2d(regionInfo.XMax, activeSliceVal)) : new LineCurve(new Point2d(activeSliceVal, regionInfo.YMax), new Point2d(activeSliceVal, regionInfo.YMin)); splitterCurves.Add(splitter); Update.Room.ProjectedFill(lp, region, splitterCurves, pm); for (int i = 0; i < lp.ProjectedFill.Count; i++) { totalProjectedFill[i] = totalProjectedFill[i] + lp.ProjectedFill[i]; } } zone.LanePackages = allLanePackages; var zoneSlices = Breps.SplitByCurves(region, splitterCurves); //RhinoApp.WriteLine("{0} splitter curves & {1} rooms.", splitterCurves.Count.ToString(), zoneSlices.Count); foreach (Brep lane in zoneSlices) { zone.Rooms.Add(new RoomPackage(lane, pm.ProgramPackages.Count)); } }
/// <summary> /// Populate room by marching along perimeter. Generate interior islands as necessary. /// </summary> /// <param name="room"></param> /// <param name="zone"></param> /// <param name="pm"></param> public static void ByPerimeter(RoomPackage room, ZonePackage zone, ProgramManifest pm) { //TODO: Write this routine. Must solve lane generation problems for curvilinear zones first. }
/// <summary> /// Basic collision detection routine for single item placement. /// </summary> /// <param name="room">Room currently being populated.</param> /// <param name="candidate">PlacementPackaged being checked for fidelity.</param> /// <param name="pm"></param> /// <returns></returns> public static bool PlacementIsValid(RoomPackage room, PlacementPackage candidate, ProgramManifest pm) { //Verify placed item does not clash with previously placed items. foreach (PlacementPackage placedItem in room.PlacedItems) { if (Confirm.CurvesIntersect(placedItem.Bounds, candidate.Bounds, true)) { if (Confirm.CurvesIntersect(placedItem.Bounds, candidate.Bounds, false)) { return(false); } CurveIntersections ccx = Intersection.CurveCurve(placedItem.Bounds, candidate.Bounds, 0.1, 0.1); if (ccx.Count(x => x.IsOverlap) > 1) { return(false); } } PointContainment insideCheck = placedItem.Bounds.Contains(candidate.Bounds.GetBoundingBox(Plane.WorldXY).Center); if (insideCheck == PointContainment.Inside || insideCheck == PointContainment.Coincident) { return(false); } } //Verify item is completely within boundary of room. foreach (Curve edgeCurve in room.AllEdgeCurves) { if (Confirm.CurvesIntersect(candidate.Bounds, edgeCurve, false)) { return(false); } /* * if (!Confirm.PointInRegion(room.Region, new CurveBounds(candidate.Bounds).Center)) * { * return false; * } */ } //Verify program area is not larger than total room area. if (room.Region.GetArea() < pm.ProgramPackages[candidate.ProgramIndex].OccupationArea) { return(false); } return(true); }
public static void ZonePreference(ProgramManifest pm, ZoneManifest zm) { foreach (ProgramPackage program in pm.ProgramPackages) { int zoneCount = zm.Zones.Count; List <int> zonePreference = new List <int>(zoneCount); //Add most preferred zones first. for (int i = 0; i < zoneCount; i++) { if (program.Affinity.Count == 0) { break; } foreach (int preferred in program.Affinity) { if (zm.Zones[i].AffinityType == preferred) { zonePreference.Add(i); } } } //Then add neutral zones. for (int i = 0; i < zoneCount; i++) { if (zonePreference.Contains(i) == false) { if (program.Enmity.Count == 0) { zonePreference.Add(i); } else { foreach (int dispreferred in program.Enmity) { if (zm.Zones[i].AffinityType != dispreferred) { zonePreference.Add(i); } } } } } //Last, add dispreferred zones, making sure least preferred is last. for (int i = 0; i < zoneCount; i++) { for (int j = program.Enmity.Count - 1; j >= 0; j--) { if (program.Enmity[j] == zm.Zones[i].AffinityType) { zonePreference.Add(i); } } } program.ZonePreference = zonePreference; } }
/// <summary> /// Negotiates program quotas, affinities, and priority to assign each zone a "sub-quota" to meet. /// </summary> /// <param name="zm"></param> /// <param name="pm"></param> public static void ProgramTargets(ZoneManifest zm, ProgramManifest pm) { int numZones = zm.Zones.Count; int numPrograms = pm.ProgramPackages.Count; #region area based distribution //Distribute targets between zones based on their proportion of the total area. foreach (ZonePackage zone in zm.Zones) { List <double> proportionalTargets = new List <double>(); foreach (ProgramPackage program in pm.ProgramPackages) { proportionalTargets.Add(program.Target * (zone.BaseArea / zm.TotalArea)); } zone.ProportionalTargets = proportionalTargets; } #endregion #region safe rounding to integers //Round these proportional targets while maintaining overall quota total. List <int> newTargets = new List <int>(); for (int i = 0; i < numPrograms; i++) { int activeTarget = pm.ProgramPackages[i].Target; List <double> distForActiveProgram = new List <double>(); foreach (ZonePackage zone in zm.Zones) { distForActiveProgram.Add(zone.ProportionalTargets[i]); } newTargets = Utils.RoundWithinTotal(distForActiveProgram, activeTarget); for (int j = 0; j < zm.Zones.Count; j++) { if (i == 0) { zm.Zones[j].ProgramTargets = new List <int>(); } //RhinoApp.WriteLine(zm.Zones[j].ProgramTargets.Count.ToString()); zm.Zones[j].ProgramTargets.Add(newTargets[j]); } } #endregion #region horse trading //In reverse-prioroity order, maximize program target in zone. for (int i = numPrograms - 1; i >= 0; i--) { ProgramPackage activeProgram = pm.ProgramPackages[i]; //Decrement by zone target, on move into new zone, and by one for each trade. int remainingToMove = activeProgram.Target; //Increment when activeZone runs out of ejectionProgram quota to send. Cannot be >= numPrograms. int ejectionIndexModifier = 0; //Increment when sourceZone runs out of activeProgram quota to pull. Cannot be >= numZones. int sourceZoneIndexModifier = 0; //RhinoApp.WriteLine("---Beginning horse trade for program {0}.", i.ToString()); for (int j = 0; j < numZones; j++) { //Prepare base information for start of trading procedure in each zone. ZonePackage activeZone = zm.Zones[activeProgram.ZonePreference[j]]; ejectionIndexModifier = 0; sourceZoneIndexModifier = 0; double baseReservedArea = activeZone.ReservedArea[j][i]; double reservedAreaRemaining = baseReservedArea - (activeZone.ProgramTargets[i] * activeProgram.OccupationArea); //RhinoApp.WriteLine(String.Format("*Maximizing zone {0} (#{1} - {3}) ({2} sqft available.)", activeProgram.ZonePreference[j].ToString(), j.ToString(), reservedAreaRemaining.ToString(), activeZone.ToString())); if (reservedAreaRemaining < 0) { //Often for maximized program, and sometimes for others, the default distribution will be larger than the reserved area. //This is most common when the zone is desired by several programs. //Since the current setup solves from lowest priority to highest, the higher priority programs will kick out the others. //RhinoApp.WriteLine("...negative remaining area. Skip!"); continue; } //Pull from zone that activeProgram least prefers. while (zm.Zones[activeProgram.ZonePreference[numZones - (sourceZoneIndexModifier + 1)]].ProgramTargets[i] <= 0) { //RhinoApp.WriteLine(">No program in zone {0} available to pull ({1}), incrementing zone.", activeProgram.ZonePreference[(numZones - (sourceZoneIndexModifier + 1))].ToString(), zm.Zones[activeProgram.ZonePreference[numZones - (sourceZoneIndexModifier + 1)]].ToString()); sourceZoneIndexModifier++; if (sourceZoneIndexModifier >= numZones) { sourceZoneIndexModifier--; break; } } ZonePackage sourceZone = zm.Zones[activeProgram.ZonePreference[numZones - (sourceZoneIndexModifier + 1)]]; //RhinoApp.WriteLine("First source zone: {0}", sourceZone.ToString()); //Eject program from activeZone that least wants to be there. //Its preference for the sourceZone is not considered. It should be thankful to be able to leave the place it hates so much. while (activeZone.ProgramTargets[activeZone.ProgramPriority[numPrograms - (ejectionIndexModifier + 1)]] <= 0) { int activeIndex = activeZone.ProgramPriority[numPrograms - (ejectionIndexModifier + 1)]; //RhinoApp.WriteLine(">No program {0} available to eject, incrementing program.", activeIndex.ToString()); ejectionIndexModifier++; if (ejectionIndexModifier >= numPrograms) { ejectionIndexModifier--; break; } } int programToEjectIndex = activeZone.ProgramPriority[numPrograms - (ejectionIndexModifier + 1)]; //RhinoApp.WriteLine("First program to eject: {0}", programToEjectIndex.ToString()); double ejectedProgramSizeCache = 0; int currentLoopEjectedProgramDelta = 0; //Preflight tests: bool remaining = (remainingToMove != 0); bool availableToEject = (activeZone.ProgramTargets[programToEjectIndex] > 0); bool reservedAvailable = (reservedAreaRemaining > 0); bool availableToPull = (sourceZone.ProgramTargets[i] > 0); //RhinoApp.WriteLine(String.Format("Preflight tests: {0} | {1} | {2} | {3}", remaining, availableToEject, reservedAvailable, availableToPull)); //While there is still activeProgram to optimize, other program to send from activeZone, reserved area to fill in activeZone, and program to receive from sourceZone... //Proceed with horse trading. while (remainingToMove != 0 && activeZone.ProgramTargets[programToEjectIndex] > 0 && reservedAreaRemaining > 0 && sourceZone.ProgramTargets[i] > 0) { //Skip loop if trying to eject self. if (programToEjectIndex == i) { ejectionIndexModifier++; if (ejectionIndexModifier >= numPrograms) { break; } programToEjectIndex = activeZone.ProgramPriority[numPrograms - (ejectionIndexModifier + 1)]; //RhinoApp.WriteLine(">Self-ejection detected, moving on to program {0}.", programToEjectIndex); continue; } //Make necessary measurements for potential horse trade. int currentLoopActiveProgramDelta = 0; currentLoopEjectedProgramDelta++; double sizeOfProgramToEject = (pm.ProgramPackages[programToEjectIndex].OccupationArea * currentLoopEjectedProgramDelta) + ejectedProgramSizeCache; //RhinoApp.WriteLine("Blip."); //RhinoApp.WriteLine("Testing swap of {0}x program {1}. (Area {2}% filled.)", currentLoopEjectedProgramDelta.ToString(), programToEjectIndex.ToString(), ((sizeOfProgramToEject / activeProgram.OccupationArea) * 100).ToString()); //Because the area of one staged program may be multiple factors larger than the area of the activeProgram, we can't just increment by one. if (sizeOfProgramToEject > activeProgram.OccupationArea) { int potentialSwap = Convert.ToInt32(Math.Floor(sizeOfProgramToEject / activeProgram.OccupationArea)); int remainingAtSource = sourceZone.ProgramTargets[i]; while (remainingToMove > 0 && potentialSwap > 0) { potentialSwap--; remainingToMove--; currentLoopActiveProgramDelta++; } //RhinoApp.WriteLine(String.Format("Trade initiated! {0} ejected ({1} still available) | {2} pulled ({3} remaining at source)", currentLoopEjectedProgramDelta.ToString(), activeZone.ProgramTargets[programToEjectIndex].ToString(), currentLoopActiveProgramDelta.ToString(), sourceZone.ProgramTargets[i].ToString())); reservedAreaRemaining = reservedAreaRemaining - sizeOfProgramToEject; //RhinoApp.WriteLine("{0} reserved area left to fill.", reservedAreaRemaining.ToString()); ejectedProgramSizeCache = 0; } //If a trade has been identified, complete it and check for necessary changes before next loop. if (currentLoopActiveProgramDelta > 0) { sourceZone.ProgramTargets[i] = sourceZone.ProgramTargets[i] - currentLoopActiveProgramDelta; activeZone.ProgramTargets[i] = activeZone.ProgramTargets[i] + currentLoopActiveProgramDelta; activeZone.ProgramTargets[programToEjectIndex] = activeZone.ProgramTargets[programToEjectIndex] - currentLoopEjectedProgramDelta; sourceZone.ProgramTargets[programToEjectIndex] = sourceZone.ProgramTargets[programToEjectIndex] + currentLoopEjectedProgramDelta; remainingToMove = remainingToMove - currentLoopActiveProgramDelta; currentLoopEjectedProgramDelta = 0; currentLoopActiveProgramDelta = 0; if (remainingToMove == 0) { //Operation is complete, move to next program. (There is a second break outside of the while loop for this.) activeZone.ReservedArea[j][i] = reservedAreaRemaining; if (sourceZone.ProgramTargets[i] < 0) { int delta = Math.Abs(sourceZone.ProgramTargets[i]); sourceZone.ProgramTargets[i] = sourceZone.ProgramTargets[i] + delta; activeZone.ProgramTargets[i] = activeZone.ProgramTargets[i] - delta; } if (activeZone.ProgramTargets[programToEjectIndex] < 0) { int delta = Math.Abs(activeZone.ProgramTargets[programToEjectIndex]); activeZone.ProgramTargets[programToEjectIndex] = activeZone.ProgramTargets[programToEjectIndex] + delta; sourceZone.ProgramTargets[programToEjectIndex] = sourceZone.ProgramTargets[programToEjectIndex] - delta; } break; } if (reservedAreaRemaining < activeProgram.OccupationArea) { //activeZone has run out of available space, reset values for next zone. ejectionIndexModifier = 0; sourceZoneIndexModifier = 0; break; } if (sourceZone.ProgramTargets[i] <= 0) { //sourceZone has run out of activeProgram to pull. Increment sourceZoneIndexModifier. if (sourceZone.ProgramTargets[i] < 0) { int delta = Math.Abs(sourceZone.ProgramTargets[i]); sourceZone.ProgramTargets[i] = sourceZone.ProgramTargets[i] + delta; activeZone.ProgramTargets[i] = activeZone.ProgramTargets[i] - delta; } currentLoopEjectedProgramDelta = 0; ejectionIndexModifier = 0; programToEjectIndex = activeZone.ProgramPriority[numPrograms - (ejectionIndexModifier + 1)]; sourceZoneIndexModifier++; sourceZone = zm.Zones[activeProgram.ZonePreference[numZones - (sourceZoneIndexModifier + 1)]]; //continue; } if (activeZone.ProgramTargets[programToEjectIndex] <= 0) { //activeZone has run out of currentProgram to eject. Increment ejectionIndexModifier. if (activeZone.ProgramTargets[programToEjectIndex] < 0) { int delta = Math.Abs(activeZone.ProgramTargets[programToEjectIndex]); activeZone.ProgramTargets[programToEjectIndex] = activeZone.ProgramTargets[programToEjectIndex] + delta; sourceZone.ProgramTargets[programToEjectIndex] = sourceZone.ProgramTargets[programToEjectIndex] - delta; } currentLoopEjectedProgramDelta = 0; ejectionIndexModifier++; programToEjectIndex = ejectionIndexModifier >= numPrograms ? numPrograms : activeZone.ProgramPriority[numPrograms - (ejectionIndexModifier + 1)]; //continue; } if (programToEjectIndex >= numPrograms) { //No more program available to pull in zone. Reset board and increment sourceZone. currentLoopEjectedProgramDelta = 0; ejectedProgramSizeCache = 0; ejectionIndexModifier = 0; programToEjectIndex = activeZone.ProgramPriority[numPrograms - (ejectionIndexModifier + 1)]; sourceZoneIndexModifier++; sourceZone = zm.Zones[activeProgram.ZonePreference[numZones - (sourceZoneIndexModifier + 1)]]; } if (sourceZoneIndexModifier >= numZones) { //No more zones to pull from. Program is as good as it's going to get. remainingToMove = 0; break; } //If none of those tests trigger, things are fine and the next loop in this zone will begin. } //Endchecks for when a trade is not completed. If any while-condition is about to fail, increment necessary modifier, prepare for next loop, or complete process. if (currentLoopActiveProgramDelta == 0) { if (remainingToMove == 0) { //Commit any outstanding deltas, move to next program. (There is a second break outside of the while loop for this.) activeZone.ReservedArea[j][i] = reservedAreaRemaining; break; } if (reservedAreaRemaining < activeProgram.OccupationArea) { //activeZone has run out of available space, reset values for next zone. programToEjectIndex = 0; sourceZoneIndexModifier = 0; break; } if (sourceZone.ProgramTargets[i] <= 0) { //sourceZone has run out of activeProgram to pull. Increment sourceZoneIndexModifier. if (sourceZone.ProgramTargets[i] < 0) { int delta = Math.Abs(sourceZone.ProgramTargets[i]); sourceZone.ProgramTargets[i] = sourceZone.ProgramTargets[i] + delta; activeZone.ProgramTargets[i] = activeZone.ProgramTargets[i] - delta; } currentLoopEjectedProgramDelta = 0; ejectionIndexModifier = 0; programToEjectIndex = activeZone.ProgramPriority[numPrograms - (ejectionIndexModifier + 1)]; sourceZoneIndexModifier++; sourceZone = zm.Zones[activeProgram.ZonePreference[numZones - (sourceZoneIndexModifier + 1)]]; //continue; } if (activeZone.ProgramTargets[programToEjectIndex] <= 0) { //activeZone has run out of currentProgram to eject. Increment ejectionIndexModifier. if (activeZone.ProgramTargets[programToEjectIndex] < 0) { int delta = Math.Abs(activeZone.ProgramTargets[programToEjectIndex]); activeZone.ProgramTargets[programToEjectIndex] = activeZone.ProgramTargets[programToEjectIndex] + delta; sourceZone.ProgramTargets[programToEjectIndex] = sourceZone.ProgramTargets[programToEjectIndex] - delta; } currentLoopEjectedProgramDelta = 0; ejectionIndexModifier++; programToEjectIndex = ejectionIndexModifier >= numPrograms ? numPrograms : activeZone.ProgramPriority[numPrograms - (ejectionIndexModifier + 1)]; ejectedProgramSizeCache = programToEjectIndex >= numPrograms ? 0 : currentLoopEjectedProgramDelta * pm.ProgramPackages[programToEjectIndex].OccupationArea; //ejectedProgramCountCache = 0; //continue; } if (ejectionIndexModifier >= numPrograms) { //No more program available to pull in zone. Reset board and increment sourceZone. currentLoopEjectedProgramDelta = 0; ejectedProgramSizeCache = 0; ejectionIndexModifier = 0; programToEjectIndex = activeZone.ProgramPriority[numPrograms - (ejectionIndexModifier + 1)]; sourceZoneIndexModifier++; sourceZone = zm.Zones[activeProgram.ZonePreference[numZones - (sourceZoneIndexModifier + 1)]]; } if (sourceZoneIndexModifier >= numZones) { //No more zones to pull from. Program is as good as it's going to get. remainingToMove = 0; break; } //If none of these tests trigger, the next loop should be able to run. Cache any contributions and begin next loop in this zone. //ejectedProgramCountCache = currentLoopEjectedProgramDelta; } } if (remainingToMove == 0) { break; } //Program has been optimized in zone. Clear all holdings. activeZone.ReservedArea[j][i] = reservedAreaRemaining; } #region forfeit procedure //Program has been optimized. Forfeit any remaining reserved area. //RhinoApp.WriteLine("---Procedure complete, forfeiting..."); int zoneCount = 0; foreach (ZonePackage zone in zm.Zones) { //RhinoApp.WriteLine("Zone number {0}: ", zoneCount.ToString()); zoneCount++; int priorityCount = 0; foreach (List <double> reservedAreas in zone.ReservedArea) { //RhinoApp.WriteLine("Priority level {0}: ", priorityCount.ToString()); priorityCount++; int remainingSuitors = 0; List <int> remainingSuitorIndices = new List <int>(); double availableToRedistribute = reservedAreas[i]; if (availableToRedistribute < 0) { reservedAreas[i] = 0; //RhinoApp.WriteLine("None available."); continue; } for (int c = 0; c < numPrograms; c++) { if (c != i && reservedAreas[c] != 0) { remainingSuitors++; remainingSuitorIndices.Add(c); } } if (remainingSuitors == 0) { reservedAreas[i] = 0; //RhinoApp.WriteLine("No one to distribute to."); continue; } double areaToDistribute = availableToRedistribute / remainingSuitors; //RhinoApp.WriteLine("Distributing {0} to {1} others.", areaToDistribute.ToString(), remainingSuitors.ToString()); foreach (int index in remainingSuitorIndices) { reservedAreas[index] = reservedAreas[index] + areaToDistribute; } reservedAreas[i] = 0; } } #endregion } #endregion }
/// <summary> /// Populates room in rows along its Y axis. Packs along the left (-X) edge until passing the room's midpoint, then packs along right edge until full. /// </summary> /// <param name="room"></param> /// <param name="zone"></param> /// <param name="pm"></param> public static void ByMostRows(RoomPackage room, ZonePackage zone, ProgramManifest pm) { var lexFull = false; var rexFull = false; var openZones = !lexFull || !rexFull; var anchor = Point3d.Unset; //Parent population method. while (room.MaxPlacement.Select(x => x > 0).Any() && openZones) { //Iterate through programs as staged. // (Chuck) Important: room.MaxPlacement aligns to program indices in room.FillOrder. // These DO NOT align to pm.ProgramPackages, and often don't even contain all programs. // To get data for the active program from its ProgramPackage, or to use a list that aligns with it, reference with index at room.FillOrder[i]. for (int i = 0; i < room.MaxPlacement.Count; i++) { var activeProgram = pm.ProgramPackages[room.FillOrder[i]]; var activeProgramIndex = room.FillOrder[i]; //Begin packing program. while (room.MaxPlacement[i] - room.NumProgramsPlaced[activeProgramIndex] > 0) { PlacementPackage candidate = null; //Start in left half. if (!lexFull) { candidate = Stage.Program.ForLeftLane(room, Stage.Program.InRoom(room, activeProgram)); //Move candidate to next position. anchor = room.NextAnchor == Point3d.Unset ? room.BaseAnchorLeft : room.NextAnchor; candidate.Bounds.Transform(Transform.Translation(anchor - room.BaseAnchorLeft)); //Verify that placement is valid and stage next anchor point accordingly. if (Collision.PlacementIsValid(room, candidate, pm)) { room.PlacedItems.Add(candidate); room.NumProgramsPlaced[activeProgramIndex]++; candidate.Dims = new CurveBounds(candidate.Bounds); candidate.Orientation.Origin.Transform(Transform.Translation(anchor - room.BaseAnchorLeft)); var buffer = 0.0; if (candidate.Program.AccessDirections == "1111") { buffer = 3; } room.PrevAnchor = anchor; room.NextAnchor = Stage.Room.NextAnchorRows(room, activeProgram, anchor, 1, buffer); } //Otherwise shift anchor slightly and retry. else { room.PrevAnchor = anchor; room.NextAnchor = Stage.Room.NextAnchorRows(room, activeProgram, anchor, 1, 0.1); } if (!Confirm.PointInRegion(room.Region, room.NextAnchor)) { if (i == room.MaxPlacement.Count - 1) { room.NextAnchor = Point3d.Unset; lexFull = true; break; } room.NextAnchor = Point3d.Unset; break; } continue; } if (!rexFull) { candidate = Stage.Program.ForRightLane(room, Stage.Program.InRoom(room, activeProgram)); //Move candidate to next position. anchor = room.NextAnchor == Point3d.Unset ? room.BaseAnchorRight : room.NextAnchor; candidate.Bounds.Transform(Transform.Translation(anchor - room.BaseAnchorRight)); //Verify that placement is valid and stage next anchor point accordingly. if (Collision.PlacementIsValid(room, candidate, pm)) { room.PlacedItems.Add(candidate); room.NumProgramsPlaced[activeProgramIndex]++; candidate.Dims = new CurveBounds(candidate.Bounds); candidate.Orientation.Origin.Transform(Transform.Translation(anchor - room.BaseAnchorRight)); var buffer = 0.0; if (candidate.Program.AccessDirections == "1111") { buffer = 3; } room.PrevAnchor = anchor; room.NextAnchor = Stage.Room.NextAnchorRows(room, activeProgram, anchor, 1, buffer); } //Otherwise shift anchor slightly and retry. else { room.PrevAnchor = anchor; room.NextAnchor = Stage.Room.NextAnchorRows(room, activeProgram, anchor, 1, 0.1); } if (!Confirm.PointInRegion(room.Region, room.NextAnchor)) { if (i == room.MaxPlacement.Count - 1) { room.NextAnchor = Point3d.Unset; rexFull = true; break; } room.NextAnchor = Point3d.Unset; break; } continue; } openZones = false; break; } //Room has been populated. } } }