/// <summary>Translate face's fitting / placement unit so this face is positioned at a specified distance from support-face</summary> /// <param name="distance">Distance to support-face</param> /// <param name="supportFace">Support-face to position against</param> public void TranslateToDistanceFromFace(float distance, ParticularFace supportFace) { Vector2D offsetInDistanceDirection = supportFace.NormalVector * (supportFace.DistanceFromFittingCenter + distance + DistanceFromFittingCenter); Vector2D offsetAlongFace = Vector2D.Zero; if (supportFace.attacherFacesAndRelations.Count > 1) { float spacing = (supportFace.SideLength - supportFace.ReservedLength) / (supportFace.attacherFacesAndRelations.Count + 1); offsetAlongFace = supportFace.VectorAlongFace * (-(supportFace.SideLength / 2) + spacing * (supportFace.PlacedAttacherFacesCount + 1) + supportFace.FilledLength + Fitting.ClearanceAreaLengthInDirection((supportFace.Direction + 3) % 4) + SideLength / 2); } supportFace.PlacedAttacherFacesCount++; supportFace.FilledLength += (SideLength + SurroundingClearanceAreaLength); // Perform translation Fitting.PlacementUnit.Translate(supportFace.Fitting.Position + offsetInDistanceDirection + offsetAlongFace - Fitting.Position); }
// Methods /// <summary>Checks if there is enough unreserved room left in face for an attacher-face</summary> /// <param name="attacherFace">Face to check if it can be supported by this face</param> /// <param name="spatialRelation">Attacher-face relation to this support-face</param> /// <returns>Whether attacher-face can be supported by this face</returns> public bool CanAttachFace(ParticularFace attacherFace, SpatialRelation spatialRelation) { if (Face.Type == spatialRelation.SupportFaceType) { if (ReservedLength == 0 || ReservedLength + attacherFace.SideLength + attacherFace.SurroundingClearanceAreaLength < SideLength) { return(true); } else { return(false); } } else { return(false); } }
/// <summary>Saves face as attacher-face and reserves length of this support-face</summary> /// <param name="attacherFace">Face to attach to this face</param> /// <param name="spatialRelation">Attacher-face relation to this support-face</param> /// <returns>Whether attacher-face was attached to this face</returns> public bool AttachFace(ParticularFace attacherFace, SpatialRelation spatialRelation) { if (Face.Type == spatialRelation.SupportFaceType) { if (ReservedLength == 0 || ReservedLength + attacherFace.SideLength + attacherFace.SurroundingClearanceAreaLength < SideLength) { ReservedLength += (attacherFace.SideLength + attacherFace.SurroundingClearanceAreaLength); attacherFacesAndRelations.Add(new Tuple <ParticularFace, SpatialRelation>(attacherFace, spatialRelation)); return(true); } else { return(false); } } else { return(false); } }
/// <summary>Add wall distance constraint from a fitting face to wall</summary> /// <param name="attacherFace">Attacher-face to be placed face-forward relative to wall</param> /// <param name="wallDistance">Distance from face to wall</param> public void AddWallConstraint(ParticularFace attacherFace, float wallDistance) { float placementUnitOriginToWall = (attacherFace.NormalVector * attacherFace.FaceCenterPosition).Length() + wallDistance; wallDirectionsAndWallDistances.Add(new Tuple <int, float>(attacherFace.Direction, placementUnitOriginToWall)); }
/// <summary>Generate fitting layout consisting of the fittings and their placements</summary> private FittingLayout GenerateFittingLayout(Room room, List <FittingModel> fittingModelsToBePlaced, int randomizationSeed = 0) { // Make sure fitting semantics are loaded if (!hasSemanticsLoaded) { LoadFittingSemantics(DefaultSemanticsFilePath); } // Start a Random instance Random globalRandom; if (randomizationSeed == 0) { // Base randomization on time seed, if no seed given globalRandom = new Random(); } else { globalRandom = new Random(randomizationSeed); } // Solve placements // Register fittings and their faces List <Fitting> fittingsToBePlaced = new List <Fitting>(); List <ParticularFace> fittingsFaces = new List <ParticularFace>(); foreach (FittingModel fittingModelToBePlaced in fittingModelsToBePlaced) { Fitting fittingToBePlaced = new Fitting(fittingModelToBePlaced); fittingsToBePlaced.Add(fittingToBePlaced); foreach (ParticularFace fittingFace in fittingToBePlaced.Faces) { fittingsFaces.Add(fittingFace); } } // Assign attacher faces to compatible support faces // List to be filled with up to one face with wall relation per fitting, that is selected to be satisfied List <Tuple <ParticularFace, SpatialRelation> > selectedAttacherFacesAndWallRelations = new List <Tuple <ParticularFace, SpatialRelation> >(); foreach (Fitting fittingToBePlaced in fittingsToBePlaced) { // List to be filled with faces with fitting relations that can be satisfied List <Tuple <ParticularFace, ParticularFace, SpatialRelation> > attacherFacesAndSupportFacesAndRelations = new List <Tuple <ParticularFace, ParticularFace, SpatialRelation> >(); int fittingRelationCount = 0; // List to be filled with faces with wall relations List <Tuple <ParticularFace, SpatialRelation> > attacherFacesAndWallRelations = new List <Tuple <ParticularFace, SpatialRelation> >(); // Go through every relation in the fitting's faces, and register satisfiable ones foreach (ParticularFace aFace in fittingToBePlaced.Faces) { foreach (SpatialRelation relation in aFace.Face.Type.SpatialRelations) { if (relation.SupportFaceType == wallFaceType || relation.SupportFaceType == doorFaceType || relation.SupportFaceType == windowFaceType) { if (relation.SupportFaceType == wallFaceType) { // Register wall related face attacherFacesAndWallRelations.Add(new Tuple <ParticularFace, SpatialRelation>(aFace, relation)); } } else { fittingRelationCount++; // Register found support faces List <ParticularFace> supportFaces = new List <ParticularFace>(); foreach (ParticularFace sFace in fittingsFaces) { if (sFace.Face.Type == relation.SupportFaceType) { supportFaces.Add(sFace); } } // Assign support faces to fulfill relation if (supportFaces.Count > 0) { // Pick a random support face as the default and check if there are less occupied ones int startIndex = globalRandom.Next() % supportFaces.Count; ParticularFace minOccupiedSupportFace = supportFaces[startIndex]; foreach (ParticularFace supportFace in supportFaces) { if (supportFace.ReservedLength < minOccupiedSupportFace.ReservedLength) { minOccupiedSupportFace = supportFace; } } if (minOccupiedSupportFace.CanAttachFace(aFace, relation)) { // Register face pair that satisfies relation attacherFacesAndSupportFacesAndRelations.Add(new Tuple <ParticularFace, ParticularFace, SpatialRelation>(aFace, minOccupiedSupportFace, relation)); } else { // Pick a random support face as the default and check if there are freer ones startIndex = globalRandom.Next() % supportFaces.Count; ParticularFace mostFreeSupportFace = supportFaces[startIndex]; foreach (ParticularFace supportFace in supportFaces) { if (supportFace.SideLength - supportFace.ReservedLength > mostFreeSupportFace.SideLength - mostFreeSupportFace.ReservedLength) { mostFreeSupportFace = supportFace; } } if (mostFreeSupportFace.CanAttachFace(aFace, relation)) { // Register face pair that satisfies relation attacherFacesAndSupportFacesAndRelations.Add(new Tuple <ParticularFace, ParticularFace, SpatialRelation>(aFace, mostFreeSupportFace, relation)); } } } } } } // Assign one fitting attacher face to its support face if (attacherFacesAndSupportFacesAndRelations.Count() > 0) { // Pick only one fitting relation to fulfill per fitting int pickedIndex = globalRandom.Next() % attacherFacesAndSupportFacesAndRelations.Count; Tuple <ParticularFace, ParticularFace, SpatialRelation> attacherFaceAndSupportFaceAndRelation = attacherFacesAndSupportFacesAndRelations[pickedIndex]; attacherFaceAndSupportFaceAndRelation.Item2.AttachFace(attacherFaceAndSupportFaceAndRelation.Item1, attacherFaceAndSupportFaceAndRelation.Item3); } else if (fittingRelationCount > 0) { Console.WriteLine("No fitting support face with capacity could be found for a {0} {1}. ", fittingToBePlaced.FittingModel.Id, fittingToBePlaced.FittingModel.FittingType.Id); } // Pick only one wall relation to fulfill per fitting if (attacherFacesAndWallRelations.Count > 0) { int selectedIndex = globalRandom.Next() % attacherFacesAndWallRelations.Count; selectedAttacherFacesAndWallRelations.Add(attacherFacesAndWallRelations[selectedIndex]); } } // Place attacher faces (and their fittings with them) in relation to support faces, in grouping placement units foreach (ParticularFace sFace in fittingsFaces) { foreach (Tuple <ParticularFace, SpatialRelation> aFaceAndRelation in sFace.attacherFacesAndRelations) { ParticularFace aFace = aFaceAndRelation.Item1; SpatialRelation relation = aFaceAndRelation.Item2; // Rotate objects to match faces aFace.RotateToFace(sFace.Direction); // Move objects to match face distance aFace.TranslateToDistanceFromFace(relation.Distance, sFace); // Merge placement units sFace.Fitting.PlacementUnit.AbsorbPlacementUnit(aFace.Fitting.PlacementUnit); } } // Register wall-attacher faces to placement units foreach (Tuple <ParticularFace, SpatialRelation> attacherFaceAndWallRelation in selectedAttacherFacesAndWallRelations) { attacherFaceAndWallRelation.Item1.Fitting.PlacementUnit.AddWallConstraint(attacherFaceAndWallRelation.Item1, attacherFaceAndWallRelation.Item2.Distance); } // Register all consolidated placement units List <PlacementUnit> placementUnits = new List <PlacementUnit>(); foreach (Fitting fitting in fittingsToBePlaced) { if (!placementUnits.Contains(fitting.PlacementUnit)) { placementUnits.Add(fitting.PlacementUnit); } } // Set placement domains for the placement units foreach (PlacementUnit placementUnit in placementUnits) { // Set placement domain placementUnit.SetDomain(room); // Shuffle placement domain placementUnit.ShuffleDomain(globalRandom); } // Test placements for all placement units if (!TestPlacements(ref placementUnits, room.Clone())) { // No possible fitting layout could be found Console.WriteLine("No possible fitting layout could be found. "); return(new FittingLayout(room, new List <Fitting>())); } // Return finished list of placed fittings return(new FittingLayout(room, fittingsToBePlaced)); }