private Space.RoomRelationShip CreateWaitingList <Initial>(List <Guid> PlacedRelList, Dictionary <Space.RelationLevel, List <Space.RoomRelationShip> > NotPlacedRelList, Space.RelationLevel lv) { Space.RoomRelationShip init = new Space.RoomRelationShip(new Space.Room(), lv); foreach (Space.RoomRelationShip rel in Relationships.Values) { if (rel.Level == lv && rel.Origin is Initial) { init = rel; } } if (init.Relations.Count == 0) { throw new System.Exception("Not Found initial start point at func CreateWaitingList"); } PlacedRelList.Add(init.Origin.id); foreach (Guid id in init.Relations.Keys) { if (Relationships[id].Level == lv) { NotPlacedRelList[lv].Add(Relationships[id]); } } NotPlacedRelList[lv].Sort(); return(init); }
private void HelpToGeneareteBlueprint(List <List <Blueprint> > results, Space.RoomRelationShip target) { if (results.Count == 0) { Blueprint Origin = new Blueprint(Conditions.WorldStdLocation); Space.RoomRelationShip Enterence = new Space.RoomRelationShip(new Space.Room(), Space.RelationLevel.First); //현관을 찾아, Origin에 배치하여 RootCase를 만듬. foreach (Space.RoomRelationShip rel in Relationships.Values) { if (rel.Origin is Space.Entrance) { Enterence = rel; //배치를 시작하는 지점을 찾았고, Results에 저장 results.Add(Blueprint.AddNewRoom(Origin, rel, Conditions)); break; } } } else { List <Blueprint>[] rootCase = results.ToArray(); results.Clear(); foreach (List <Blueprint> cases in rootCase) { foreach (Blueprint blue in cases) { results.Add(Blueprint.AddNewRoom(blue, target, Conditions)); } } } }
private Space.RoomRelationShip PlanSecondLevel(Space.RoomRelationShip connRel) { //계단 정의 Space.Room stair = new Space.Stair(connRel.Origin.Size, connRel.Origin.Materials, Conditions.StaticComponents); Space.RoomRelationShip stairRel = new Space.RoomRelationShip(stair, Space.RelationLevel.Second); this.ConnectRelation(stairRel, connRel, Space.ConnectionType.Level); this.Register(stairRel); //가족실(거실) 정의 Space.Room family = new Space.Living(new Data.Size(360, 400, Data.Config.RoomHeight), GetMaterials(Space.RoomType.Living), Conditions.StaticComponents); Space.RoomRelationShip familyRel = new Space.RoomRelationShip(family, Space.RelationLevel.Second); this.ConnectRelation(stairRel, familyRel, Space.ConnectionType.Open); this.Register(familyRel); //침실 정의 Space.Room bed = new Space.Bed(new Data.Size(350, 380, Data.Config.RoomHeight), GetMaterials(Space.RoomType.Bed), Conditions.StaticComponents); Space.RoomRelationShip bedRel = new Space.RoomRelationShip(bed, Space.RelationLevel.Second); this.ConnectRelation(bedRel, familyRel, Space.ConnectionType.Door); this.Register(bedRel); //화장실 정의 Space.Room rest = new Space.Rest(new Data.Size(260, 300, Data.Config.RoomHeight), GetMaterials(Space.RoomType.Rest), Conditions.StaticComponents); Space.RoomRelationShip restRel = new Space.RoomRelationShip(rest, Space.RelationLevel.Second); this.ConnectRelation(restRel, familyRel, Space.ConnectionType.Door); this.Register(restRel); return(stairRel); }
public void CreateEnterence() //Interpreter로 부터 받을 변수가 있다면 추가 { //1층에서만 현관을 찾는다. foreach (Space.RoomRelationShip rel in this.Relations[Space.RelationLevel.First]) { if (rel.Origin is Space.Entrance) { Space.RoomRelationShip Ent = rel; bool placed = false; //어느 Side부터 탐색할지를 랜덤하게 결정하는 과정 List <Data.SideCardinalDirection> search = new List <Data.SideCardinalDirection>(); foreach (Data.SideCardinalDirection elem in Enum.GetValues(typeof(Data.SideCardinalDirection))) { search.Add(elem); } int n = search.Count; while (n > 1) { n--; int k = rng.Next(n + 1); Data.SideCardinalDirection value = search[k]; search[k] = search[n]; search[n] = value; } foreach (Data.SideCardinalDirection relsideDirection in search) { foreach (Data.Side relSide in rel.Origin.Sides[relsideDirection]) { if (relSide.Type == Data.SideType.Wall && relSide.GetLength() > Data.Config.DoorLength && this.IsOutterSide(rel.Origin.id, relSide, rel.Level)) { relSide.Type = Data.SideType.Door; placed = true; break; } } if (placed) { break; } } break; } } }
private void ConnectRelation(Space.RoomRelationShip a, Space.RoomRelationShip b, Space.ConnectionType type) { a.AddRelation(b.Origin, type); b.AddRelation(a.Origin, type); }
private void Register(Space.RoomRelationShip rel) { Rooms.Add(rel.Origin.id, rel.Origin); Relationships.Add(rel.Origin.id, rel); }
public List <List <Blueprint> > GenerateBlueprints() //배치순서를 결정할 수 있는 변수들을 입력 { //리턴할 준비 List <List <Blueprint> > Results = new List <List <Blueprint> >(); //Coditions을 활용하여 Rooms과 관계도를 생성하여, 객체에 저장 GenerateRoomsAndRel(); //배치과정을 기록하는 과정 Dictionary <Space.RelationLevel, Space.RoomRelationShip> StartPoint = new Dictionary <Space.RelationLevel, Space.RoomRelationShip>(); List <Guid> PlacedRelList = new List <Guid>(); Dictionary <Space.RelationLevel, List <Space.RoomRelationShip> > NotPlacedRelList = new Dictionary <Space.RelationLevel, List <Space.RoomRelationShip> >(); //초기화 foreach (Space.RelationLevel lv in Enum.GetValues(typeof(Space.RelationLevel))) { NotPlacedRelList.Add(lv, new List <Space.RoomRelationShip>()); //찾는 타입의 방으로 시작할 지점을 찾아, List를 만든다. if (lv == Space.RelationLevel.First) { StartPoint.Add(lv, CreateWaitingList <Space.Entrance>(PlacedRelList, NotPlacedRelList, lv)); } else if (lv == Space.RelationLevel.Second) { StartPoint.Add(lv, CreateWaitingList <Space.Stair>(PlacedRelList, NotPlacedRelList, lv)); } } //실제로 배치가 진행되는 과정. (모든 룸이 배치될 때까지 배치) foreach (Space.RelationLevel lv in Enum.GetValues(typeof(Space.RelationLevel))) { //StartPoint를 배치한다. HelpToGeneareteBlueprint(Results, StartPoint[lv]); while (NotPlacedRelList[lv].Count != 0) { //새로 배치할 대상 Space.RoomRelationShip target = NotPlacedRelList[lv][0]; //실제로 배치가 진행되는 함수 HelpToGeneareteBlueprint(Results, target); //Results에 포함된 가능성을 새로운 가능성으로 전환 //배치완료를 기록 PlacedRelList.Add(target.Origin.id); //Wating 리스트에서 제거 NotPlacedRelList[lv].Remove(target); foreach (Guid id in target.Relations.Keys) { if (!(PlacedRelList.Contains(id) || NotPlacedRelList[lv].Contains(Relationships[id])) && target.Level == Relationships[id].Level) { NotPlacedRelList[lv].Add(Relationships[id]); } } //관계 수에 따라 다시 정렬! NotPlacedRelList[lv].Sort(); } } //모든 배치가 완료된 상황에서, 창문과 현관문을 배치하기 위해서 외곽선을 파악하고 각 공간마다 창문 타입의 Side를 지정할 수 있도록 한다. foreach (List <Blueprint> bps in Results) { foreach (Blueprint bp in bps) { bp.CreateEnterence(); bp.CreateWindows(); //TODO: 현재 계단작업은 잠시 중단되었다. //bp.CreateStair(); } } return(Results); }
private Space.RoomRelationShip PlanFirstLevel() { //현관 정의 Space.Room enterence = new Space.Entrance(Conditions.RoomSize.EnterenceSize, GetMaterials(Space.RoomType.Entrance), Conditions.StaticComponents); Space.RoomRelationShip enterenceRel = new Space.RoomRelationShip(enterence, Space.RelationLevel.First); this.Register(enterenceRel); //거실 정의 Space.Room living = new Space.Living(Conditions.RoomSize.LivingSize, GetMaterials(Space.RoomType.Living), Conditions.StaticComponents); Space.RoomRelationShip livingRel = new Space.RoomRelationShip(living, Space.RelationLevel.First); //거실 남향을 통창으로 바꿈 living.Sides[Data.SideCardinalDirection.South][0].Type = Data.SideType.WindowWall; this.ConnectRelation(livingRel, enterenceRel, Space.ConnectionType.Door); this.Register(livingRel); //주방 정의 Space.Room kitchen = new Space.Kitchen(Conditions.RoomSize.KitchenSize, GetMaterials(Space.RoomType.Kitchen), Conditions.StaticComponents); Space.RoomRelationShip kitchenRel = new Space.RoomRelationShip(kitchen, Space.RelationLevel.First); this.ConnectRelation(livingRel, kitchenRel, Space.ConnectionType.Open); this.Register(kitchenRel); //침실 정의 List <Space.Room> beds = new List <Space.Room>(); foreach (Data.Size size in Conditions.RoomSize.BedSizes) { Space.Room bed = new Space.Bed(size, GetMaterials(Space.RoomType.Bed), Conditions.StaticComponents); Space.RoomRelationShip bedRel = new Space.RoomRelationShip(bed, Space.RelationLevel.First); this.ConnectRelation(livingRel, bedRel, Space.ConnectionType.Door); this.Register(bedRel); beds.Add(bed); } beds.Sort(); //침실을 크기에 따라 정렬 //화장실 정의 for (int i = 0; i < Conditions.RoomSize.RestSizes.Count; i++) { Space.Room rest = new Space.Rest(Conditions.RoomSize.RestSizes[i], GetMaterials(Space.RoomType.Rest), Conditions.StaticComponents); Space.RoomRelationShip restRel = new Space.RoomRelationShip(rest, Space.RelationLevel.First); if (i == 0) { this.ConnectRelation(livingRel, restRel, Space.ConnectionType.Door); } else { if (beds.Count != 0) { Space.Room target = beds.First(); this.ConnectRelation(this.Relationships[target.id], restRel, Space.ConnectionType.Door); beds.Remove(target); } } this.Register(restRel); } //계단 정의 Space.Room stair = new Space.Stair(new Data.Size(260, 400, Data.Config.RoomHeight), GetMaterials(Space.RoomType.Stair), Conditions.StaticComponents); Space.RoomRelationShip stairRel = new Space.RoomRelationShip(stair, Space.RelationLevel.First); this.ConnectRelation(livingRel, stairRel, Space.ConnectionType.Open); this.Register(stairRel); return(stairRel); }
//배치의 기준을 잡는 핵심적인 논리파트 private void GenerateRoomsAndRel() //관계를 정의하기 위한 변수를 추가하려면 여기에 추가할 것! { Space.RoomRelationShip connection = PlanFirstLevel(); //1층을 설계하는데 필요한 변수는 여기에 추가할 것! connection = PlanSecondLevel(connection); //2층을 설계하는데 필요한 변수는 여기에 추가할 것! }
//관계도 구분을 고려하며 배치할 수 있도록 개선했다. public static List <Blueprint> AddNewRoom(Blueprint blueprint, Space.RoomRelationShip newRelation, Variables conditions) { Blueprint PreviousBlueprint = (Blueprint)blueprint.Clone(); List <Blueprint> PossiblBlueprint = new List <Blueprint>(); if (newRelation.Level == Space.RelationLevel.First) { //1층에 기준 방 배치가 안되어 있으므로, 맵 기준점에 배치한다. if (PreviousBlueprint.Relations[newRelation.Level].Count == 0) { if (newRelation.Origin is Space.Entrance) { newRelation.Origin.ChangeLocation(PreviousBlueprint.WorldLocation); PreviousBlueprint.Relations[newRelation.Level].Add(newRelation); PossiblBlueprint.Add(PreviousBlueprint); return(PossiblBlueprint); } else { throw new NotImplementedException("Error"); } } } else if (newRelation.Level == Space.RelationLevel.Second) { if (PreviousBlueprint.Relations[newRelation.Level].Count == 0) { if (newRelation.Origin is Space.Stair) { //1층에 있던 계단을 찾는다. foreach (Space.RoomRelationShip level in PreviousBlueprint.Relations[Space.RelationLevel.First]) { if (level.Origin is Space.Stair) { newRelation.Origin.ChangeLocation(new Data.Cord(level.Origin.Location.X, level.Origin.Location.Y, level.Origin.Location.Z + Data.Config.RoomHeight)); foreach (Data.SideCardinalDirection sideDirection in Enum.GetValues(typeof(Data.SideCardinalDirection))) { foreach (Data.Side levelOneSide in level.Origin.Sides[sideDirection]) { if (levelOneSide.Type == Data.SideType.Wall) { foreach (Data.Side levelTwoSide in newRelation.Origin.Sides[sideDirection]) { //단순히 타입을 Block으로 바꾸는 것보다는 괜찮은 방법이 필요함. levelTwoSide.Type = Data.SideType.Window; } } } } PreviousBlueprint.Relations[newRelation.Level].Add((Space.RoomRelationShip)newRelation.Clone()); break; } } PossiblBlueprint.Add(PreviousBlueprint); return(PossiblBlueprint); } else { throw new NotImplementedException("Error"); } } } else { throw new NotImplementedException("New Relation Level does not handled."); } #region 설명 /* * 배치에 제일 핵심이 되는 부분! * 1) 배치가능한 SidePart를 검색한다. (현재는 MinimalOverlap보다 긴 Side) * 2) 배치가 가능한지 판단을 한다. (Overlap이 되지 않았는지, 관계에 맞게 Cotact하게 배치되었는지) * * 3) 배치가 가능하면, * (Room의 OccupySide) * - Side를 Occupy한다. * - 새로운 Side 상태를 기록한다. * - 이전 Side 상태도 남긴다. * * - Relation에 배치된 공간을 추가한다. * - 새로운 공간이 추가된 도면을 반환할 도면리스트에 추가한다. * * 4) 배치가 불가능하다면, 다른 SidePart를 탐색하고, 다른 Side와의 가능성도 계산한다. * * 5) 만약 배치되어 있던 모든 공간의 모든 Side가 불가능하다면, * - NULL 값을 반환한다. * 6) 모든 가능성을 탐구하였다면, 가능한 모든 도면리스트를 반환한다. * */ #endregion //TODO: 1층이라도 Boundary를 체크할 수 있도록 개선할 필요가 있다. foreach (Space.RoomRelationShip rel in PreviousBlueprint.Relations[newRelation.Level].ToArray()) { //현재까지 배치된 공간 중, 배치할 공간과 관련이 있는 공간만 해당 if (newRelation.Relations.ContainsKey(rel.Origin.id)) { foreach (Data.SideCardinalDirection sidePos in Enum.GetValues(typeof(Data.SideCardinalDirection))) { foreach (Data.Side side in rel.Origin.Sides[sidePos].ToArray()) { if (side.IsAvailable(Data.Config.MinimalOverlap)) { //sidepart 중 배치가 가능한 부분을 순차적으로 검색하며 찾는 것이 중요. //(2진 검색을 하듯이, Start와 End의 중간값 찾기와 같이 배치할 위치를 찾는다.) List <Data.Cord> avalCords = PreviousBlueprint.GetPosibleCords(newRelation.Origin, side, conditions.is_simple); foreach (Data.Cord cord in avalCords) { //TODO: Rotate를 한 뒤에도 배치 테스트를 할 수 있도록 개선이 필요하다. //새로 배치될 수 있는 위치를 시도! Space.RoomRelationShip CloneRel = (Space.RoomRelationShip)newRelation.Clone(); CloneRel.Origin.ChangeLocation((Data.Cord)cord.Clone()); // 위치를 바꾸기 전에, Side 정보를 복구 또는 저장해놓을 필요가 있다. //배치 이후에 기존의 공간과의 모순이 없는지 테스트 (Contact와 Overlap 체크) bool failed = false; #region CheckOverlap foreach (Space.RoomRelationShip test in PreviousBlueprint.Relations[newRelation.Level]) { //이미 배치된 공간과 겹치는지 확인 if (Space.Room.IsOverlap(test.Origin, CloneRel.Origin)) { failed = true; break; } } #endregion #region CheckBoundary if (!failed) { } #endregion #region CheckConnect if (!failed) { foreach (Space.RoomRelationShip test in PreviousBlueprint.Relations[newRelation.Level]) { //새로운 공간과 관계가 있는 공간이 연결되었는지 확인 if (CloneRel.Relations.ContainsKey(test.Origin.id)) { if (Space.Room.IsContact(CloneRel.Origin, test.Origin, Data.Config.MinimalOverlap)) { //Room의 함수로 들어가야 하는 부분 //겹치게 된 라인을 찾아 점령 Space.Room.OccupySides(test.Origin, CloneRel.Origin, CloneRel.Relations[test.Origin.id]); } else { failed = true; break; } } } } #endregion //모든 테스트를 통과했다면, // 1) 관계도에 포함시킨다. // 2) 새로운 도면을 출력 리스트에 추가시킨다. if (!failed) { PreviousBlueprint.Relations[newRelation.Level].Add((Space.RoomRelationShip)CloneRel.Clone()); PossiblBlueprint.Add(PreviousBlueprint); PreviousBlueprint = (Blueprint)blueprint.Clone(); } } } } } } } return(PossiblBlueprint); }