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 | |