graph2plan / PostProcess /g2p /add_archs.py
Zai
test
06db6e9
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})'
@property
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
)
@staticmethod
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])
@property
def width(self):return self.rect[2]
@property
def height(self):return self.rect[3]
@property
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 = []
@property
def label(self):return room_label[self.category][1]
@property
def type(self): return room_label[self.category][3]
@property
def center(self): return self.box.reshape(-1,2).mean(0)
@staticmethod
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
@staticmethod
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
@staticmethod
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