Spaces:
Runtime error
Runtime error
| import numpy as np | |
| import copy | |
| room_label = [(0, 'LivingRoom', 1, "PublicArea",[220, 213, 205]), | |
| (1, 'MasterRoom', 0, "Bedroom",[138, 113, 91]), | |
| (2, 'Kitchen', 1, "FunctionArea",[244, 245, 247]), | |
| (3, 'Bathroom', 0, "FunctionArea",[224, 225, 227]), | |
| (4, 'DiningRoom', 1, "FunctionArea",[200, 193, 185]), | |
| (5, 'ChildRoom', 0, "Bedroom",[198, 173, 151]), | |
| (6, 'StudyRoom', 0, "Bedroom",[178, 153, 131]), | |
| (7, 'SecondRoom', 0, "Bedroom",[158, 133, 111]), | |
| (8, 'GuestRoom', 0, "Bedroom",[189, 172, 146]), | |
| (9, 'Balcony', 1, "PublicArea",[244, 237, 224]), | |
| (10, 'Entrance', 1, "PublicArea",[238, 235, 230]), | |
| (11, 'Storage', 0, "PublicArea",[226, 220, 206]), | |
| (12, 'Wall-in', 0, "PublicArea",[226, 220, 206]), | |
| (13, 'External', 0, "External",[255, 255, 255]), | |
| (14, 'ExteriorWall', 0, "ExteriorWall",[0, 0, 0]), | |
| (15, 'FrontDoor', 0, "FrontDoor",[255,255,0]), | |
| (16, 'InteriorWall', 0, "InteriorWall",[128,128,128]), | |
| (17, 'InteriorDoor', 0, "InteriorDoor",[255,255,255])] | |
| class DirectedLine(): | |
| def __init__(self,p1,p2): | |
| '''search direction : 0(horizontal) / 1(vertical)''' | |
| if np.abs(p1[0]-p2[0])<1e-6: | |
| self.dir = 1 | |
| self.level = p1[0] | |
| self.minLevel = min(p1[1],p2[1]) | |
| self.maxLevel = max(p1[1],p2[1]) | |
| else: | |
| self.dir = 0 | |
| self.level = p1[1] | |
| self.minLevel = min(p1[0],p2[0]) | |
| self.maxLevel = max(p1[0],p2[0]) | |
| def __repr__(self): | |
| if self.dir==0: return f'({self.level},[{self.minLevel},{self.maxLevel}])' | |
| else: return f'([{self.minLevel},{self.maxLevel}],{self.level})' | |
| def length(self):return self.maxLevel-self.minLevel | |
| def is_contact(self,line): | |
| minl = min(self.minLevel,line.minLevel) | |
| maxl = max(self.maxLevel,line.maxLevel) | |
| length = maxl-minl | |
| return ( | |
| self.dir==line.dir and | |
| #self.level!=line.level and | |
| abs(self.level-line.level)<6 and | |
| length < self.length+line.length | |
| ) | |
| def lines_from_boundary(boundary): | |
| if len(boundary)==0:return [] | |
| pts = boundary.tolist()+[boundary[0].tolist()] | |
| lines = [ DirectedLine(pts[i],pts[i+1]) for i in range(len(pts)-1)] | |
| return lines | |
| class DirectedWall(): | |
| def __init__(self): | |
| '''orientation : 0(right) / 1(down) / 2(left) / 3(up)''' | |
| self.dir = 0 | |
| self.rect = np.array([0,0,0,0]) | |
| def width(self):return self.rect[2] | |
| def height(self):return self.rect[3] | |
| def center(self):return self.rect[:2]+self.rect[2:]/2 | |
| def setX(self,x):self.rect[0]=x | |
| def setY(self,y):self.rect[1]=y | |
| def setWidth(self,w):self.rect[2]=w | |
| def setHeight(self,h):self.rect[2]=h | |
| def setLeft(self,x): | |
| self.rect[2]=(self.rect[0]-x)+self.rect[2] | |
| self.rect[0]=x | |
| def setTop(self,y): | |
| self.rect[3]=(self.rect[1]-y)+self.rect[3] | |
| self.rect[1]=y | |
| def to_line(self): | |
| if self.dir in [0,2]: | |
| return DirectedLine([self.rect[0],self.rect[1]],[self.rect[0],self.rect[1]+self.rect[3]]) | |
| else: | |
| return DirectedLine([self.rect[0],self.rect[1]],[self.rect[0]+self.rect[2],self.rect[1]]) | |
| def __repr__(self): | |
| pos = ['right','down','left','up','None'][self.dir] | |
| return f'({pos},{self.rect})' | |
| class Entry(): | |
| def __init__(self): | |
| '''door type : 0(door) / 1(open wall)''' | |
| self.type = -1 | |
| self.entry = None | |
| def __repr__(self): | |
| if self.type==0: return f'(door,{self.entry})' | |
| else: return f'(open wall,{self.entry})' | |
| class Room(): | |
| def __init__(self): | |
| self.box = None | |
| self.category = None | |
| self.boundary = None | |
| self.map = None | |
| self.entry = None | |
| self.windows = [] | |
| def label(self):return room_label[self.category][1] | |
| def type(self): return room_label[self.category][3] | |
| def center(self): return self.box.reshape(-1,2).mean(0) | |
| def rooms_from_data(data): | |
| rooms = [] | |
| for i in range(len(data.rType)): | |
| room = Room() | |
| room.box = data.newBox[i] | |
| room.category = data.rType[i] | |
| room.boundary = data.rBoundary[i] | |
| room.lines = DirectedLine.lines_from_boundary(room.boundary) | |
| rooms.append(room) | |
| return rooms | |
| def from_node_box(node,box): | |
| x0,y0,x1,y1 = box | |
| room = Room() | |
| room.box = box | |
| room.category = node[-1] | |
| room.boundary = np.array([ | |
| [x0,y0], | |
| [x0,y1], | |
| [x1,y1], | |
| [x1,y0] | |
| ]) | |
| room.lines = DirectedLine.lines_from_boundary(room.boundary) | |
| # room.boundary = [ | |
| # DirectedLine((x0,y0),(x1,y0)), # top | |
| # DirectedLine((x1,y0),(x1,y1)), # right | |
| # DirectedLine((x0,y1),(x1,y1)), # bottom | |
| # DirectedLine((x0,y0),(x0,y1)), # left | |
| # ] | |
| return room | |
| def from_boundary(boundary): | |
| # pts = boundary[:,:2].tolist() | |
| # pts = pts+[pts[0]] | |
| room = Room() | |
| room.category = 0 | |
| room.box = np.array([np.min(boundary[:,0]),np.min(boundary[:,1]),np.max(boundary[:,0]),np.max(boundary[:,1])]) | |
| room.boundary = boundary[:,:2] | |
| room.lines = DirectedLine.lines_from_boundary(room.boundary) | |
| # room.boundary = [ | |
| # DirectedLine(pts[i],pts[i+1]) | |
| # for i in range(len(pts)-1) | |
| # ] | |
| return room | |
| def __repr__(self): | |
| return f'({self.label},{self.type},{self.box},{self.entry},{self.windows})' | |
| def find_contact_walls(room1,room2,reverse=False): | |
| contactWalls = [] | |
| lines1 = copy.deepcopy(room1.lines) #DirectedLine.from_boundary(room1.boundary)#room1.lines | |
| center1 = room1.center | |
| lines2 = copy.deepcopy(room2.lines) #DirectedLine.from_boundary(room2.boundary)# room2.lines | |
| center2 = room2.center | |
| temp = [] | |
| for i in range(len(lines1)): | |
| line1 = lines1[i] | |
| for j in range(len(lines2)): | |
| line2 = lines2[j] | |
| if line1.is_contact(line2): | |
| contactWall = DirectedWall() | |
| if line1.dir==0: | |
| minh = line1.level if not reverse else line2.level #min(line1.level,line2.level) | |
| maxh = line1.level if not reverse else line2.level #max(line1.level,line2.level) | |
| minw = max(line1.minLevel,line2.minLevel) | |
| maxw = min(line1.maxLevel,line2.maxLevel) | |
| # @todo:boudanry not work! | |
| if center1[1] > line1.level: contactWall.dir=1 | |
| else: contactWall.dir=3 | |
| contactWall.rect = np.array([minw,minh,maxw-minw,maxh-minh]) | |
| else: | |
| minw = line1.level if not reverse else line2.level#min(line1.level,line2.level) | |
| maxw = line1.level if not reverse else line2.level#max(line1.level,line2.level) | |
| minh = max(line1.minLevel,line2.minLevel) | |
| maxh = min(line1.maxLevel,line2.maxLevel) | |
| if center1[0] > line1.level: contactWall.dir=0 | |
| else: contactWall.dir=2 | |
| contactWall.rect = np.array([minw,minh,maxw-minw,maxh-minh]) | |
| contactWalls.append(contactWall) | |
| return contactWalls | |
| def find_longest_wall(contactWalls,dtype=1): | |
| contactLength = 0 | |
| openWall = None | |
| for i in range(len(contactWalls)): | |
| maxLength = max(contactWalls[i].width,contactWalls[i].height) | |
| if maxLength>contactLength: | |
| contactLength = maxLength | |
| openWall = contactWalls[i] | |
| if contactLength!=0: | |
| # @todo: adjust door | |
| openWall = adjust_door(openWall,dtype) | |
| entry = Entry() | |
| entry.type = dtype | |
| entry.entry = openWall | |
| assert entry.entry.dir!=-1, "find longest wall with dir -1!" | |
| return entry | |
| return None | |
| def find_closest_wall(candidateDoors,frontDoorCenter,dtype=1,boundary_lines=[]): | |
| dis = 1e8 | |
| door = None | |
| for i in range(len(candidateDoors)): | |
| maxLength = max(candidateDoors[i].width,candidateDoors[i].height) | |
| if maxLength<12:continue | |
| valid = True | |
| line = candidateDoors[i].to_line() | |
| for b_line in boundary_lines: | |
| if line.is_contact(b_line): | |
| valid = False | |
| break | |
| if not valid: continue | |
| center = candidateDoors[i].center | |
| candidateDis = np.sum(np.power((center-frontDoorCenter),2)) | |
| if dis>candidateDis: | |
| dis = candidateDis | |
| door = candidateDoors[i] | |
| if door is not None: | |
| door = adjust_door(door,dtype) | |
| entry = Entry() | |
| entry.type = dtype | |
| entry.entry = door | |
| assert entry.entry.dir!=-1, "find closest wall with dir -1!" | |
| return entry | |
| return None | |
| def adjust_door(door,dtype=1): | |
| if door.dir in [1,3]: | |
| if dtype==1: | |
| door.rect[0] = door.rect[0]+door.rect[2]/8 | |
| door.rect[2] = door.rect[2]*3/4 | |
| else: | |
| # door.rect[0] = door.center[0]-6 | |
| door.rect[2] = min(2*6,door.rect[2]) | |
| else: | |
| if dtype==1: | |
| door.rect[1] = door.rect[1]+door.rect[3]/8 | |
| door.rect[3] = door.rect[3]*3/4 | |
| else: | |
| # door.rect[1] = door.center[1]-6 | |
| door.rect[3] = min(2*6,door.rect[3]) | |
| return door | |
| def add_interior_door(rooms,living_idx,house): | |
| frontDoorCenter = house.boundary[:2].mean(0) | |
| for i in range(len(rooms)): | |
| if i==living_idx:continue | |
| # 1. Balcony: find the longest door | |
| # 2. Public Area: find the longest door | |
| # 3. Others: | |
| # 3.1 Contact with living room: find the cloest door with the front door | |
| # 3.2 Others: find the longest wall | |
| if rooms[i].label == 'Balcony': | |
| contactWalls = [] | |
| for j in range(len(rooms)): | |
| if i!=j: | |
| contactWalls.extend(find_contact_walls(rooms[i],rooms[j])) | |
| rooms[i].entry = find_longest_wall(contactWalls,dtype=1) | |
| else: | |
| contactWalls = find_contact_walls(rooms[i],rooms[living_idx]) | |
| if len(contactWalls)>0: | |
| if rooms[i].type == 'PublicArea': | |
| rooms[i].entry = find_longest_wall(contactWalls,dtype=1) | |
| else: | |
| candidateDoors = [ | |
| wall for wall in contactWalls | |
| if (wall.width>wall.height and wall.width>2*6) or | |
| (wall.height>wall.width and wall.height>2*6) | |
| ] | |
| if len(candidateDoors)==0: | |
| rooms[i].entry = find_longest_wall(contactWalls,dtype=0) | |
| else: | |
| rooms[i].entry = find_closest_wall(contactWalls,frontDoorCenter,dtype=0,boundary_lines=house.lines) | |
| else: | |
| contactWalls = [] | |
| for j in range(len(rooms)): | |
| if i!=j: | |
| contactWalls.extend(find_contact_walls(rooms[i],rooms[j])) | |
| if len(contactWalls)>0: | |
| rooms[i].entry = find_closest_wall(contactWalls,frontDoorCenter,dtype=1,boundary_lines=house.lines) | |
| return rooms | |
| def find_windows(contactWalls,wtypes=['mid'],keep_longest=False): | |
| windows = [] | |
| contactLength = 1e8 | |
| for i in range(len(contactWalls)): | |
| contactWall = contactWalls[i] | |
| maxLength = max(contactWall.width,contactWall.height) | |
| if ('large' in wtypes and maxLength>3*12): | |
| contactWalls[i] = adjust_window(contactWalls[i],'large') | |
| windows.append(contactWall) | |
| elif 'mid' in wtypes and maxLength>3*9: | |
| contactWalls[i] = adjust_window(contactWalls[i],'mid') | |
| windows.append(contactWall) | |
| elif 'small' in wtypes and maxLength>2*5: | |
| contactWalls[i] = adjust_window(contactWalls[i],'small') | |
| windows.append(contactWall) | |
| elif 'balcony' in wtypes and maxLength>2*5: | |
| contactWalls[i] = adjust_window(contactWalls[i],'balcony') | |
| windows.append(contactWall) | |
| return windows | |
| def find_window_by_length(contactWalls,wtypes=['mid'],ltype='max'): | |
| window = None | |
| contactLength = 0 if ltype=='max' else 1e8 | |
| ufunc = np.greater if ltype=='max' else np.less | |
| for i in range(len(contactWalls)): | |
| contactWall = contactWalls[i] | |
| maxLength = max(contactWall.width,contactWall.height) | |
| if ufunc(maxLength,contactLength): | |
| if 'large' in wtypes and maxLength>3*12: | |
| contactWalls[i] = adjust_window(contactWalls[i],'large') | |
| window = contactWalls[i] | |
| contactLength = maxLength | |
| elif 'mid' in wtypes and maxLength>3*9: | |
| contactWalls[i] = adjust_window(contactWalls[i],'mid') | |
| window = contactWalls[i] | |
| contactLength = maxLength | |
| elif 'small' in wtypes and maxLength>2*5: | |
| contactWalls[i] = adjust_window(contactWalls[i],'small') | |
| window = contactWalls[i] | |
| contactLength = maxLength | |
| return [window] if window is not None else [] | |
| def adjust_window(window,wtype='mid'): | |
| hl = {'small':5,'mid':9,'large':12} | |
| if window.dir in [1,3]: | |
| if wtype=='balcony': | |
| window.rect[0] = window.rect[0]+window.rect[2]/10 | |
| window.rect[2] = window.rect[2]*4/5 | |
| else: | |
| length = hl[wtype] | |
| window.rect[0] = window.center[0]-length | |
| window.rect[2] = 2*length | |
| else: | |
| if wtype=='balcony': | |
| window.rect[1] = window.rect[1]+window.rect[3]/10 | |
| window.rect[3] = window.rect[3]*4/5 | |
| else: | |
| length = hl[wtype] | |
| window.rect[1] = window.center[1]-length | |
| window.rect[3] = 2*length | |
| return window | |
| def add_window(rooms,house): | |
| for i in range(len(rooms)): | |
| # 1. Balcony: small(half=5) | |
| # 2. Living Room: mid(half=9),large(half=12) | |
| # 3. Bathroom: shortest wall, small | |
| # 4. Others: longest wall, mid | |
| contactWalls = find_contact_walls(rooms[i],house,reverse=True) | |
| if rooms[i].label == 'Balcony': | |
| rooms[i].windows.extend(find_windows(contactWalls,['balcony'])) | |
| elif rooms[i].label == 'LivingRoom': | |
| rooms[i].windows.extend(find_windows(contactWalls,['mid','large'])) | |
| elif rooms[i].label == 'Bathroom': | |
| rooms[i].windows.extend(find_window_by_length(contactWalls,['small'],'min')) | |
| else: | |
| rooms[i].windows.extend(find_window_by_length(contactWalls,['mid'],'max')) | |
| return rooms | |
| def rooms_to_numpy(rooms): | |
| doors = [] | |
| windows = [] | |
| for i in range(len(rooms)): | |
| if rooms[i].entry is not None: | |
| door = rooms[i].entry.entry | |
| doors.append([i,door.rect[0],door.rect[1],door.rect[2],door.rect[3],door.dir]) | |
| if len(rooms[i].windows) > 0: | |
| ws = [[i,w.rect[0],w.rect[1],w.rect[2],w.rect[3],w.dir] for w in rooms[i].windows] | |
| windows.extend(ws) | |
| return np.array(doors),np.array(windows) | |
| def add_door_window(data): | |
| boundary = data.boundary | |
| living_idx = np.where(data.rType==0)[0][0] | |
| rooms = Room.rooms_from_data(data) | |
| house = Room.from_boundary(boundary[:,:2]) | |
| house.lines = house.lines[1:] | |
| rooms = add_interior_door(rooms,living_idx,house) | |
| rooms = add_window(rooms,house) | |
| return rooms_to_numpy(rooms) | |
| def add_dw_fp(data): | |
| doors,windows = add_door_window(data) | |
| data.doors = doors | |
| data.windows = windows | |
| return data | |