diff --git a/House/__init__.py b/House/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/House/__pycache__/__init__.cpython-311.pyc b/House/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fa97cc6e5164a8a130e93e502a40362abe934362 Binary files /dev/null and b/House/__pycache__/__init__.cpython-311.pyc differ diff --git a/House/__pycache__/__init__.cpython-37.pyc b/House/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fba50486dd3c3f209dac50e9969963c812e8234c Binary files /dev/null and b/House/__pycache__/__init__.cpython-37.pyc differ diff --git a/House/__pycache__/settings.cpython-311.pyc b/House/__pycache__/settings.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cc3774ad044ac4099e157f7439bdb5894bd434b0 Binary files /dev/null and b/House/__pycache__/settings.cpython-311.pyc differ diff --git a/House/__pycache__/settings.cpython-37.pyc b/House/__pycache__/settings.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6c0cb605374f64edb45fae83a776b3039502a1d2 Binary files /dev/null and b/House/__pycache__/settings.cpython-37.pyc differ diff --git a/House/__pycache__/urls.cpython-311.pyc b/House/__pycache__/urls.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ef7c97662eb626bbd18e1dc0e605f7af37d963da Binary files /dev/null and b/House/__pycache__/urls.cpython-311.pyc differ diff --git a/House/__pycache__/urls.cpython-37.pyc b/House/__pycache__/urls.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9643c5082113c8bada760d16079d0f48726217e0 Binary files /dev/null and b/House/__pycache__/urls.cpython-37.pyc differ diff --git a/House/__pycache__/wsgi.cpython-37.pyc b/House/__pycache__/wsgi.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7607d08566a6acc733e396590387c5303276c10 Binary files /dev/null and b/House/__pycache__/wsgi.cpython-37.pyc differ diff --git a/House/asgi.py b/House/asgi.py new file mode 100644 index 0000000000000000000000000000000000000000..968e1e108f4e19d6b25049ff38a432a3b5f0f782 --- /dev/null +++ b/House/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for House project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'House.settings') + +application = get_asgi_application() diff --git a/House/settings.py b/House/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..3f626d2232e9f6ee93c8a36421473270ceff7d04 --- /dev/null +++ b/House/settings.py @@ -0,0 +1,127 @@ +""" +Django settings for House project. + +Generated by 'django-admin startproject' using Django 3.0.2. + +For more information on this file, see +https://docs.djangoproject.com/en/3.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/3.0/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'y&ol628+upm0r&=8pilr@u_w_0ji!1afp!st*y#ympn3u@!3s%' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'House.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [os.path.join(BASE_DIR, 'templates')] + , + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'House.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/3.0/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/3.0/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/3.0/howto/static-files/ + +STATIC_URL = '/static/' +HERE = os.path.dirname(os.path.abspath(__file__)) +HERE = os.path.join(HERE, '../') +STATICFILES_DIRS = ( + os.path.join(HERE, 'static/'), +) +ALLOWED_HOSTS = ['*'] \ No newline at end of file diff --git a/House/urls.py b/House/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..18fd1938bdd2b0b98710edb0ffc7335c522685fd --- /dev/null +++ b/House/urls.py @@ -0,0 +1,37 @@ +"""House URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/3.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path + +from Houseweb import views + +urlpatterns = [ + # path('admin/', admin.site.urls), + path('index/LoadTestBoundary', views.LoadTestBoundary), + path('index/NumSearch/', views.NumSearch), + path(r'index/LoadTrainHouse/', views.LoadTrainHouse), + path(r'index/TransGraph/', views.TransGraph), + path(r'index/TransGraph_net/', views.TransGraph_net), + path(r'index/Init/', views.Init), + path(r'index/AdjustGraph/', views.AdjustGraph), + path(r'index/GraphSearch/', views.GraphSearch), + path(r'index/RelBox/', views.RelBox), + path(r'index/Save_Editbox/', views.Save_Editbox), + + path('home', views.home), + + +] diff --git a/House/wsgi.py b/House/wsgi.py new file mode 100644 index 0000000000000000000000000000000000000000..882f00753d2c5d4c3dfd352fc5c8c11bf4ce7010 --- /dev/null +++ b/House/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for House project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'House.settings') + +application = get_wsgi_application() diff --git a/Houseweb/__init__.py b/Houseweb/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Houseweb/__pycache__/__init__.cpython-311.pyc b/Houseweb/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4fbc96cda109cb59640df7f766c062eac7f69dd5 Binary files /dev/null and b/Houseweb/__pycache__/__init__.cpython-311.pyc differ diff --git a/Houseweb/__pycache__/__init__.cpython-37.pyc b/Houseweb/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d39714e4bc8207dd61e92696ffa0401c5fa201a4 Binary files /dev/null and b/Houseweb/__pycache__/__init__.cpython-37.pyc differ diff --git a/Houseweb/__pycache__/create.cpython-37.pyc b/Houseweb/__pycache__/create.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7a5fa617d5af6850f1394a58413650515b6cd32b Binary files /dev/null and b/Houseweb/__pycache__/create.cpython-37.pyc differ diff --git a/Houseweb/__pycache__/floorplan2.cpython-37.pyc b/Houseweb/__pycache__/floorplan2.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d6b705ed27aa9a60472169ec08865ab06d823466 Binary files /dev/null and b/Houseweb/__pycache__/floorplan2.cpython-37.pyc differ diff --git a/Houseweb/__pycache__/network.cpython-37.pyc b/Houseweb/__pycache__/network.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ad8ac531fa21e474ca239d9a218bbcfd948a376 Binary files /dev/null and b/Houseweb/__pycache__/network.cpython-37.pyc differ diff --git a/Houseweb/__pycache__/utils.cpython-37.pyc b/Houseweb/__pycache__/utils.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..20273a78e10486d3bd1428bf036ad0d5d03180d6 Binary files /dev/null and b/Houseweb/__pycache__/utils.cpython-37.pyc differ diff --git a/Houseweb/__pycache__/utils1.cpython-37.pyc b/Houseweb/__pycache__/utils1.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..14907ee09d682fe3ef4b6ef43d85fe5426c5f0c8 Binary files /dev/null and b/Houseweb/__pycache__/utils1.cpython-37.pyc differ diff --git a/Houseweb/__pycache__/views.cpython-311.pyc b/Houseweb/__pycache__/views.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c6332a2ab4ac3f86032d57d8f7929fb09827efcb Binary files /dev/null and b/Houseweb/__pycache__/views.cpython-311.pyc differ diff --git a/Houseweb/__pycache__/views.cpython-37.pyc b/Houseweb/__pycache__/views.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7d44542b03bf1df874d22615cf3da7530638402c Binary files /dev/null and b/Houseweb/__pycache__/views.cpython-37.pyc differ diff --git a/Houseweb/admin.py b/Houseweb/admin.py new file mode 100644 index 0000000000000000000000000000000000000000..8c38f3f3dad51e4585f3984282c2a4bec5349c1e --- /dev/null +++ b/Houseweb/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/Houseweb/apps.py b/Houseweb/apps.py new file mode 100644 index 0000000000000000000000000000000000000000..81b6cc58b954534d40e3721afbe053803372a1ac --- /dev/null +++ b/Houseweb/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class HousewebConfig(AppConfig): + name = 'Houseweb' diff --git a/Houseweb/migrations/__init__.py b/Houseweb/migrations/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Houseweb/models.py b/Houseweb/models.py new file mode 100644 index 0000000000000000000000000000000000000000..71a836239075aa6e6e4ecb700e9c42c95c022d91 --- /dev/null +++ b/Houseweb/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/Houseweb/tests.py b/Houseweb/tests.py new file mode 100644 index 0000000000000000000000000000000000000000..7ce503c2dd97ba78597f6ff6e4393132753573f6 --- /dev/null +++ b/Houseweb/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/Houseweb/views.py b/Houseweb/views.py new file mode 100644 index 0000000000000000000000000000000000000000..f50cabfbcbfd6ba0a07167803dcc92d8bf5854ee --- /dev/null +++ b/Houseweb/views.py @@ -0,0 +1,757 @@ +from django.shortcuts import render +from django.http import HttpResponse, JsonResponse +import json +import model.test as mltest +import model.utils as mdul +from model.floorplan import * +import retrieval.retrieval as rt +import time +import pickle +import scipy.io as sio +import numpy as np +from model.decorate import * +import math +import pandas as pd +import matlab.engine + +global test_data, test_data_topk, testNameList, trainNameList +global train_data, trainNameList, trainTF, train_data_eNum, train_data_rNum +global engview, model +global tf_train, centroids, clusters + + +def home(request): + return render(request, "home.html", ) + + +def Init(request): + start = time.clock() + getTestData() + getTrainData() + loadMatlabEng() + loadModel() + loadRetrieval() + end = time.clock() + print('Init(model+test+train+engine+retrieval) time: %s Seconds' % (end - start)) + + return HttpResponse(None) + + +def loadMatlabEng(): + startengview = time.clock() + global engview + engview = matlab.engine.start_matlab() + engview.addpath(r'./align_fp/', nargout=0) + endengview = time.clock() + print(' matlab.engineview time: %s Seconds' % (endengview - startengview)) + + +def loadRetrieval(): + global tf_train, centroids, clusters + t1 = time.clock() + tf_train = np.load('./retrieval/tf_train.npy') + centroids = np.load('./retrieval/centroids_train.npy') + clusters = np.load('./retrieval/clusters_train.npy') + t2 = time.clock() + print('load tf/centroids/clusters', t2 - t1) + + +def getTestData(): + start = time.clock() + global test_data, testNameList, trainNameList + + test_data = pickle.load(open('./static/Data/data_test_converted.pkl', 'rb')) + test_data, testNameList, trainNameList = test_data['data'], list(test_data['testNameList']), list( + test_data['trainNameList']) + end = time.clock() + print('getTestData time: %s Seconds' % (end - start)) + + +def getTrainData(): + start = time.clock() + global train_data, trainNameList, trainTF, train_data_eNum, train_data_rNum + + train_data = pickle.load(open('./static/Data/data_train_converted.pkl', 'rb')) + train_data, trainNameList, trainTF = train_data['data'], list(train_data['nameList']), list(train_data['trainTF']) + + train_data_eNum = pickle.load(open('./static/Data/data_train_eNum.pkl', 'rb')) + train_data_eNum = train_data_eNum['eNum'] + train_data_rNum = np.load('./static/Data/rNum_train.npy') + + end = time.clock() + print('getTrainData time: %s Seconds' % (end - start)) + + +def loadModel(): + global model, train_data, trainNameList + start = time.clock() + model = mltest.load_model() + end = time.clock() + print('loadModel time: %s Seconds' % (end - start)) + start = time.clock() + test = train_data[trainNameList.index("75119")] + mltest.test(model, FloorPlan(test, train=True)) + end = time.clock() + print('test Model time: %s Seconds' % (end - start)) + + +def LoadTestBoundary(request): + start = time.clock() + testName = request.GET.get('testName').split(".")[0] + test_index = testNameList.index(testName) + data = test_data[test_index] + data_js = {} + data_js["door"] = str(data.boundary[0][0]) + "," + str(data.boundary[0][1]) + "," + str( + data.boundary[1][0]) + "," + str(data.boundary[1][1]) + ex = "" + for i in range(len(data.boundary)): + ex = ex + str(data.boundary[i][0]) + "," + str(data.boundary[i][1]) + " " + data_js['exterior'] = ex + end = time.clock() + print('LoadTestBoundary time: %s Seconds' % (end - start)) + return HttpResponse(json.dumps(data_js), content_type="application/json") + + +def get_filter_func(mask, acc, num): + filters = [ + None if not mask else ( + np.equal if acc[i] else np.greater_equal + ) + for i in range(len(mask)) + ] + + def filter_func(data): + for i in range(len(filters)): + if (filters[i] is not None) and (not filters[i](data[i], num[i])): return False + return True + + return filter_func + + +def filter_graph(graph_): + filters = graph_ + + def filter_graphfunc(data): + sub = data - filters + return ((sub >= 0).all()) + + return filter_graphfunc + + +def NumSearch(request): + start = time.clock() + data_new = json.loads(request.GET.get("userInfo")) + testName = data_new[0].split(".")[0] + test_index = testNameList.index(testName) + topkList = [] + topkList.clear() + data = test_data[test_index] + + + multi_clusters=False + test_data_topk = rt.retrieval(data, 1000,multi_clusters) + + if len(data_new) > 1: + roomactarr = data_new[1] + roomexaarr = data_new[2] + roomnumarr = [int(x) for x in data_new[3]] + + test_num = train_data_rNum[test_data_topk] + filter_func = get_filter_func(roomactarr, roomexaarr, roomnumarr) + indices = np.where(list(map(filter_func, test_num))) + indices = list(indices) + if len(indices[0]) < 20: + topk = len(indices[0]) + else: + topk = 20 + topkList.clear() + for i in range(topk): + topkList.append(str(trainNameList[int(test_data_topk[indices[0][i]])]) + ".png") + end = time.clock() + print('NumberSearch time: %s Seconds' % (end - start)) + return HttpResponse(json.dumps(topkList), content_type="application/json") + + +def FindTraindata(trainname): + start = time.clock() + train_index = trainNameList.index(trainname) + data = train_data[train_index] + data_js = {} + data_js["hsname"] = trainname + + data_js["door"] = str(data.boundary[0][0]) + "," + str(data.boundary[0][1]) + "," + str( + data.boundary[1][0]) + "," + str(data.boundary[1][1]) + print("testboundary", data_js["door"]) + ex = "" + for i in range(len(data.boundary)): + ex = ex + str(data.boundary[i][0]) + "," + str(data.boundary[i][1]) + " " + data_js['exterior'] = ex + + data_js["hsedge"] = [[int(u), int(v)] for u, v in data.edge[:, [0, 1]]] + + hsbox = [[[float(x1), float(y1), float(x2), float(y2)], [mdul.room_label[cate][1]]] for + x1, y1, x2, y2, cate in data.box[:]] + external = np.asarray(data.boundary) + xmin, xmax = np.min(external[:, 0]), np.max(external[:, 0]) + ymin, ymax = np.min(external[:, 1]), np.max(external[:, 1]) + + area_ = (ymax - ymin) * (xmax - xmin) + + data_js["rmsize"] = [ + [[20 * math.sqrt((float(x2) - float(x1)) * (float(y2) - float(y1)) / float(area_))], [mdul.room_label[cate][1]]] + for + x1, y1, x2, y2, cate in data.box[:]] + + + box_order = data.order + data_js["hsbox"] = [] + for i in range(len(box_order)): + data_js["hsbox"].append(hsbox[int(float(box_order[i])) - 1]) + + data_js["rmpos"] = [[int(cate), str(mdul.room_label[cate][1]), float((x1 + x2) / 2), float((y1 + y2) / 2)] for + x1, y1, x2, y2, cate in data.box[:]] + end = time.clock() + print('find train data time: %s Seconds' % (end - start)) + return data_js + + +def LoadTrainHouse(request): + trainname = request.GET.get("roomID").split(".")[0] + data_js = FindTraindata(trainname) + return HttpResponse(json.dumps(data_js), content_type="application/json") + + +''' + transfer the graph of the training data into the graph of the test data +''' + + +def TransGraph(request): + start = time.clock() + userInfo = request.GET.get("userInfo") + testname = userInfo.split(',')[0] + trainname = request.GET.get("roomID") + mlresult = mltest.get_userinfo(testname, trainname) + + fp_end = mlresult + + sio.savemat("./static/" + userInfo.split(',')[0].split('.')[0] + ".mat", {"data": fp_end.data}) + + data_js = {} + # fp_end hsedge + data_js["hsedge"] = (fp_end.get_triples(tensor=False)[:, [0, 2, 1]]).astype(np.float).tolist() + + # fp_rmsize + external = np.asarray(fp_end.data.boundary) + xmin, xmax = np.min(external[:, 0]), np.max(external[:, 0]) + ymin, ymax = np.min(external[:, 1]), np.max(external[:, 1]) + area_ = (ymax - ymin) * (xmax - xmin) + data_js["rmsize"] = [ + [[20 * math.sqrt((float(x2) - float(x1)) * (float(y2) - float(y1)) / float(area_))], [mdul.room_label[cate][1]]] + for + x1, y1, x2, y2, cate in fp_end.data.box[:]] + # fp_end rmpos + + rooms = fp_end.get_rooms(tensor=False) + + + center = [[(x1 + x2) / 2, (y1 + y2) / 2] for x1, y1, x2, y2 in fp_end.data.box[:, :4]] + + # boxes_pred + data_js["rmpos"] = [] + for k in range(len(center)): + node = float(rooms[k]), mdul.room_label[int(rooms[k])][1], center[k][0], center[k][1], float(k) + data_js["rmpos"].append(node) + + test_index = testNameList.index(testname.split(".")[0]) + data = test_data[test_index] + ex = "" + for i in range(len(data.boundary)): + ex = ex + str(data.boundary[i][0]) + "," + str(data.boundary[i][1]) + " " + data_js['exterior'] = ex + data_js["door"] = str(data.boundary[0][0]) + "," + str(data.boundary[0][1]) + "," + str( + data.boundary[1][0]) + "," + str(data.boundary[1][1]) + end = time.clock() + print('TransGraph time: %s Seconds' % (end - start)) + return HttpResponse(json.dumps(data_js), content_type="application/json") + + +def AdjustGraph(request): + start = time.clock() + # newNode index-typename-cx-cy + # oldNode index-typename-cx-cy + # newEdge u-v + NewGraph = json.loads(request.GET.get("NewGraph")) + testname = request.GET.get("userRoomID") + trainname = request.GET.get("adptRoomID") + s = time.clock() + mlresult = mltest.get_userinfo_adjust(testname, trainname, NewGraph) + e = time.clock() + print('get_userinfo_adjust: %s Seconds' % (e - s)) + fp_end = mlresult[0] + global boxes_pred + boxes_pred = mlresult[1] + + data_js = {} + data_js["hsedge"] = (fp_end.get_triples(tensor=False)[:, [0, 2, 1]]).astype(np.float).tolist() + + rooms = fp_end.get_rooms(tensor=False) + center = [[(x1 + x2) / 2, (y1 + y2) / 2] for x1, y1, x2, y2 in fp_end.data.box[:, :4]] + + box_order = mlresult[2] + ''' + handle the information of the room boxes + boxes_pred: the prediction from net + box_order: The order in which boxes are drawn + + ''' + room = [] + for o in range(len(box_order)): + room.append(float((rooms[int(float(box_order[o][0])) - 1]))) + boxes_end = [] + for i in range(len(box_order)): + tmp = [] + for j in range(4): + tmp.append(float(boxes_pred[int(float(box_order[i][0])) - 1][j])) + boxes_end.append(tmp) + + data_js['roomret'] = [] + for k in range(len(room)): + data = boxes_end[k], [mdul.room_label[int(room[k])][1]], box_order[k][0] - 1 + data_js['roomret'].append(data) + + # change the box size + global relbox + relbox = data_js['roomret'] + global reledge + reledge = data_js["hsedge"] + + test_index = testNameList.index(testname.split(".")[0]) + data = test_data[test_index] + ex = "" + for i in range(len(data.boundary)): + ex = ex + str(data.boundary[i][0]) + "," + str(data.boundary[i][1]) + " " + data_js['exterior'] = ex + data_js["door"] = str(data.boundary[0][0]) + "," + str(data.boundary[0][1]) + "," + str( + data.boundary[1][0]) + "," + str(data.boundary[1][1]) + + external = np.asarray(data.boundary) + xmin, xmax = np.min(external[:, 0]), np.max(external[:, 0]) + ymin, ymax = np.min(external[:, 1]), np.max(external[:, 1]) + area_ = (ymax - ymin) * (xmax - xmin) + data_js['rmsize'] = [] + for i in range(len(data_js['roomret'])): + rmsize = 20 * math.sqrt((float(data_js['roomret'][i][0][2]) - float(data_js['roomret'][i][0][0])) * ( + float(data_js['roomret'][i][0][3]) - float(data_js['roomret'][i][0][1])) / float(area_)), \ + data_js["roomret"][i][1][0] + data_js["rmsize"].append(rmsize) + + data_js["rmpos"] = [] + + newGraph = NewGraph[0] + for i in range(len(data_js['roomret'])): + for k in range(len(newGraph)): + if (data_js['roomret'][i][1][0] == newGraph[k][1]): + x_center = int((data_js['roomret'][i][0][0] + data_js['roomret'][i][0][2]) / 2) + y_center = int((data_js['roomret'][i][0][1] + data_js['roomret'][i][0][3]) / 2) + x_graph = newGraph[k][2] + y_graph = newGraph[k][3] + if ((int(x_graph - 30) < x_center < int(x_graph + 30))): + node = float(rooms[k]), newGraph[k][1], x_center, y_center, float( + newGraph[k][0]) + data_js["rmpos"].append(node) + newGraph.pop(k) + break + if ((int(y_graph - 30) < y_center < int(y_graph + 30))): + node = float(rooms[k]), newGraph[k][1], x_center, y_center, float( + newGraph[k][0]) + data_js["rmpos"].append(node) + newGraph.pop(k) + + break + + fp_end.data = add_dw_fp(fp_end.data) + data_js["indoor"] = [] + + boundary = data.boundary + + isNew = boundary[:, 3] + frontDoor = boundary[[0, 1]] + frontDoor = frontDoor[:, [0, 1]] + frontsum = frontDoor.sum(axis=1).tolist() + idx = frontsum.index(min(frontsum)) + wallThickness = 3 + if idx == 1: + frontDoor = frontDoor[[1, 0], :] + orient = boundary[0][2] + if orient == 0 or orient == 2: + frontDoor[0][0] = frontDoor[0][0] + wallThickness / 4 + frontDoor[1][0] = frontDoor[1][0] - wallThickness / 4 + if orient == 1 or orient == 3: + frontDoor[0][1] = frontDoor[0][1] + wallThickness / 4 + frontDoor[1][1] = frontDoor[1][1] - wallThickness / 4 + + + data_js["windows"] = [] + for indx, x, y, w, h, r in fp_end.data.windows: + if w != 0: + tmp = [x + 2, y - 2, w - 2, 4] + data_js["windows"].append(tmp) + if h != 0: + tmp = [x - 2, y, 4, h] + data_js["windows"].append(tmp) + data_js["windowsline"] = [] + for indx, x, y, w, h, r in fp_end.data.windows: + if w != 0: + tmp = [x + 2, y, w + x, y] + data_js["windowsline"].append(tmp) + if h != 0: + tmp = [x, y, x, h + y] + data_js["windowsline"].append(tmp) + + sio.savemat("./static/" + testname.split(',')[0].split('.')[0] + ".mat", {"data": fp_end.data}) + + end = time.clock() + print('AdjustGraph time: %s Seconds' % (end - start)) + return HttpResponse(json.dumps(data_js), content_type="application/json") + + +def RelBox(request): + id = request.GET.get("selectRect") + print(id) + global relbox + global reledge + rdirgroup=get_dir(id,relbox,reledge) + return HttpResponse(json.dumps(rdirgroup), content_type="application/json") + +def get_dir(id,relbox,reledge): + rel = [] + selectindex = int(id.split("_")[1]) + select = np.zeros(4).astype(int) + for i in range(len(relbox)): + a = math.ceil(relbox[i][0][0]), math.ceil(relbox[i][0][1]), math.ceil(relbox[i][0][2]), math.ceil( + relbox[i][0][3]), int(relbox[i][2]) + rel.append(a) + if (selectindex == int(relbox[i][2])): + # select:x1,x0,y0,y1.relbox:x0,y0,x1,y1 + select[0] = math.ceil(relbox[i][0][2]) + select[1] = math.ceil(relbox[i][0][0]) + select[2] = math.ceil(relbox[i][0][1]) + select[3] = math.ceil(relbox[i][0][3]) + rel = np.array(rel) + df = pd.DataFrame({'x0': rel[:, 0], 'y0': rel[:, 1], 'x1': rel[:, 2], 'y1': rel[:, 3], 'rindex': rel[:, 4]}) + group_label = [(0, 'x1', "right"), + (1, 'x0', "left"), + (2, 'y0', "top"), + (3, 'y1', "down")] + dfgroup = [] + for i in range(len(group_label)): + dfgroup.append(df.groupby(group_label[i][1], as_index=True).get_group(name=select[i])) + rdirgroup = [] + for i in range(len(dfgroup)): + dir = dfgroup[i] + rdir = [] + for k in range(len(dir)): + idx = (dir.loc[dir['rindex'] == (dir.iloc[[k]].values)[0][4]].index.values)[0] + rdir.append(relbox[idx][1][0].__str__() + "_" + (dir.iloc[[k]].values)[0][4].__str__()) + rdirgroup.append(rdir) + reledge = np.array(reledge) + data1 = reledge[np.where((reledge[:, [0]] == selectindex))[0]] + data2 = reledge[np.where((reledge[:, [1]] == selectindex))[0]] + reledge1 = np.vstack((data1, data2)) + return rdirgroup +def Save_Editbox(request): + global indxlist,boxes_pred + NewGraph = json.loads(request.GET.get("NewGraph")) + NewLay = json.loads(request.GET.get("NewLay")) + userRoomID = request.GET.get("userRoomID") + adptRoomID = request.GET.get("adptRoomID") + + NewLay=np.array(NewLay) + NewLay=NewLay[np.argsort(NewLay[:, 1])][:,2:] + NewLay=NewLay.astype(float).tolist() + + test_index = testNameList.index(userRoomID.split(".")[0]) + test_ = test_data[test_index] + + Boundary = test_.boundary + boundary=[[float(x),float(y),float(z),float(k)] for x,y,z,k in list(Boundary)] + test_fp =FloorPlan(test_) + + train_index = trainNameList.index(adptRoomID.split(".")[0]) + train_ =train_data[train_index] + train_fp =FloorPlan(train_,train=True) + fp_end = test_fp.adapt_graph(train_fp) + fp_end.adjust_graph() + newNode = NewGraph[0] + newEdge = NewGraph[1] + oldNode = NewGraph[2] + temp = [] + for newindx, newrmname, newx, newy,scalesize in newNode: + for type, oldrmname, oldx, oldy, oldindx in oldNode: + if (int(newindx) == oldindx): + tmp=int(newindx), (newx - oldx), ( newy- oldy),float(scalesize) + temp.append(tmp) + newbox=[] + if mltest.adjust==True: + oldbox = [] + for i in range(len(boxes_pred)): + indxtmp=[boxes_pred[i][0],boxes_pred[i][1],boxes_pred[i][2],boxes_pred[i][3],boxes_pred[i][0]] + oldbox.append(indxtmp) + if mltest.adjust==False: + indxlist=[] + oldbox=fp_end.data.box.tolist() + for i in range(len(oldbox)): + indxlist.append([oldbox[i][4]]) + indxlist=np.array(indxlist) + adjust=True + oldbox=fp_end.data.box.tolist() + X=0 + Y=0 + for i in range(len(oldbox)): + X= X+(oldbox[i][2]-oldbox[i][0]) + Y= Y+(oldbox[i][3]-oldbox[i][1]) + x_ave=(X/len(oldbox))/2 + y_ave=(Y/len(oldbox))/2 + + index_mapping = {} + # The room that already exists + # Move: Just by the distance + for newindx, tempx, tempy,scalesize in temp: + index_mapping[newindx] = len(newbox) + tmpbox=[] + scalesize = int(scalesize) + if scalesize<1: + scale = math.sqrt(scalesize) + scalex = (oldbox[newindx][2] - oldbox[newindx][0]) * (1 - scale) / 2 + scaley = (oldbox[newindx][3] - oldbox[newindx][1]) * (1 - scale) / 2 + tmpbox = [(oldbox[newindx][0] + tempx) + scalex, (oldbox[newindx][1] + tempy)+scaley, + (oldbox[newindx][2] + tempx) - scalex, (oldbox[newindx][3] + tempy) - scaley, oldbox[newindx][4]] + if scalesize == 1: + tmpbox = [(oldbox[newindx][0] + tempx) , (oldbox[newindx][1] + tempy) ,(oldbox[newindx][2] + tempx), (oldbox[newindx][3] + tempy), oldbox[newindx][4]] + + if scalesize>1: + scale=math.sqrt(scalesize) + scalex = (oldbox[newindx][2] - oldbox[newindx][0]) * ( scale-1) / 2 + scaley = (oldbox[newindx][3] - oldbox[newindx][1]) * (scale-1) / 2 + tmpbox = [(oldbox[newindx][0] + tempx) - scalex, (oldbox[newindx][1] + tempy) - scaley, + (oldbox[newindx][2] + tempx) + scalex, (oldbox[newindx][3] + tempy) + scaley, oldbox[newindx][4]] + + newbox.append(tmpbox) + + # The room just added + # Move: The room node with the average size of the existing room + for newindx, newrmname, newx, newy,scalesize in newNode: + if int(newindx)>(len(oldbox)-1): + scalesize=int(scalesize) + index_mapping[int(newindx)] = (len(newbox)) + tmpbox=[] + if scalesize < 1: + scale = math.sqrt(scalesize) + scalex = x_ave * (1 - scale) / 2 + scaley = y_ave* (1 - scale) / 2 + tmpbox = [(newx-x_ave) +scalex,(newy-y_ave) +scaley,(newx+x_ave)-scalex,(newy+y_ave)-scaley,vocab['object_name_to_idx'][newrmname]] + + if scalesize == 1: + tmpbox = [(newx - x_ave), (newy - y_ave), (newx + x_ave), (newy + y_ave),vocab['object_name_to_idx'][newrmname]] + if scalesize > 1: + scale = math.sqrt(scalesize) + scalex = x_ave * (scale - 1) / 2 + scaley = y_ave * (scale - 1) / 2 + tmpbox = [(newx-x_ave) - scalex, (newy-y_ave) - scaley,(newx+x_ave) + scalex, (newy+y_ave) + scaley,vocab['object_name_to_idx'][newrmname]] + # tmpboxin = [(newx-x_ave) ,(newy-y_ave) ,(newx+x_ave) ,(newy+y_ave) ,vocab['object_name_to_idx'][newrmname]] + # print(tmpboxin) + # print(tmpbox) + # print(scalesize) + newbox.append(tmpbox) + + fp_end.data.box=np.array(newbox) + + adjust_Edge=[] + for u, v in newEdge: + tmp=[index_mapping[int(u)],index_mapping[int(v)], 0] + adjust_Edge.append(tmp) + fp_end.data.edge=np.array(adjust_Edge) + rType = fp_end.get_rooms(tensor=False) + + rEdge = fp_end.get_triples(tensor=False)[:, [0, 2, 1]] + Edge = [[float(u), float(v), float(type2)] for u, v, type2 in rEdge] + Box=NewLay + boundary_mat = matlab.double(boundary) + rType_mat = matlab.double(rType.tolist()) + Edge_mat = matlab.double(Edge) + Box_mat=matlab.double(Box) + fp_end.data.boundary =np.array(boundary) + fp_end.data.rType =np.array(rType).astype(int) + fp_end.data.refineBox=np.array(Box) + fp_end.data.rEdge=np.array(Edge) + + box_refine = engview.align_fp(boundary_mat, Box_mat, rType_mat,Edge_mat ,18,False, nargout=3) + box_out=box_refine[0] + box_order=box_refine[1] + rBoundary=box_refine[2] + fp_end.data.newBox = np.array(box_out) + fp_end.data.order = np.array(box_order) + fp_end.data.rBoundary = [np.array(rb) for rb in rBoundary] + fp_end.data = add_dw_fp(fp_end.data) + sio.savemat("./static/" + userRoomID + ".mat", {"data": fp_end.data}) + flag=1 + return HttpResponse(json.dumps(flag), content_type="application/json") + + +def TransGraph_net(request): + userInfo = request.GET.get("userInfo") + testname = userInfo.split(',')[0] + trainname = request.GET.get("roomID") + mlresult = mltest.get_userinfo_net(testname, trainname) + + fp_end = mlresult[0] + boxes_pred = mlresult[1] + + data_js = {} + # fp_end hsedge + data_js["hsedge"] = (fp_end.get_triples(tensor=False)[:, [0, 2, 1]]).astype(np.float).tolist() + + # fp_end rmpos + rooms = fp_end.get_rooms(tensor=False) + room = rooms + center = [[(x1 + x2) / 2, (y1 + y2) / 2] for x1, y1, x2, y2 in fp_end.data.box[:, :4]] + + + + # boxes_pred + data_js["rmpos"] = [] + for k in range(len(center)): + node = float(room[k]), mdul.room_label[int(room[k])][1], center[k][0], center[k][1] + data_js["rmpos"].append(node) + boxes_end = boxes_pred.tolist() + data_js['roomret'] = [] + for k in range(len(room)): + data = boxes_end[k], [mdul.room_label[int(room[k])][1]] + data_js['roomret'].append(data) + + test_index = testNameList.index(testname.split(".")[0]) + data = test_data[test_index] + ex = "" + for i in range(len(data.boundary)): + ex = ex + str(data.boundary[i][0]) + "," + str(data.boundary[i][1]) + " " + data_js['exterior'] = ex + x0, x1 = np.min(data.boundary[:, 0]), np.max(data.boundary[:, 0]) + y0, y1 = np.min(data.boundary[:, 1]), np.max(data.boundary[:, 1]) + data_js['bbxarea'] = float((x1 - x0) * (y1 - y0)) + return HttpResponse(json.dumps(data_js), content_type="application/json") + + +def GraphSearch(request): + s=time.clock() + # Graph + Searchtype = ["BedRoom", "Bathroom", "Kitchen", "Balcony", "Storage"] + BedRoomlist = ["MasterRoom", "SecondRoom", "GuestRoom", "ChildRoom", "StudyRoom"] + NewGraph = json.loads(request.GET.get("NewGraph")) + + testname = request.GET.get("userRoomID") + newNode = NewGraph[0] + newEdge = NewGraph[1] + r_Num = np.zeros((1, 14)).tolist() + r_Mask = np.zeros((1, 14)).tolist() + r_Acc = np.zeros((1, 14)).tolist() + r_Num[0][0] = 1 + r_Mask[0][0] = 1 + r_Acc[0][0] = 1 + + for indx, rmname, x, y, scalesize in newNode: + r_Num[0][mdul.vocab['object_name_to_idx'][rmname]] = r_Num[0][mdul.vocab['object_name_to_idx'][rmname]] + 1 + r_Mask[0][mdul.vocab['object_name_to_idx'][rmname]] = 1 + if rmname in BedRoomlist: + r_Num[0][13] = r_Num[0][13] + 1 + r_Mask[0][13] = 1 + + test_index = testNameList.index(testname.split(".")[0]) + topkList = [] + topkList.clear() + data = test_data[test_index] + + Numrooms = json.loads(request.GET.get("Numrooms")) + + + roomactarr = Numrooms[0] + roomexaarr = Numrooms[1] + roomnumarr = [int(x) for x in Numrooms[2]] + test_data_topk=np.arange(0,74995) + + if np.sum(roomactarr) != 1 or np.sum(roomexaarr) != 1 or np.sum(roomnumarr) != 1: + test_num = train_data_rNum[test_data_topk] + # Number filter + + filter_func = get_filter_func(roomactarr, roomexaarr, roomnumarr) + indices = np.where(list(map(filter_func, test_num))) + # print("np.where(list(map(fil", test_num) + indices = list(indices) + test_data_topk = test_data_topk[indices[0]] + + test_num = train_data_eNum[test_data_topk] + # Graph filter + + edgematrix = np.zeros((5, 5)) + for indx1, indx2 in newEdge: + tmp1 = "" + tmp2 = "" + for indx, rmname, x, y, scalesize in newNode: + if indx1 == indx: + if rmname in BedRoomlist: + tmp1 = "BedRoom" + else: + tmp1 = rmname + for indx, rmname, x, y, scalesize in newNode: + if indx2 == indx: + if rmname in BedRoomlist: + tmp2 = "BedRoom" + else: + tmp2 = rmname + if tmp1 != "" and tmp2 != "": + edgematrix[Searchtype.index(tmp1)][Searchtype.index(tmp2)] = edgematrix[Searchtype.index(tmp1)][ + Searchtype.index(tmp2)] + 1 + edgematrix[Searchtype.index(tmp2)][Searchtype.index(tmp1)] = edgematrix[Searchtype.index(tmp2)][ + Searchtype.index(tmp1)] + 1 + edge = edgematrix.reshape((1, 25)) + filter_graphfunc = filter_graph(edge) + # rNum_list + eNumData = [] + + indices = np.where(list(map(filter_graphfunc, test_num))) + + indices = list(indices) + tf_trainsub=tf_train[test_data_topk[indices[0]]] + re_data = train_data[test_data_topk[indices[0]]] + test_data_tftopk=retrieve_bf(tf_trainsub, data, k=20) + re_data=re_data[test_data_tftopk] + if len(re_data) < 20: + topk = len(re_data) + else: + topk = 20 + topkList = [] + for i in range(topk): + topkList.append(str(re_data[i].name) + ".png") + + e=time.clock() + print('Graph Search time: %s Seconds' % (e - s)) + + print("topkList", topkList) + return HttpResponse(json.dumps(topkList), content_type="application/json") + + +def retrieve_bf(tf_trainsub, datum, k=20): + # compute tf for the data boundary + x, y = rt.compute_tf(datum.boundary) + y_sampled = rt.sample_tf(x, y, 1000) + dist = np.linalg.norm(y_sampled - tf_trainsub, axis=1) + if k > np.log2(len(tf_trainsub)): + index = np.argsort(dist)[:k] + else: + index = np.argpartition(dist, k)[:k] + index = index[np.argsort(dist[index])] + return index + + +if __name__ == "__main__": + pass \ No newline at end of file diff --git a/Img/data.mat.png b/Img/data.mat.png new file mode 100644 index 0000000000000000000000000000000000000000..f1850253c21f93e79c881e7dfd2d6cecb51963e0 Binary files /dev/null and b/Img/data.mat.png differ diff --git a/Img/data_test_converted.png b/Img/data_test_converted.png new file mode 100644 index 0000000000000000000000000000000000000000..e353f0429b080a6b2cd54d07bd609865a900a712 Binary files /dev/null and b/Img/data_test_converted.png differ diff --git a/Img/data_train_converted.png b/Img/data_train_converted.png new file mode 100644 index 0000000000000000000000000000000000000000..71996f79d42dd04b2a7a0838588910b90964fa2c Binary files /dev/null and b/Img/data_train_converted.png differ diff --git a/Img/interface.jpg b/Img/interface.jpg new file mode 100644 index 0000000000000000000000000000000000000000..75e2ada4fcbfdca7c8e1d6e1bb7e4931b75b4ef6 Binary files /dev/null and b/Img/interface.jpg differ diff --git a/Img/paper.png b/Img/paper.png new file mode 100644 index 0000000000000000000000000000000000000000..5c745ebf30d643c9ed7765fb682c6d2a6ba2b538 Binary files /dev/null and b/Img/paper.png differ diff --git a/align_fp/align_adjacent_room3.m b/align_fp/align_adjacent_room3.m new file mode 100644 index 0000000000000000000000000000000000000000..f2bbb1e11661da6daa704baf023dcdc2e792a3a4 --- /dev/null +++ b/align_fp/align_adjacent_room3.m @@ -0,0 +1,121 @@ +function [newBox, constraint] = align_adjacent_room3(box, tempBox, updated, type, threshold) +% position of box1 relative to box2 +% 0 left-above +% 1 left-below +% 2 left-of +% 3 above +% 4 inside +% 5 surrounding +% 6 below +% 7 right-of +% 8 right-above +% 9 right-below + + +newBox = box; +constraint = zeros(4, 2); +idx = 1; + +if type == 0 + alignV(true); + alignH(true); +elseif type == 1 + alignV(true); + alignH(false); +elseif type == 2 + align([2,1], [1,3], threshold); + align([2,2], [1,2], threshold/2); + align([2,4], [1,4], threshold/2); +elseif type == 3 + align([2,2], [1,4], threshold); + align([2,1], [1,1], threshold/2); + align([2,3], [1,3], threshold/2); +elseif type == 4 + align([2,1], [1,1], true); + align([2,2], [1,2], true); + align([2,3], [1,3], true); + align([2,4], [1,4], true); +elseif type == 5 + align([1,1], [2,1], true); + align([1,2], [2,2], true); + align([1,3], [2,3], true); + align([1,4], [2,4], true); +elseif type == 6 + align([2,4], [1,2], threshold); + align([2,1], [1,1], threshold/2); + align([2,3], [1,3], threshold/2); +elseif type == 7 + align([2,3], [1,1], threshold); + align([2,2], [1,2], threshold/2); + align([2,4], [1,4], threshold/2); +elseif type == 8 + alignV(false); + alignH(true); +elseif type == 9 + alignV(false); + alignH(false); +end + +constraint = constraint(1:idx-1, :); + +function alignV(isLeft) + if isLeft + idx1 = 1; + idx2 = 3; + else + idx1 = 3; + idx2 = 1; + end + + if abs(tempBox(2,idx1) - tempBox(1,idx2)) <= abs(tempBox(2,idx2) - tempBox(1,idx2)) + align([2,idx1], [1,idx2], threshold/2) + else + align([2,idx2], [1,idx2], threshold/2) + end +end + +function alignH(isAbove) + if isAbove + idx1 = 2; + idx2 = 4; + else + idx1 = 4; + idx2 = 2; + end + + if abs(tempBox(2,idx1) - tempBox(1,idx2)) <= abs(tempBox(2,idx2) - tempBox(1,idx2)) + align([2,idx1], [1,idx2], threshold/2) + else + align([2,idx2], [1,idx2], threshold/2) + end +end + +function align(idx1, idx2, threshold, attach) + if nargin < 4 + attach = false; + end + if abs(tempBox(idx1(1),idx1(2))- tempBox(idx2(1), idx2(2))) <= threshold + if updated(idx1(1), idx1(2)) && ~updated(idx2(1), idx2(2)) + newBox(idx2(1), idx2(2)) = newBox(idx1(1),idx1(2)); + elseif updated(idx2(1), idx2(2)) && ~updated(idx1(1), idx1(2)) + newBox(idx1(1), idx1(2)) = newBox(idx2(1),idx2(2)); + elseif ~updated(idx1(1), idx1(2)) && ~updated(idx2(1), idx2(2)) + if attach + newBox(idx2(1), idx2(2)) = newBox(idx1(1),idx1(2)); + else + y = (newBox(idx1(1),idx1(2)) + newBox(idx2(1), idx2(2)))/2; + newBox(idx1(1),idx1(2)) = y; + newBox(idx2(1), idx2(2)) = y; + end + end + + if idx1(1) == 1 + constraint(idx, :) = [idx1(2) idx2(2)]; + else + constraint(idx, :) = [idx2(2) idx1(2)]; + end + idx = idx + 1; + end +end + +end \ No newline at end of file diff --git a/align_fp/align_fp.m b/align_fp/align_fp.m new file mode 100644 index 0000000000000000000000000000000000000000..21d1ef3168ff08f688e40478cc1ce51bd0ed495b --- /dev/null +++ b/align_fp/align_fp.m @@ -0,0 +1,108 @@ +function [newBox, order, rBoundary] = align_fp(boundary, rBox, rType, rEdge, fp, threshold, drawResult) +% align the neighboring rooms first and then align with the boundary + +if nargin < 7 + drawResult =false; +end + +% pre-processing: +% move the edge relation w.r.t. living room to the end +livingIdx = find(rType==0); +idx = rEdge(:,1) == livingIdx-1 | rEdge(:,2) == livingIdx-1; +% a = rEdge(~idx, :); +% b = rEdge(idx, :); +% rEdge = [a; b]; +rEdge = rEdge(~idx, :); +entranceBox = get_entrance_space(boundary(1:2, 1:2), boundary(1,3), threshold); + +if drawResult + clf + subplot(2,2,1) + plot_fp(rBox, boundary, rType, entranceBox); + title('original'); +end + +%% option #1: use greedy method: align with boundary first and then neighbor +% 1. align with boundary after the neighbors have been aligned +[~, newBox, updated] = align_with_boundary(rBox, boundary, threshold, rType); + +if drawResult + subplot(2,2,2) + plot_fp(newBox, boundary, rType, entranceBox); + title('Align with boundary'); +end + + +% 2. for each adjacent pair of room, +[~, newBox, ~] = align_neighbor(newBox, rEdge, updated, threshold+6); +if drawResult + subplot(2,2,3) + plot_fp(newBox, boundary, rType, entranceBox); + title('Align with neighbors'); +end + +% 3. regularize fp, include crop using boundary, gap filling +[newBox, order] = regularize_fp(newBox, boundary, rType); + +% 4. generate the room polygons +[newBox, rBoundary] = get_room_boundary(newBox, boundary, order); + +if drawResult + subplot(2,2,4) + plot_fp(newBox(order,:), boundary, rType(order), entranceBox); + title('Regularize fp'); +end + +% %% option #2: use optimization to align neighbors, and then align the boundary +% % 1. get the constraint from the adjacent rooms, and optimize +% %[constraint1, ~, ~] = align_with_boundary(rBox, boundary, threshold, rNode); +% [constraint2, ~, ~] = align_neighbor(rBox, rEdge, [], threshold+2); +% newBox = optimize_fp(rBox, [], constraint2); +% if drawResult +% subplot(3,4,6) +% plot_fp(newBox, boundary, rNode, entranceBox); +% title('Optimize the neighboring'); +% end +% +% % 2. align with boundary after the neighbors have been aligned +% [constraint1, newBox2, ~] = align_with_boundary(newBox, boundary, threshold, rNode); +% if drawResult +% subplot(3,4,7) +% plot_fp(newBox2, boundary, rNode, entranceBox); +% title('Align with boundary w/o optimization'); +% end +% +% % 3. regularize fp, include crop using boundary, gap filling +% [newBox2, order] = regularize_fp(newBox2, boundary, rNode); +% if drawResult +% subplot(3,4,8) +% plot_fp(newBox2(order,:), boundary, rNode(order,:), entranceBox); +% title('Regularize fp'); +% end +% +% +% +% newBox = optimize_fp(newBox, constraint1, constraint2); +% if drawResult +% subplot(3,4,11) +% plot_fp(newBox, boundary, rNode, entranceBox); +% title('Align with boundary with optimization'); +% end +% +% % 3. regularize fp, include crop using boundary, gap filling +% [newBox, order] = regularize_fp(newBox, boundary, rNode); +% if drawResult +% subplot(3,4,12) +% plot_fp(newBox(order,:), boundary, rNode(order,:), entranceBox); +% title('Regularize fp'); +% if ~isempty(figName) +% saveas(gcf, figName); +% end +% end +% + + + +%% +end + diff --git a/align_fp/align_neighbor.m b/align_fp/align_neighbor.m new file mode 100644 index 0000000000000000000000000000000000000000..7fae9328b43615ae54c40e3179dfe0b0340ef795 --- /dev/null +++ b/align_fp/align_neighbor.m @@ -0,0 +1,53 @@ +function [constraint, box, updated] = align_neighbor(box, rEdge, updated, threshold) + +if isempty(updated) + updated = false(size(box)); +end + +tempBox = box; +constraint = zeros(size(rEdge, 1)*3, 2); +iBegin = 1; +checked = false(size(rEdge, 1), 1); +updatedCount = get_updated_count(updated, rEdge); +for i = 1:size(rEdge, 1) + I = find(~checked); + [~, t] = maxk(updatedCount(I), 1); + checked(I(t)) = true; + idx = rEdge(I(t),1:2)+1; + [b, c] = align_adjacent_room3(box(idx, :), tempBox(idx, :), updated(idx,:), rEdge(I(t),3), threshold); + for j = 1:length(idx) + + updated(idx(j), c(:,j)) = true; + + c(:, j) = (c(:,j)-1)*size(box,1) + double(idx(j)); + + if b(j, 1) == b(j, 3) + b(j, [1 3]) = box(idx(j), [1 3]); + updated(idx(j), c(:,j)) = false; + end + if b(j, 2) == b(j, 4) + b(j, [2 4]) = box(idx(j), [2 4]); + updated(idx(j), c(:,j)) = false; + end + + end + box(idx, :) = b; + + + cNum = size(c, 1); + + constraint(iBegin:iBegin+cNum-1, :) = c; + iBegin = iBegin+cNum; + + updatedCount = get_updated_count(updated, rEdge); +end +constraint = constraint(1:iBegin-1, :); + +function updatedCount = get_updated_count(updated, rEdge) + updatedCount = zeros(size(rEdge, 1), 1); + for k = 1:size(rEdge, 1) + index = rEdge(k,1:2)+1; + updatedCount(k) = sum(sum(updated(index,:))); + end +end +end \ No newline at end of file diff --git a/align_fp/align_with_boundary.m b/align_fp/align_with_boundary.m new file mode 100644 index 0000000000000000000000000000000000000000..fab88f12ddfe050f8959812f9b784c610e61d094 --- /dev/null +++ b/align_fp/align_with_boundary.m @@ -0,0 +1,29 @@ +function [constraint, box, updated] = align_with_boundary(box, boundary, threshold, rType) +tempBox = box; +updated = false(size(box)); +closedSeg = zeros(size(box)); +distSeg = zeros(size(box)); +for i = 1:length(box) + [closedSeg(i,:), distSeg(i,:)] = find_close_seg(box(i,:), boundary); +end + + +box(distSeg <= threshold) = closedSeg(distSeg <= threshold); +updated(distSeg <= threshold) = true; +idx = find(distSeg <= threshold); +constraint = [idx closedSeg(idx)]; + + +% check if any room box blocks the door +entranceBox = get_entrance_space(boundary(1:2, 1:2), boundary(1,3), threshold); +entrancePoly = polyshape(entranceBox([1 1 3 3]), entranceBox([2 4 4 2])); +for i = 1:length(box) + if rType(i) ~= 10 && rType(i) ~= 0 + roomPoly = polyshape(box(i, [1 1 3 3]), box(i, [2 4 4 2])); + if overlaps(entrancePoly, roomPoly) + box(i,:) = shrink_box(roomPoly, entrancePoly, boundary(1,3)); + updated(i, box(i,:)==tempBox(i,:)) = false; + updated(i, box(i,:)~=tempBox(i,:)) = true; + end + end +end diff --git a/align_fp/find_close_seg.m b/align_fp/find_close_seg.m new file mode 100644 index 0000000000000000000000000000000000000000..97132947933b8484c75d491958d8c62a040e5c7f --- /dev/null +++ b/align_fp/find_close_seg.m @@ -0,0 +1,74 @@ +function [closedSeg, distSeg, idx] = find_close_seg(box, boundary) + +% need to carefully select the closed wall seg for each box +% cannot introduce a hole inside the boundary + +isNew = boundary(:,4); +boundary = double(boundary(~isNew, :)); + +% get the ordered horizontal and vertical segments on the boundary +bSeg = [boundary(:, 1:2), boundary([2:end 1], 1:2), boundary(:,3)]; +vSeg = bSeg(mod(boundary(:,3), 2)==1, :); +vSeg(vSeg(:,5)==3, [2 4]) = vSeg(vSeg(:,5)==3, [4 2]); +[~, I] = sort(vSeg(:,1)); +vSeg = vSeg(I,:); + +hSeg = bSeg(mod(boundary(:,3), 2)==0, :); +hSeg(hSeg(:,5)==2, [1 3]) = hSeg(hSeg(:,5)==2, [3 1]); +[~, I] = sort(hSeg(:,2)); +hSeg = hSeg(I,:); + +closedSeg = ones(1,4)*256; +distSeg = ones(1,4)*256; +idx = zeros(1, 4); + +% check vertial seg +for i = 1:size(vSeg,1) + seg = vSeg(i, :); + vdist = 0; + if seg(4) <= box(2) + vdist = box(2) - seg(4); + elseif seg(2) >= box(4) + vdist = seg(2) - box(4); + end + + hdist = box([1 3]) - seg(1); + dist1 = norm(double([hdist(1), vdist])); + dist3 = norm(double([hdist(2), vdist])); + + if dist1 < distSeg(1) && dist1 <= dist3 && hdist(1) > 0 + distSeg(1) = dist1; + idx(1) = i; + closedSeg(1) = seg(1); + elseif dist3 < distSeg(3) && hdist(2) < 0 + distSeg(3) = dist3; + idx(3) = i; + closedSeg(3) = seg(3); + end +end + +% check horizontal seg +for i = 1:size(hSeg,1) + + seg = hSeg(i, :); + hdist = 0; + if seg(3) <= box(1) + hdist = box(1) - seg(3); + elseif seg(1) >= box(3) + hdist = seg(1) - box(3); + end + + vdist = box([2 4]) - seg(2); + dist2 = norm(double([vdist(1), hdist])); + dist4 = norm(double([vdist(2), hdist])); + + if dist2 <= dist4 && dist2 < distSeg(2) && vdist(1) > 0 + distSeg(2) = dist2; + idx(2) = i; + closedSeg(2) = seg(2); + elseif dist4 < distSeg(4) && vdist(2) < 0 + distSeg(4) = dist4; + idx(4) = i; + closedSeg(4) = seg(4); + end +end \ No newline at end of file diff --git a/align_fp/find_room_order.m b/align_fp/find_room_order.m new file mode 100644 index 0000000000000000000000000000000000000000..0524e2c2b75bcd2c1c88878a6e6f2eb41eeafa90 --- /dev/null +++ b/align_fp/find_room_order.m @@ -0,0 +1,29 @@ +function order = find_room_order(M) + +n = size(M,1); +G = digraph(M); +name = cell(n,1); +for i = 1:n + name{i} = num2str(i); +end +G.Nodes.Name = name; + +order = zeros(n, 1); +i = 1; +while i <= n + D = indegree(G); + c = find(D==0); + if isempty(c) + idx = find(D==1); + c = setdiff(idx, order); + order(i) = str2double(G.Nodes.Name{c(1)}); + G = rmnode(G, c(1)); + i = i+1; + else + for j = 1:length(c) + order(i+j-1) = str2double(G.Nodes.Name{c(j)}); + end + G = rmnode(G, c); + i = i + length(c); + end +end \ No newline at end of file diff --git a/align_fp/get_entrance_space.m b/align_fp/get_entrance_space.m new file mode 100644 index 0000000000000000000000000000000000000000..50540c2e83358873297873fa3c64cff581573eef --- /dev/null +++ b/align_fp/get_entrance_space.m @@ -0,0 +1,14 @@ + +function doorBox = get_entrance_space(doorSeg, doorOri, threshold) + +doorBox = [doorSeg(1,:) doorSeg(2,:)]; +if doorOri == 0 + doorBox(4) = doorBox(4) + threshold; +elseif doorOri == 1 + doorBox(1) = doorBox(1) - threshold; +elseif doorOri == 2 + doorBox(2) = doorBox(2) - threshold; +elseif doorOri == 3 + doorBox(3) = doorBox(3) + threshold; +end + \ No newline at end of file diff --git a/align_fp/get_room_boundary.m b/align_fp/get_room_boundary.m new file mode 100644 index 0000000000000000000000000000000000000000..cd29593ed4ca94585e1204f17fdcc4089c55929b --- /dev/null +++ b/align_fp/get_room_boundary.m @@ -0,0 +1,25 @@ +function [newBox, rBoundary] = get_room_boundary(box, boundary, order) + +isNew = boundary(:,4); +polyBoundary = polyshape(boundary(~isNew,1), boundary(~isNew,2)); + +poly = cell(size(box,1), 1); +for i = 1:size(box,1) + poly{i} = polyshape(box(i, [1 1 3 3]), box(i, [2 4 4 2])); +end + +newBox = box; +rBoundary = cell(size(box,1), 1); +for i = 1:size(box,1) + idx = order(i); + + rPoly = intersect(polyBoundary, poly{idx}); + for j = i+1:size(box,1) + rPoly = subtract(rPoly, poly{order(j)}); + end + rBoundary{idx} = rPoly.Vertices; + [xLimit, yLimit]= boundingbox(rPoly); + if ~isempty(xLimit) + newBox(idx,:) = [xLimit(1) yLimit(1) xLimit(2), yLimit(2)]; + end +end \ No newline at end of file diff --git a/align_fp/regularize_fp.m b/align_fp/regularize_fp.m new file mode 100644 index 0000000000000000000000000000000000000000..8668aa72597d09a53e191ceeb372df7da24d13c2 --- /dev/null +++ b/align_fp/regularize_fp.m @@ -0,0 +1,111 @@ +function [box, order] = regularize_fp(box, boundary, rType) + +% 1. use the boundary to crop each room box +isNew = boundary(:,4); +polyBoundary = polyshape(boundary(~isNew,1), boundary(~isNew,2)); +for i = 1:size(box, 1) + polyRoom = polyshape(box(i, [1 1 3 3]), box(i, [2 4 4 2])); + [xLimit, yLimit] = boundingbox(intersect(polyBoundary,polyRoom)); + if isempty(xLimit) + disp('One room outside the building!'); + else + box(i,:) = [xLimit(1) yLimit(1) xLimit(2), yLimit(2)]; + end +end + + +% 2. check if there is any overlapped region to determine the layer of boxes +orderM = false(size(box,1), size(box,1)); +for i = 1:size(box,1) + polyRoom1 = polyshape(box(i, [1 1 3 3]), box(i, [2 4 4 2])); + area1 = area(polyRoom1); + for j = i+1:size(box,1) + polyRoom2 = polyshape(box(j, [1 1 3 3]), box(j, [2 4 4 2])); + area2 = area(polyRoom2); + inter = intersect(polyRoom1, polyRoom2); + if inter.NumRegions >= 1 + if area1 <= area2 % may need to add the FP into consideration + orderM(i,j) = true; + else + orderM(j,i) = true; + end + end + end +end +order = 1:size(box,1); +if any(orderM(:)) + order = find_room_order(orderM); +end +order = order(end:-1:1); + +% 3. check if there are more than one uncovered regions inside the building +livingIdx = find(rType==0); +for i = 1:size(box, 1) + if i ~= livingIdx + if box(i,1)==box(i,3) || box(i,2)==box(i,4) + disp('Empty box!!!'); + else + polyRoom = polyshape(box(i, [1 1 3 3]), box(i, [2 4 4 2])); + polyBoundary = subtract(polyBoundary,polyRoom); + end + + end +end +livingPoly = polyshape(box(livingIdx, [1 1 3 3]), box(livingIdx, [2 4 4 2])); + +gap = polyBoundary; +if gap.NumRegions == 1 + [xLimit, yLimit] = boundingbox(gap); + box(livingIdx,:) = [xLimit(1) yLimit(1) xLimit(2), yLimit(2)]; +else + rIdx = find(isnan(gap.Vertices(:,1))); + rIdx = [rIdx; size(gap.Vertices,1)+1]; + + % for each region, check if it intersects with the living room, + % otherwise get the room label and find the room that should cover + % the region + + region = cell(length(rIdx), 1); + overlapArea = zeros(length(rIdx), 1); + closeRoomIdx = zeros(length(rIdx), 1); + idx = 1; + for k = 1:length(rIdx) + regionV = gap.Vertices(idx:rIdx(k)-1, :); + idx = rIdx(k) + 1; + region{k} = polyshape(regionV); + + if overlaps(region{k}, livingPoly) + iter = intersect(region{k}, livingPoly); + overlapArea(k) = area(iter); + end + + [x, y] = centroid(region{k}); + center = [x, y]; + + dist = 256; + bIdx = 0; + for i = 1:size(box, 1) + b = box(i, :); + bCenter = double([(b(:,1)+b(:,3))/2, (b(:,2)+b(:,4))/2]); + d = norm(bCenter-center); + if d 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): + # data要有 boundary(网络输出经过matlab新返回的),newBox(网络输出经过matlab新返回的),rType,rBoundary(网络输出经过matlab新返回的) + doors,windows = add_door_window(data) + data.doors = doors + data.windows = windows + return data diff --git a/model/floorplan.py b/model/floorplan.py new file mode 100644 index 0000000000000000000000000000000000000000..8326d9da6e8d2314de4fe71fb39e515f9e35e8e7 --- /dev/null +++ b/model/floorplan.py @@ -0,0 +1,210 @@ +import torch +import scipy.io as sio +import numpy as np +import cv2 +import copy +from model.utils import * + + +class FloorPlan(): + + def __init__(self, data, train=False, rot=None): + self.data = copy.deepcopy(data) + self._get_rot() + if rot is not None: + if train: + boxes = self.data.box[:, :4][:, [1, 0, 3, 2]] + boxes = align_box(boxes, self.rot, rot)[:, [1, 0, 3, 2]] + self.data.box[:, :4] = boxes + points = self.data.boundary[:, :2][:, [1, 0]] + points = align_points(points, self.rot, rot)[:, [1, 0]] + self.data.boundary[:, :2] = points + self._get_rot() + + def _get_rot(self): + door_line = self.data.boundary[:2, :2] # [:,[1,0]] + c = door_line.mean(0) - np.array([127.5,127.5]) + theta = np.arctan2(c[1], c[0]) + np.pi # [-pi,pi] + self.rot = theta + + def get_input_boundary(self, tensor=True): + external = self.data.boundary[:, :2] + door = self.data.boundary[:2, :2] + + boundary = np.zeros((128, 128), dtype=float) + inside = np.zeros((128, 128), dtype=float) + front = np.zeros((128, 128), dtype=float) + + pts = np.concatenate([external, external[:1]]) // 2 + pts_door = door // 2 + + cv2.fillPoly(inside, pts.reshape(1, -1, 2), 1.0) + cv2.polylines(boundary, pts.reshape(1, -1, 2), True, 1.0, 3) + cv2.polylines(boundary, pts_door.reshape(1, -1, 2), True, 0.5, 3) + cv2.polylines(front, pts_door.reshape(1, -1, 2), True, 1.0, 3) + + input_image = np.stack([inside, boundary, front], -1) + if tensor: input_image = torch.tensor(input_image).permute((2, 0, 1)).float() + return input_image + + def get_inside_box(self, tensor=True): + external = self.data.boundary[:, :2] + + X, Y = np.linspace(0, 1, 256), np.linspace(0, 1, 256) + x0, x1 = np.min(external[:, 0]), np.max(external[:, 0]) + y0, y1 = np.min(external[:, 1]), np.max(external[:, 1]) + box = np.array([[X[x0], Y[y0], X[x1], Y[y1]]]) + if tensor: box = torch.tensor(box).float() + return box + + def get_rooms(self, tensor=True): + rooms = self.data.box[:, -1] + if tensor: rooms = torch.tensor(rooms).long() + return rooms + + def get_attributes(self, gsize=5, alevel=10, relative=True, tensor=True): + boxes = self.data.box[:, :4][:, [1, 0, 3, 2]] + external = self.data.boundary + + h, w = 256, 256 + if relative: + external = np.asarray(external) + x0, x1 = np.min(external[:, 0]), np.max(external[:, 0]) + y0, y1 = np.min(external[:, 1]), np.max(external[:, 1]) + h, w = y1 - y0, x1 - x0 + boxes = boxes - np.array([y0, x0, y0, x0], dtype=float) + boxes /= np.array([h, w, h, w]) + boxes[:, 2:] -= boxes[:, :2] # y1,x1->h,w + boxes[:, :2] += boxes[:, 2:] / 2 # y0,x0->yc,xc + + l = len(boxes) + gbins = np.linspace(0,1,gsize+1) # [1,gsize] + gbins[0],gbins[-1]=-np.inf,np.inf + abins = np.linspace(0,1,alevel+1) # [1,gsize] + abins[0],abins[-1]=-np.inf,np.inf + + attributes = np.zeros((l,gsize*gsize+alevel)) + # pos: xc*gsize+yc*gsize*gsize + attributes[range(l),(np.digitize(boxes[:,0],gbins)-1)*gsize+np.digitize(boxes[:,1],gbins)-1]=1 + # area:(w*h) + attributes[range(l),gsize*gsize+np.digitize(boxes[:,2:].prod(1),abins)-1]=1 + if tensor: attributes = torch.tensor(attributes).float() + return attributes + + def get_triples(self, random=False, tensor=True): + boxes = self.data.box[:, :4][:, [1, 0, 3, 2]] + + triples = [] + # add edge relation + for u, v, _ in self.data.edge: + uy0, ux0, uy1, ux1 = boxes[u] + vy0, vx0, vy1, vx1 = boxes[v] + uc = (uy0 + uy1) / 2, (ux0 + ux1) / 2 + vc = (vy0 + vy1) / 2, (vx0 + vx1) / 2 + + # surrounding/inside -> X four quadrants + if ux0 < vx0 and ux1 > vx1 and uy0 < vy0 and uy1 > vy1: + relation = 'surrounding' + elif ux0 >= vx0 and ux1 <= vx1 and uy0 >= vy0 and uy1 <= vy1: + relation = 'inside' + else: + relation = point_box_relation(uc, boxes[v]) + + triples.append([u, vocab['pred_name_to_idx'][relation], v]) + + triples = np.array(triples, dtype=int) + if tensor: triples = torch.tensor(triples).long() + return triples + + def vis_box(self): + h, w = 128, 128 + image = np.full((h, w, 4), 0, dtype=np.uint8) + + boxes = self.data.box[:, :4] // 2 + objs = self.data.box[:, -1] + + for i, obj in enumerate(objs): + if obj == 14: continue + color = colormap_255[obj] + box = boxes[i] + cv2.rectangle(image, (box[0], box[1]), (box[2], box[3]), (color[0], color[1], color[2], 255), 3) + + return image + + def get_test_data(self, tensor=True): + boundary = self.get_input_boundary(tensor=tensor) + inside_box = self.get_inside_box(tensor=tensor) + rooms = self.get_rooms(tensor=tensor) + attrs = self.get_attributes(tensor=tensor) + triples = self.get_triples(random=False, tensor=tensor) + return boundary, inside_box, rooms, attrs, triples + + def adapt_graph(self, fp_graph): + fp = FloorPlan(fp_graph.data, train=True, rot=self.rot) + g_external = fp.data.boundary[:, :2] + gx0, gx1 = np.min(g_external[:, 0]), np.max(g_external[:, 0]) + gy0, gy1 = np.min(g_external[:, 1]), np.max(g_external[:, 1]) + gw, gh = gx1 - gx0, gy1 - gy0 + + fp.data.boundary = self.data.boundary + b_external = self.data.boundary[:, :2] + bx0, bx1 = np.min(b_external[:, 0]), np.max(b_external[:, 0]) + by0, by1 = np.min(b_external[:, 1]), np.max(b_external[:, 1]) + bh, bw = by1 - by0, bx1 - bx0 + box_adapter = lambda box: (((box - np.array([gx0, gy0, gx0, gy0])) * np.array([bw, bh, bw, bh])) / np.array( + [gw, gh, gw, gh]) + np.array([bx0, by0, bx0, by0])).astype(int) + + fp.data.box[:, :4] = np.apply_along_axis(box_adapter, 1, fp.data.box[:, :4]) + return fp + + def adjust_graph(self): + external = self.data.boundary[:, :2] + bx0, bx1 = np.min(external[:, 0]), np.max(external[:, 0]) + by0, by1 = np.min(external[:, 1]), np.max(external[:, 1]) + hw_b = np.array([by1 - by0, bx1 - bx0]) + step = hw_b / 10 + + pts = np.concatenate([external, external[:1]]) + mask = np.zeros((256, 256), dtype=np.uint8) + cv2.fillPoly(mask, pts.reshape(1, -1, 2), 255) + # plt.imshow(mask) + # plt.show() + mask = cv2.resize(mask[by0:by1 + 1, bx0:bx1 + 1], (10, 10)) + # plt.imshow(mask) + # plt.show() + mask[mask > 0] = 255 + + outside_rooms = [] + for i in range(len(self.data.box)): + box = self.data.box[i][:4][[1, 0, 3, 2]] + center = (box[:2] + box[2:]) / 2 + center55 = ((center - np.array([by0, bx0])) * 10 / hw_b).astype(int) + + if not mask[center55[0], center55[1]]: + outside_rooms.append([i, center55]) + + candicate_coords55 = {} + for i, coords55 in outside_rooms: + row, col = coords55 + # left/right/up/down + candicate_coords55[i] = np.array([ + next((col-c for c in range(col,-1,-1) if mask[row,c]==255),255), + next((c-col for c in range(col+1,5) if mask[row,c]==255),255), + next((row-r for r in range(row,-1,-1) if mask[r,col]==255),255), + next((r-row for r in range(row+1,5) if mask[r,col]==255),255)]) + + signs = np.array([ + [0, -1, 0, -1], + [0, 1, 0, 1], + [-1, 0, -1, 0], + [1, 0, 1, 0] + ]) + + for i, coords55 in outside_rooms: + deltas = candicate_coords55[i] + idx = np.argmin(deltas) + self.data.box[i, :4] += (signs[idx] * deltas[idx] * np.tile(step, 2)).astype(int)[[1, 0, 3, 2]] + + +if __name__ == "__main__": + pass diff --git a/model/graph.py b/model/graph.py new file mode 100644 index 0000000000000000000000000000000000000000..83f48a48ebe50d04de6e12745bc334428f048240 --- /dev/null +++ b/model/graph.py @@ -0,0 +1,147 @@ +#!/usr/bin/python +# +# Copyright 2018 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import torch +import torch.nn as nn + +from model.layers import build_mlp + +""" +PyTorch modules for dealing with graphs. +""" + + +def _init_weights(module): + if hasattr(module, 'weight'): + if isinstance(module, nn.Linear): + nn.init.kaiming_normal_(module.weight) + + +class GraphTripleConv(nn.Module): + """ + A single layer of scene graph convolution. + """ + + def __init__(self, input_dim, attributes_dim=0, output_dim=None, hidden_dim=512, + pooling='avg', mlp_normalization='none'): + super(GraphTripleConv, self).__init__() + if output_dim is None: + output_dim = input_dim + self.input_dim = input_dim + self.output_dim = output_dim + self.hidden_dim = hidden_dim + + assert pooling in ['sum', 'avg'], 'Invalid pooling "%s"' % pooling + self.pooling = pooling + net1_layers = [3 * input_dim + 2 * attributes_dim, hidden_dim, 2 * hidden_dim + output_dim] + net1_layers = [l for l in net1_layers if l is not None] + self.net1 = build_mlp(net1_layers, batch_norm=mlp_normalization) + self.net1.apply(_init_weights) + + net2_layers = [hidden_dim, hidden_dim, output_dim] + self.net2 = build_mlp(net2_layers, batch_norm=mlp_normalization) + self.net2.apply(_init_weights) + + def forward(self, obj_vecs, pred_vecs, edges): + """ + Inputs: + - obj_vecs: FloatTensor of shape (O, D) giving vectors for all objects + - pred_vecs: FloatTensor of shape (T, D) giving vectors for all predicates + - edges: LongTensor of shape (T, 2) where edges[k] = [i, j] indicates the + presence of a triple [obj_vecs[i], pred_vecs[k], obj_vecs[j]] + + Outputs: + - new_obj_vecs: FloatTensor of shape (O, D) giving new vectors for objects + - new_pred_vecs: FloatTensor of shape (T, D) giving new vectors for predicates + """ + dtype, device = obj_vecs.dtype, obj_vecs.device + O, T = obj_vecs.size(0), pred_vecs.size(0) + Din, H, Dout = self.input_dim, self.hidden_dim, self.output_dim + + # Break apart indices for subjects and objects; these have shape (T,) + s_idx = edges[:, 0].contiguous() + o_idx = edges[:, 1].contiguous() + + # Get current vectors for subjects and objects; these have shape (T, Din) + cur_s_vecs = obj_vecs[s_idx] + cur_o_vecs = obj_vecs[o_idx] + + # Get current vectors for triples; shape is (T, 3 * Din) + # Pass through net1 to get new triple vecs; shape is (T, 2 * H + Dout) + cur_t_vecs = torch.cat([cur_s_vecs, pred_vecs, cur_o_vecs], dim=1) + new_t_vecs = self.net1(cur_t_vecs) + + # Break apart into new s, p, and o vecs; s and o vecs have shape (T, H) and + # p vecs have shape (T, Dout) + new_s_vecs = new_t_vecs[:, :H] + new_p_vecs = new_t_vecs[:, H:(H + Dout)] + new_o_vecs = new_t_vecs[:, (H + Dout):(2 * H + Dout)] + + # Allocate space for pooled object vectors of shape (O, H) + pooled_obj_vecs = torch.zeros(O, H, dtype=dtype, device=device) + + # Use scatter_add to sum vectors for objects that appear in multiple triples; + # we first need to expand the indices to have shape (T, D) + s_idx_exp = s_idx.view(-1, 1).expand_as(new_s_vecs) + o_idx_exp = o_idx.view(-1, 1).expand_as(new_o_vecs) + pooled_obj_vecs = pooled_obj_vecs.scatter_add(0, s_idx_exp, new_s_vecs) + pooled_obj_vecs = pooled_obj_vecs.scatter_add(0, o_idx_exp, new_o_vecs) + + if self.pooling == 'avg': + # Figure out how many times each object has appeared, again using + # some scatter_add trickery. + obj_counts = torch.zeros(O, dtype=dtype, device=device) + ones = torch.ones(T, dtype=dtype, device=device) + obj_counts = obj_counts.scatter_add(0, s_idx, ones) + obj_counts = obj_counts.scatter_add(0, o_idx, ones) + + # Divide the new object vectors by the number of times they + # appeared, but first clamp at 1 to avoid dividing by zero; + # objects that appear in no triples will have output vector 0 + # so this will not affect them. + obj_counts = obj_counts.clamp(min=1) + pooled_obj_vecs = pooled_obj_vecs / obj_counts.view(-1, 1) + + # Send pooled object vectors through net2 to get output object vectors, + # of shape (O, Dout) + new_obj_vecs = self.net2(pooled_obj_vecs) + + return new_obj_vecs, new_p_vecs + + +class GraphTripleConvNet(nn.Module): + """ A sequence of scene graph convolution layers """ + + def __init__(self, input_dim, num_layers=5, hidden_dim=512, pooling='avg', + mlp_normalization='none'): + super(GraphTripleConvNet, self).__init__() + + self.num_layers = num_layers + self.gconvs = nn.ModuleList() + gconv_kwargs = { + 'input_dim': input_dim, + 'hidden_dim': hidden_dim, + 'pooling': pooling, + 'mlp_normalization': mlp_normalization, + } + for _ in range(self.num_layers): + self.gconvs.append(GraphTripleConv(**gconv_kwargs)) + + def forward(self, obj_vecs, pred_vecs, edges): + for i in range(self.num_layers): + gconv = self.gconvs[i] + obj_vecs, pred_vecs = gconv(obj_vecs, pred_vecs, edges) + return obj_vecs, pred_vecs diff --git a/model/layers.py b/model/layers.py new file mode 100644 index 0000000000000000000000000000000000000000..1eb069cd7c847e345136828e7b9e6a85e377210e --- /dev/null +++ b/model/layers.py @@ -0,0 +1,335 @@ +#!/usr/bin/python +# +# Copyright 2018 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import functools + +import torch +import torch.nn as nn +from torch.nn.functional import interpolate + +class PPM(nn.Module): + def __init__(self, in_dim, reduction_dim, bins, BatchNorm): + super(PPM, self).__init__() + self.features = [] + for bin in bins: + self.features.append(nn.Sequential( + nn.AdaptiveAvgPool2d(bin), + nn.Conv2d(in_dim, reduction_dim, kernel_size=1, bias=False), + BatchNorm(reduction_dim), + #nn.ReLU(inplace=True) + nn.LeakyReLU(inplace=True) + )) + self.features = nn.ModuleList(self.features) + + def forward(self, x): + x_size = x.size() + out = [x] + for f in self.features: + out.append(interpolate(f(x), x_size[2:], mode='bilinear', align_corners=True)) + return torch.cat(out, 1) + +def get_normalization_2d(channels, normalization): + if normalization == 'instance': + return nn.InstanceNorm2d(channels) + elif normalization == 'batch': + return nn.BatchNorm2d(channels) + elif normalization == 'none': + return None + else: + raise ValueError('Unrecognized normalization type "%s"' % normalization) + + +def get_activation(name): + kwargs = {} + if name.lower().startswith('leakyrelu'): + if '-' in name: + slope = float(name.split('-')[1]) + kwargs = {'negative_slope': slope} + name = 'leakyrelu' + activations = { + 'relu': nn.ReLU, + 'leakyrelu': nn.LeakyReLU, + } + if name.lower() not in activations: + raise ValueError('Invalid activation "%s"' % name) + return activations[name.lower()](**kwargs) + + +def _init_conv(layer, method): + if not isinstance(layer, nn.Conv2d): + return + if method == 'default': + return + elif method == 'kaiming-normal': + nn.init.kaiming_normal(layer.weight) + elif method == 'kaiming-uniform': + nn.init.kaiming_uniform(layer.weight) + + +class Flatten(nn.Module): + def forward(self, x): + return x.view(x.size(0), -1) + + def __repr__(self): + return 'Flatten()' + + +class Unflatten(nn.Module): + def __init__(self, size): + super(Unflatten, self).__init__() + self.size = size + + def forward(self, x): + return x.view(*self.size) + + def __repr__(self): + size_str = ', '.join('%d' % d for d in self.size) + return 'Unflatten(%s)' % size_str + + +class GlobalAvgPool(nn.Module): + def forward(self, x): + N, C = x.size(0), x.size(1) + return x.view(N, C, -1).mean(dim=2) + + +class ResidualBlock(nn.Module): + def __init__(self, channels, normalization='batch', activation='relu', + padding='same', kernel_size=3, init='default'): + super(ResidualBlock, self).__init__() + + K = kernel_size + P = _get_padding(K, padding) + C = channels + self.padding = P + layers = [ + get_normalization_2d(C, normalization), + get_activation(activation), + nn.Conv2d(C, C, kernel_size=K, padding=P), + get_normalization_2d(C, normalization), + get_activation(activation), + nn.Conv2d(C, C, kernel_size=K, padding=P), + ] + layers = [layer for layer in layers if layer is not None] + for layer in layers: + _init_conv(layer, method=init) + self.net = nn.Sequential(*layers) + + def forward(self, x): + P = self.padding + shortcut = x + if P == 0: + shortcut = x[:, :, P:-P, P:-P] + y = self.net(x) + return shortcut + self.net(x) + + +def _get_padding(K, mode): + """ Helper method to compute padding size """ + if mode == 'valid': + return 0 + elif mode == 'same': + assert K % 2 == 1, 'Invalid kernel size %d for "same" padding' % K + return (K - 1) // 2 + + +def build_cnn(arch, normalization='batch', activation='leakyrelu', padding='same', + pooling='max', init='default'): + """ + Build a CNN from an architecture string, which is a list of layer + specification strings. The overall architecture can be given as a list or as + a comma-separated string. + + All convolutions *except for the first* are preceeded by normalization and + nonlinearity. + + All other layers support the following: + - IX: Indicates that the number of input channels to the network is X. + Can only be used at the first layer; if not present then we assume + 3 input channels. + - CK-X: KxK convolution with X output channels + - CK-X-S: KxK convolution with X output channels and stride S + - R: Residual block keeping the same number of channels + - UX: Nearest-neighbor upsampling with factor X + - PX: Spatial pooling with factor X + - FC-X-Y: Flatten followed by fully-connected layer + + Returns a tuple of: + - cnn: An nn.Sequential + - channels: Number of output channels + """ + if isinstance(arch, str): + arch = arch.split(',') + cur_C = 3 + if len(arch) > 0 and arch[0][0] == 'I': + cur_C = int(arch[0][1:]) + arch = arch[1:] + + first_conv = True + flat = False + layers = [] + for i, s in enumerate(arch): + if s[0] == 'C': + if not first_conv: + layers.append(get_normalization_2d(cur_C, normalization)) + layers.append(get_activation(activation)) + first_conv = False + vals = [int(i) for i in s[1:].split('-')] + if len(vals) == 2: + K, next_C = vals + stride = 1 + elif len(vals) == 3: + K, next_C, stride = vals + # K, next_C = (int(i) for i in s[1:].split('-')) + P = _get_padding(K, padding) + conv = nn.Conv2d(cur_C, next_C, kernel_size=K, padding=P, stride=stride) + layers.append(conv) + _init_conv(layers[-1], init) + cur_C = next_C + elif s[0] == 'R': + norm = 'none' if first_conv else normalization + res = ResidualBlock(cur_C, normalization=norm, activation=activation, + padding=padding, init=init) + layers.append(res) + first_conv = False + elif s[0] == 'U': + factor = int(s[1:]) + layers.append(Interpolate(scale_factor=factor, mode='nearest')) + elif s[0] == 'P': + factor = int(s[1:]) + if pooling == 'max': + pool = nn.MaxPool2d(kernel_size=factor, stride=factor) + elif pooling == 'avg': + pool = nn.AvgPool2d(kernel_size=factor, stride=factor) + layers.append(pool) + elif s[:2] == 'FC': + _, Din, Dout = s.split('-') + Din, Dout = int(Din), int(Dout) + if not flat: + layers.append(Flatten()) + flat = True + layers.append(nn.Linear(Din, Dout)) + if i + 1 < len(arch): + layers.append(get_activation(activation)) + cur_C = Dout + else: + raise ValueError('Invalid layer "%s"' % s) + layers = [layer for layer in layers if layer is not None] + # for layer in layers: + # print(layer) + return nn.Sequential(*layers), cur_C + + +def build_mlp(dim_list, activation='leakyrelu', batch_norm='none', + dropout=0, final_nonlinearity=True): + layers = [] + for i in range(len(dim_list) - 1): + dim_in, dim_out = dim_list[i], dim_list[i + 1] + layers.append(nn.Linear(dim_in, dim_out)) + final_layer = (i == len(dim_list) - 2) + if not final_layer or final_nonlinearity: + if batch_norm == 'batch': + layers.append(nn.BatchNorm1d(dim_out)) + if activation == 'relu': + layers.append(nn.ReLU()) + elif activation == 'leakyrelu': + layers.append(nn.LeakyReLU()) + if dropout > 0: + layers.append(nn.Dropout(p=dropout)) + return nn.Sequential(*layers) + + +class ResnetBlock(nn.Module): + def __init__(self, dim, padding_type, norm_layer, activation=nn.ReLU(True), use_dropout=False): + super(ResnetBlock, self).__init__() + self.conv_block = self.build_conv_block(dim, padding_type, norm_layer, activation, use_dropout) + + def build_conv_block(self, dim, padding_type, norm_layer, activation, use_dropout): + conv_block = [] + p = 0 + if padding_type == 'reflect': + conv_block += [nn.ReflectionPad2d(1)] + elif padding_type == 'replicate': + conv_block += [nn.ReplicationPad2d(1)] + elif padding_type == 'zero': + p = 1 + else: + raise NotImplementedError('padding [%s] is not implemented' % padding_type) + + conv_block += [nn.Conv2d(dim, dim, kernel_size=3, padding=p), + norm_layer(dim), + activation] + if use_dropout: + conv_block += [nn.Dropout(0.5)] + + p = 0 + if padding_type == 'reflect': + conv_block += [nn.ReflectionPad2d(1)] + elif padding_type == 'replicate': + conv_block += [nn.ReplicationPad2d(1)] + elif padding_type == 'zero': + p = 1 + else: + raise NotImplementedError('padding [%s] is not implemented' % padding_type) + conv_block += [nn.Conv2d(dim, dim, kernel_size=3, padding=p), + norm_layer(dim)] + + return nn.Sequential(*conv_block) + + def forward(self, x): + out = x + self.conv_block(x) + return out + + +class ConditionalBatchNorm2d(nn.Module): + def __init__(self, num_features, num_classes): + super(ConditionalBatchNorm2d).__init__() + self.num_features = num_features + self.bn = nn.BatchNorm2d(num_features, affine=False) + self.embed = nn.Embedding(num_classes, num_features * 2) + self.embed.weight.data[:, :num_features].normal_(1, 0.02) # Initialise scale at N(1, 0.02) + self.embed.weight.data[:, num_features:].zero_() # Initialise bias at 0 + + def forward(self, x, y): + out = self.bn(x) + gamma, beta = self.embed(y).chunk(2, 1) + out = gamma.view(-1, self.num_features, 1, 1) * out + beta.view(-1, self.num_features, 1, 1) + return out + + +def get_norm_layer(norm_type='instance'): + if norm_type == 'batch': + norm_layer = functools.partial(nn.BatchNorm2d, affine=True) + elif norm_type == 'instance': + norm_layer = functools.partial(nn.InstanceNorm2d, affine=False) + elif norm_type == 'conditional': + norm_layer = functools.partial(ConditionalBatchNorm2d) + else: + raise NotImplementedError('normalization layer [%s] is not found' % norm_type) + return norm_layer + + +class Interpolate(nn.Module): + def __init__(self, size=None, scale_factor=None, mode='nearest', align_corners=None): + super(Interpolate, self).__init__() + self.size = size + self.scale_factor = scale_factor + self.mode = mode + self.align_corners = align_corners + + def forward(self, x): + return interpolate(x, size=self.size, scale_factor=self.scale_factor, mode=self.mode, + align_corners=self.align_corners) diff --git a/model/layout.py b/model/layout.py new file mode 100644 index 0000000000000000000000000000000000000000..1e52414b588aa715f6e3e42a51058af6895e433a --- /dev/null +++ b/model/layout.py @@ -0,0 +1,314 @@ +#!/usr/bin/python +# +# Copyright 2018 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import torch +import torch.nn as nn +import torch.nn.functional as F + +import model.box_utils as box_utils + + +""" +Functions for computing image layouts from object vectors, bounding boxes, +and segmentation masks. These are used to compute course scene layouts which +are then fed as input to the cascaded refinement network. +""" + + +def boxes_to_layout(vecs, boxes, obj_to_img, H, W=None, pooling='sum'): + """ + Inputs: + - vecs: Tensor of shape (O, D) giving vectors + - boxes: Tensor of shape (O, 4) giving bounding boxes in the format + [x0, y0, x1, y1] in the [0, 1] coordinate space + - obj_to_img: LongTensor of shape (O,) mapping each element of vecs to + an image, where each element is in the range [0, N). If obj_to_img[i] = j + then vecs[i] belongs to image j. + - H, W: Size of the output + + Returns: + - out: Tensor of shape (N, D, H, W) + """ + O, D = vecs.size() + if W is None: + W = H + + grid = _boxes_to_grid(boxes, H, W) + + # If we don't add extra spatial dimensions here then out-of-bounds + # elements won't be automatically set to 0 + img_in = vecs.view(O, D, 1, 1).expand(O, D, 8, 8) + sampled = F.grid_sample(img_in, grid) # (O, D, H, W) + + # Explicitly masking makes everything quite a bit slower. + # If we rely on implicit masking the interpolated boxes end up + # blurred around the edges, but it should be fine. + # mask = ((X < 0) + (X > 1) + (Y < 0) + (Y > 1)).clamp(max=1) + # sampled[mask[:, None]] = 0 + + out = _pool_samples(sampled, obj_to_img, pooling=pooling) + + return out + + +def masks_to_layout(vecs, boxes, masks, obj_to_img, H, W=None, pooling='sum'): + """ + Inputs: + - vecs: Tensor of shape (O, D) giving vectors + - boxes: Tensor of shape (O, 4) giving bounding boxes in the format + [x0, y0, x1, y1] in the [0, 1] coordinate space + - masks: Tensor of shape (O, M, M) giving binary masks for each object + - obj_to_img: LongTensor of shape (O,) mapping objects to images + - H, W: Size of the output image. + + Returns: + - out: Tensor of shape (N, D, H, W) + """ + O, D = vecs.size() + M = masks.size(1) + assert masks.size() == (O, M, M) + if W is None: + W = H + grid = _boxes_to_grid(boxes, H, W) + img_in = vecs.view(O, D, 1, 1) * masks.float().view(O, 1, M, M) + sampled = F.grid_sample(img_in, grid) + out = _pool_samples(sampled, obj_to_img, pooling=pooling) + return out + + +def _boxes_to_grid(boxes, H, W): + """ + Input: + - boxes: FloatTensor of shape (O, 4) giving boxes in the [x0, y0, x1, y1] + format in the [0, 1] coordinate space + - H, W: Scalars giving size of output + + Returns: + - grid: FloatTensor of shape (O, H, W, 2) suitable for passing to grid_sample + """ + O = boxes.size(0) + boxes = box_utils.centers_to_extents(boxes) + boxes = boxes.view(O, 4, 1, 1) + + # w,h = boxes[:, 2], boxes[:, 3] + # # All these are (O, 1, 1) + # x0, y0 = boxes[:, 0]-w/2, boxes[:, 1]-h/2 + # x1, y1 = boxes[:, 0]+w/2, boxes[:, 1]+h/2 + + x0, y0 = boxes[:, 0], boxes[:, 1] + ww, hh = boxes[:, 2] - x0, boxes[:, 3] - y0 + # ww = x1 - x0 + # hh = y1 - y0 + + X = torch.linspace(0, 1, steps=W).view(1, 1, W).to(boxes) + Y = torch.linspace(0, 1, steps=H).view(1, H, 1).to(boxes) + X = (X - x0) / ww # (O, 1, W) + Y = (Y - y0) / hh # (O, H, 1) + + # Stack does not broadcast its arguments so we need to expand explicitly + X = X.expand(O, H, W) + Y = Y.expand(O, H, W) + grid = torch.stack([X, Y], dim=3) # (O, H, W, 2) + + # Right now grid is in [0, 1] space; transform to [-1, 1] + grid = grid.mul(2).sub(1) + + return grid + + +def _pool_samples(samples, obj_to_img, pooling='sum'): + """ + Input: + - samples: FloatTensor of shape (O, D, H, W) + - obj_to_img: LongTensor of shape (O,) with each element in the range + [0, N) mapping elements of samples to output images + + Output: + - pooled: FloatTensor of shape (N, D, H, W) + """ + dtype, device = samples.dtype, samples.device + O, D, H, W = samples.size() + N = obj_to_img.data.max().item() + 1 + + # Use scatter_add to sum the sampled outputs for each image + out = torch.zeros(N, D, H, W, dtype=dtype, device=device) + idx = obj_to_img.view(O, 1, 1, 1).expand(O, D, H, W) + #out = out.scatter_add(0, idx, samples) + + if pooling == 'avg': + # Divide each output mask by the number of objects; use scatter_add again + # to count the number of objects per image. + out = out.scatter_add(0, idx, samples) + ones = torch.ones(O, dtype=dtype, device=device) + obj_counts = torch.zeros(N, dtype=dtype, device=device) + obj_counts = obj_counts.scatter_add(0, obj_to_img, ones) + obj_counts = obj_counts.clamp(min=1) + out = out / obj_counts.view(N, 1, 1, 1) + elif pooling == 'max': + all_out = [] + obj_to_img_list = [i.item() for i in list(obj_to_img)] + for i in range(N): + start = obj_to_img_list.index(i) + end = len(obj_to_img_list) - obj_to_img_list[::-1].index(i) + all_out.append(torch.max(samples[start:end, :, :, :], dim=0)[0]) + out = torch.stack(all_out) + elif pooling == 'sum': + out = out.scatter_add(0, idx, samples) + #raise ValueError('Invalid pooling "%s"' % pooling) + + return out + +def masks_to_seg(boxes, masks, objs, obj_to_img, H, W=None, num_classes=15): + """ + Inputs: + - vecs: Tensor of shape (O, D) giving vectors + - boxes: Tensor of shape (O, 4) giving bounding boxes in the format + [x0, y0, x1, y1] in the [0, 1] coordinate space + - obj_to_img: LongTensor of shape (O,) mapping each element of vecs to + an image, where each element is in the range [0, N). If obj_to_img[i] = j + then vecs[i] belongs to image j. + - H, W: Size of the output + + Returns: + - out: Tensor of shape (N, D, H, W) + """ + dtype, device = boxes.dtype, boxes.device + O, D = boxes.size() + M = masks.size(1) + assert masks.size() == (O, M, M) + if W is None: + W = H + N = obj_to_img.data.max().item() + 1 + grid = _boxes_to_grid(boxes, H, W) + mask_sampled = F.grid_sample(masks.float().view(O, 1, M, M), grid) + seg = torch.zeros((N,num_classes,H,W)).to(device) + # obj_to_img_list = [i.item() for i in list(obj_to_img)] + for i in range(N): + obj_to_i = (obj_to_img==i).nonzero().view(-1) + # start = obj_to_img_list.index(i) + # end = len(obj_to_img_list) - obj_to_img_list[::-1].index(i) + # for j in range(start,end): + for j in obj_to_i: + obj = objs[j] + seg[i,obj]=seg[i,obj]+mask_sampled[j] + return seg + +def boxes_to_seg(boxes, objs, obj_to_img, H, W=None,num_classes=15): + """ + Inputs: + - vecs: Tensor of shape (O, D) giving vectors + - boxes: Tensor of shape (O, 4) giving bounding boxes in the format + [x0, y0, x1, y1] in the [0, 1] coordinate space + - obj_to_img: LongTensor of shape (O,) mapping each element of vecs to + an image, where each element is in the range [0, N). If obj_to_img[i] = j + then vecs[i] belongs to image j. + - H, W: Size of the output + + Returns: + - out: Tensor of shape (N, D, H, W) + """ + dtype, device = boxes.dtype, boxes.device + O, D = boxes.size() + if W is None: + W = H + N = obj_to_img.data.max().item() + 1 + + grid = _boxes_to_grid(boxes, H, W) + mask_sampled = F.grid_sample(torch.ones(O,1,8,8).to(boxes), grid) + + seg = torch.zeros((N,num_classes,H,W)).to(device) + obj_to_img_list = [i.item() for i in list(obj_to_img)] + for i in range(N): + start = obj_to_img_list.index(i) + end = len(obj_to_img_list) - obj_to_img_list[::-1].index(i) + for j in range(start,end): + #obj_to_i = (obj_to_img==i).nonzero().view(-1) + #for j in obj_to_i: + obj = objs[j] + seg[i,obj]=seg[i,obj]+mask_sampled[j] + return seg + +if __name__ == '__main__': + vecs = torch.FloatTensor([ + [1, 0, 0], [0, 1, 0], [0, 0, 1], + [1, 0, 0], [0, 1, 0], [0, 0, 1], + ]) + boxes = torch.FloatTensor([ + [0.25, 0.125, 0.5, 0.875], + [0, 0, 1, 0.25], + [0.6125, 0, 0.875, 1], + [0, 0.8, 1, 1.0], + [0.25, 0.125, 0.5, 0.875], + [0.6125, 0, 0.875, 1], + ]) + obj_to_img = torch.LongTensor([0, 0, 0, 1, 1, 1]) + # vecs = torch.FloatTensor([[[1]]]) + # boxes = torch.FloatTensor([[[0.25, 0.25, 0.75, 0.75]]]) + vecs, boxes = vecs.cuda(), boxes.cuda() + obj_to_img = obj_to_img.cuda() + out = boxes_to_layout(vecs, boxes, obj_to_img, 256, pooling='sum') + + from torchvision.utils import save_image + save_image(out.data, 'out.png') + + + masks = torch.FloatTensor([ + [ + [0, 0, 1, 0, 0], + [0, 1, 1, 1, 0], + [1, 1, 1, 1, 1], + [0, 1, 1, 1, 0], + [0, 0, 1, 0, 0], + ], + [ + [0, 0, 1, 0, 0], + [0, 1, 0, 1, 0], + [1, 0, 0, 0, 1], + [0, 1, 0, 1, 0], + [0, 0, 1, 0, 0], + ], + [ + [0, 0, 1, 0, 0], + [0, 1, 1, 1, 0], + [1, 1, 1, 1, 1], + [0, 1, 1, 1, 0], + [0, 0, 1, 0, 0], + ], + [ + [0, 0, 1, 0, 0], + [0, 1, 1, 1, 0], + [1, 1, 1, 1, 1], + [0, 1, 1, 1, 0], + [0, 0, 1, 0, 0], + ], + [ + [0, 0, 1, 0, 0], + [0, 1, 1, 1, 0], + [1, 1, 1, 1, 1], + [0, 1, 1, 1, 0], + [0, 0, 1, 0, 0], + ], + [ + [0, 0, 1, 0, 0], + [0, 1, 1, 1, 0], + [1, 1, 1, 1, 1], + [0, 1, 1, 1, 0], + [0, 0, 1, 0, 0], + ] + ]) + masks = masks.cuda() + out = masks_to_layout(vecs, boxes, masks, obj_to_img, 256) + save_image(out.data, 'out_masks.png') diff --git a/model/model.pth b/model/model.pth new file mode 100644 index 0000000000000000000000000000000000000000..aa8bed758b1541b1ef8335b829a54233c5ae5457 --- /dev/null +++ b/model/model.pth @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:08046c64b993d1c3382f5dbb37755095c372fff540f5c7502262b1abf7584140 +size 30613537 diff --git a/model/model.py b/model/model.py new file mode 100644 index 0000000000000000000000000000000000000000..4d861944a43d3dcc0bd91c49d7fc951482833e01 --- /dev/null +++ b/model/model.py @@ -0,0 +1,217 @@ +#!/usr/bin/python +# +# Copyright 2018 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licens8.0es/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import math +import torch +import torch.nn as nn +import torch.nn.functional as F +from torchvision.ops import RoIAlign + +import model.box_utils as box_utils +from model.graph import GraphTripleConv, GraphTripleConvNet +from model.layout import boxes_to_layout, masks_to_layout, boxes_to_seg, masks_to_seg +from model.layers import build_mlp,build_cnn +from model.utils import vocab + +class Model(nn.Module): + def __init__(self, + embedding_dim=128, + image_size=(128,128), + input_dim=3, + attribute_dim=35, + # graph_net + gconv_dim=128, + gconv_hidden_dim=512, + gconv_num_layers=5, + # inside_cnn + inside_cnn_arch="C3-32-2,C3-64-2,C3-128-2,C3-256-2", + # refinement_net + refinement_dims=(1024, 512, 256, 128, 64), + # box_refine + box_refine_arch = "I15,C3-64-2,C3-128-2,C3-256-2", + roi_output_size = (8,8), + roi_spatial_scale = 1.0/8.0, + roi_cat_feature = True, + # others + mlp_activation='leakyrelu', + mlp_normalization='none', + cnn_activation='leakyrelu', + cnn_normalization='batch' + ): + super(Model, self).__init__() + ''' embedding ''' + self.vocab = vocab + num_objs = len(vocab['object_idx_to_name']) + num_preds = len(vocab['pred_idx_to_name']) + num_doors = len(vocab['door_idx_to_name']) + self.obj_embeddings = nn.Embedding(num_objs, embedding_dim) + self.pred_embeddings = nn.Embedding(num_preds, embedding_dim) + self.image_size = image_size + self.feature_dim = embedding_dim+attribute_dim + + ''' graph_net ''' + self.gconv = GraphTripleConv( + embedding_dim, + attributes_dim=attribute_dim, + output_dim=gconv_dim, + hidden_dim=gconv_hidden_dim, + mlp_normalization=mlp_normalization + ) + self.gconv_net = GraphTripleConvNet( + gconv_dim, + num_layers=gconv_num_layers-1, + mlp_normalization=mlp_normalization + ) + + ''' inside_cnn ''' + inside_cnn,inside_feat_dim = build_cnn( + f'I{input_dim},{inside_cnn_arch}', + padding='valid' + ) + self.inside_cnn = nn.Sequential( + inside_cnn, + nn.AdaptiveAvgPool2d(1) + ) + inside_output_dim = inside_feat_dim + obj_vecs_dim = gconv_dim+inside_output_dim + + ''' box_net ''' + box_net_dim = 4 + box_net_layers = [obj_vecs_dim, gconv_hidden_dim, box_net_dim] + self.box_net = build_mlp( + box_net_layers, + activation=mlp_activation, + batch_norm=mlp_normalization + ) + + ''' relationship_net ''' + rel_aux_layers = [obj_vecs_dim, gconv_hidden_dim, num_doors] + self.rel_aux_net = build_mlp( + rel_aux_layers, + activation=mlp_activation, + batch_norm=mlp_normalization + ) + + ''' refinement_net ''' + if refinement_dims!=None: + self.refinement_net,_ = build_cnn(f"I{obj_vecs_dim},C3-128,C3-64,C3-{num_objs}") + else: + self.refinement_net = None + + ''' roi ''' + self.box_refine_backbone = None + self.roi_cat_feature = roi_cat_feature + if box_refine_arch!=None: + box_refine_cnn,box_feat_dim = build_cnn( + box_refine_arch, + padding='valid' + ) + self.box_refine_backbone = box_refine_cnn + self.roi_align = RoIAlign(roi_output_size,roi_spatial_scale,-1) #(256,8,8) + self.down_sample = nn.AdaptiveAvgPool2d(1) + box_refine_layers = [obj_vecs_dim+256 if self.roi_cat_feature else 256, 512, 4] + self.box_reg =build_mlp( + box_refine_layers, + activation=mlp_activation, + batch_norm=mlp_normalization + ) + + def forward( + self, + objs, + triples, + boundary, + obj_to_img=None, + attributes=None, + boxes_gt=None, + generate=False, + refine=False, + relative=False, + inside_box=None + ): + """ + Required Inputs: + - objs: LongTensor of shape (O,) giving categories for all objects + - triples: LongTensor of shape (T, 3) where triples[t] = [s, p, o] + means that there is a triple (objs[s], p, objs[o]) + + Optional Inputs: + - obj_to_img: LongTensor of shape (O,) where obj_to_img[o] = i + means that objects[o] is an object in image i. If not given then + all objects are assumed to belong to the same image. + - boxes_gt: FloatTensor of shape (O, 4) giving boxes to use for computing + the spatial layout; if not given then use predicted boxes. + """ + # input size + O, T = objs.size(0), triples.size(0) + s, p, o = triples.chunk(3, dim=1) # All have shape (T, 1) + s, p, o = [x.squeeze(1) for x in [s, p, o]] # Now have shape (T,) + edges = torch.stack([s, o], dim=1) # Shape is (T, 2) + B = boundary.size(0) + H, W = self.image_size + + if obj_to_img is None: + obj_to_img = torch.zeros(O, dtype=objs.dtype, device=objs.device) + + ''' embedding ''' + obj_vecs = self.obj_embeddings(objs) + pred_vecs = self.pred_embeddings(p) + + ''' attribute ''' + if attributes is not None: + obj_vecs = torch.cat([obj_vecs,attributes],1) + obj_vecs_orig = obj_vecs + + ''' gconv ''' + obj_vecs, pred_vecs = self.gconv(obj_vecs, pred_vecs, edges) + obj_vecs, pred_vecs = self.gconv_net(obj_vecs, pred_vecs, edges) + + ''' inside ''' + inside_vecs = self.inside_cnn(boundary).view(B,-1) + obj_vecs = torch.cat([obj_vecs,inside_vecs[obj_to_img]],dim=1) + + ''' box ''' + boxes_pred = self.box_net(obj_vecs) + if relative: boxes_pred = box_utils.box_rel2abs(boxes_pred,inside_box,obj_to_img) + + ''' relation ''' + # unused, for door position predition + # rel_scores = self.rel_aux_net(obj_vecs) + + ''' generate ''' + gene_layout = None + boxes_refine = None + layout_boxes = boxes_pred if boxes_gt is None else boxes_gt + if generate: + layout_features = boxes_to_layout(obj_vecs,layout_boxes,obj_to_img,H,W) + gene_layout = self.refinement_net(layout_features) + + ''' box refine ''' + if refine: + gene_feat = self.box_refine_backbone(gene_layout) + rois = torch.cat([ + obj_to_img.float().view(-1,1), + box_utils.centers_to_extents(layout_boxes)*H + ],-1) + roi_feat = self.down_sample(self.roi_align(gene_feat,rois)).flatten(1) + roi_feat = torch.cat([ + roi_feat, + obj_vecs + ],-1) + boxes_refine = self.box_reg(roi_feat) + if relative: boxes_refine = box_utils.box_rel2abs(boxes_refine,inside_box,obj_to_img) + + return boxes_pred, gene_layout, boxes_refine diff --git a/model/test.py b/model/test.py new file mode 100644 index 0000000000000000000000000000000000000000..d4646c0b047719a543f7eec6b3635d934dc1d115 --- /dev/null +++ b/model/test.py @@ -0,0 +1,255 @@ +from model.floorplan import * +from model.box_utils import * +from model.model import Model +import os +from model.utils import * + +import Houseweb.views as vw + +import numpy as np +import time +import math +import matlab.engine + + +global adjust,indxlist +adjust=False + +def get_data(fp): + batch = list(fp.get_test_data()) + batch[0] = batch[0].unsqueeze(0).cuda() + batch[1] = batch[1].cuda() + batch[2] = batch[2].cuda() + batch[3] = batch[3].cuda() + batch[4] = batch[4].cuda() + return batch + +def test(model,fp): + with torch.no_grad(): + batch = get_data(fp) + boundary,inside_box,rooms,attrs,triples = batch + model_out = model( + rooms, + triples, + boundary, + obj_to_img = None, + attributes = attrs, + boxes_gt= None, + generate = True, + refine = True, + relative = True, + inside_box=inside_box + ) + boxes_pred, gene_layout, boxes_refine= model_out + boxes_pred = boxes_pred.detach() + boxes_pred = centers_to_extents(boxes_pred) + boxes_refine = boxes_refine.detach() + boxes_refine = centers_to_extents(boxes_refine) + gene_layout = gene_layout*boundary[:,:1] + gene_preds = torch.argmax(gene_layout.softmax(1).detach(),dim=1) + return boxes_pred.squeeze().cpu().numpy(),gene_preds.squeeze().cpu().double().numpy(),boxes_refine.squeeze().cpu().numpy() + +def load_model(): + + model = Model() + model.cuda(0) + model.load_state_dict( + torch.load('./model/model.pth', map_location={'cuda:0': 'cuda:0'})) + model.eval() + return model + +def get_userinfo(userRoomID,adptRoomID): + start = time.clock() + global model + test_index = vw.testNameList.index(userRoomID.split(".")[0]) + test_data = vw.test_data[test_index] + + # boundary + Boundary = test_data.boundary + boundary=[[float(x),float(y),float(z),float(k)] for x,y,z,k in list(Boundary)] + + test_fp =FloorPlan(test_data) + + train_index = vw.trainNameList.index(adptRoomID.split(".")[0]) + train_data = vw.train_data[train_index] + train_fp =FloorPlan(train_data,train=True) + fp_end = test_fp.adapt_graph(train_fp) + fp_end.adjust_graph() + return fp_end + + +def get_userinfo_adjust(userRoomID,adptRoomID,NewGraph): + global adjust,indxlist + test_index = vw.testNameList.index(userRoomID.split(".")[0]) + test_data = vw.test_data[test_index] + # boundary + Boundary = test_data.boundary + boundary=[[float(x),float(y),float(z),float(k)] for x,y,z,k in list(Boundary)] + + test_fp =FloorPlan(test_data) + + train_index = vw.trainNameList.index(adptRoomID.split(".")[0]) + train_data = vw.train_data[train_index] + train_fp =FloorPlan(train_data,train=True) + fp_end = test_fp.adapt_graph(train_fp) + fp_end.adjust_graph() + + + newNode = NewGraph[0] + newEdge = NewGraph[1] + oldNode = NewGraph[2] + + temp = [] + for newindx, newrmname, newx, newy,scalesize in newNode: + for type, oldrmname, oldx, oldy, oldindx in oldNode: + if (int(newindx) == oldindx): + tmp=int(newindx), (newx - oldx), ( newy- oldy),float(scalesize) + temp.append(tmp) + newbox=[] + print(adjust) + if adjust==True: + oldbox = [] + for i in range(len(vw.boxes_pred)): + indxtmp=[vw.boxes_pred[i][0],vw.boxes_pred[i][1],vw.boxes_pred[i][2],vw.boxes_pred[i][3],vw.boxes_pred[i][0]] + oldbox.append(indxtmp) + if adjust==False: + indxlist=[] + oldbox=fp_end.data.box.tolist() + for i in range(len(oldbox)): + indxlist.append([oldbox[i][4]]) + indxlist=np.array(indxlist) + adjust=True + oldbox=fp_end.data.box.tolist() + + # print("oldbox",oldbox) + # print(oldbox,"oldbox") + X=0 + Y=0 + for i in range(len(oldbox)): + X= X+(oldbox[i][2]-oldbox[i][0]) + Y= Y+(oldbox[i][3]-oldbox[i][1]) + x_ave=(X/len(oldbox))/2 + y_ave=(Y/len(oldbox))/2 + + index_mapping = {} + # The room that already exists + # Move: Just by the distance + for newindx, tempx, tempy,scalesize in temp: + index_mapping[newindx] = len(newbox) + tmpbox=[] + scalesize = int(scalesize) + if scalesize<1: + scale = math.sqrt(scalesize) + scalex = (oldbox[newindx][2] - oldbox[newindx][0]) * (1 - scale) / 2 + scaley = (oldbox[newindx][3] - oldbox[newindx][1]) * (1 - scale) / 2 + tmpbox = [(oldbox[newindx][0] + tempx) + scalex, (oldbox[newindx][1] + tempy)+scaley, + (oldbox[newindx][2] + tempx) - scalex, (oldbox[newindx][3] + tempy) - scaley, oldbox[newindx][4]] + if scalesize == 1: + tmpbox = [(oldbox[newindx][0] + tempx) , (oldbox[newindx][1] + tempy) ,(oldbox[newindx][2] + tempx), (oldbox[newindx][3] + tempy), oldbox[newindx][4]] + + if scalesize>1: + scale=math.sqrt(scalesize) + scalex = (oldbox[newindx][2] - oldbox[newindx][0]) * ( scale-1) / 2 + scaley = (oldbox[newindx][3] - oldbox[newindx][1]) * (scale-1) / 2 + tmpbox = [(oldbox[newindx][0] + tempx) - scalex, (oldbox[newindx][1] + tempy) - scaley, + (oldbox[newindx][2] + tempx) + scalex, (oldbox[newindx][3] + tempy) + scaley, oldbox[newindx][4]] + + + newbox.append(tmpbox) + + # The room just added + # Move: The room node with the average size of the existing room + for newindx, newrmname, newx, newy,scalesize in newNode: + if int(newindx)>(len(oldbox)-1): + scalesize=int(scalesize) + index_mapping[int(newindx)] = (len(newbox)) + tmpbox=[] + if scalesize < 1: + scale = math.sqrt(scalesize) + scalex = x_ave * (1 - scale) / 2 + scaley = y_ave* (1 - scale) / 2 + tmpbox = [(newx-x_ave) +scalex,(newy-y_ave) +scaley,(newx+x_ave)-scalex,(newy+y_ave)-scaley,vocab['object_name_to_idx'][newrmname]] + + if scalesize == 1: + tmpbox = [(newx - x_ave), (newy - y_ave), (newx + x_ave), (newy + y_ave),vocab['object_name_to_idx'][newrmname]] + if scalesize > 1: + scale = math.sqrt(scalesize) + scalex = x_ave * (scale - 1) / 2 + scaley = y_ave * (scale - 1) / 2 + tmpbox = [(newx-x_ave) - scalex, (newy-y_ave) - scaley,(newx+x_ave) + scalex, (newy+y_ave) + scaley,vocab['object_name_to_idx'][newrmname]] + print(scalesize) + newbox.append(tmpbox) + + fp_end.data.box=np.array(newbox) + adjust_Edge=[] + for u, v in newEdge: + tmp=[index_mapping[int(u)],index_mapping[int(v)], 0] + adjust_Edge.append(tmp) + fp_end.data.edge=np.array(adjust_Edge) + rNode = fp_end.get_rooms(tensor=False) + + rEdge = fp_end.get_triples(tensor=False)[:, [0, 2, 1]] + Edge = [[float(u), float(v), float(type2)] for u, v, type2 in rEdge] + + s=time.clock() + boxes_pred, gene_layout, boxes_refeine = test(vw.model, fp_end) + + e=time.clock() + print(' model test time: %s Seconds' % (e - s)) + + boxes_pred = boxes_pred * 255 + + fp_end.data.gene = gene_layout + rBox = boxes_pred[:] + Box = [[float(x), float(y), float(z), float(k)] for x, y, z, k in rBox] + + boundary_mat = matlab.double(boundary) + rNode_mat = matlab.double(rNode.tolist()) + print("rNode.tolist()",rNode.tolist()) + Edge_mat = matlab.double(Edge) + + Box_mat=matlab.double(Box) + + fp_end.data.boundary =np.array(boundary) + fp_end.data.rType =np.array(rNode).astype(int) + fp_end.data.refineBox=np.array(Box) + fp_end.data.rEdge=np.array(Edge) + gene_mat=matlab.double(np.array(fp_end.data.gene).tolist()) + startcom= time.clock() + box_refine = vw.engview.align_fp(boundary_mat, Box_mat, rNode_mat,Edge_mat,matlab.double(fp_end.data.gene.astype(float).copy().tolist()) ,18,False, nargout=3) + endcom = time.clock() + print(' matlab.compute time: %s Seconds' % (endcom - startcom)) + box_out=box_refine[0] + box_order=box_refine[1] + + rBoundary=box_refine[2] + fp_end.data.newBox = np.array(box_out) + fp_end.data.order = np.array(box_order) + fp_end.data.rBoundary = [np.array(rb) for rb in rBoundary] + return fp_end,box_out,box_order, gene_layout, boxes_refeine + + +def get_userinfo_net(userRoomID,adptRoomID): + global model + test_index = vw.testNameList.index(userRoomID.split(".")[0]) + test_data = vw.test_data[test_index] + + # boundary + Boundary = test_data.boundary + boundary = [[float(x), float(y), float(z), float(k)] for x, y, z, k in list(Boundary)] + test_fp = FloorPlan(test_data) + + train_index = vw.trainNameList.index(adptRoomID.split(".")[0]) + train_data = vw.train_data[train_index] + train_fp = FloorPlan(train_data, train=True) + fp_end = test_fp.adapt_graph(train_fp) + fp_end.adjust_graph() + boxes_pred, gene_layout, boxes_refeine = test(model, fp_end) + boxes_pred=boxes_pred*255 + for i in range(len(boxes_pred)): + for j in range(len(boxes_pred[i])): + boxes_pred[i][j]=float(boxes_pred[i][j]) + return fp_end,boxes_pred, gene_layout, boxes_refeine + +if __name__ == "__main__": + pass diff --git a/model/utils.py b/model/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..9256d96352c849a7cf8b1b20b9944b44854f0c8d --- /dev/null +++ b/model/utils.py @@ -0,0 +1,371 @@ +import numpy as np + +# index,name,type(private/public),floorTexture +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])] + +# color palette for nyu40 labels +def create_color_palette(): + return [ + (0, 0, 0), + (174, 199, 232), # wall + (152, 223, 138), # floor + (31, 119, 180), # cabinet + (255, 187, 120), # bed + (188, 189, 34), # chair + (140, 86, 75), # sofa + (255, 152, 150), # table + (214, 39, 40), # door + (197, 176, 213), # window + (148, 103, 189), # bookshelf + (196, 156, 148), # picture + (23, 190, 207), # counter + (178, 76, 76), + (247, 182, 210), # desk + (66, 188, 102), + (219, 219, 141), # curtain + (140, 57, 197), + (202, 185, 52), + (51, 176, 203), + (200, 54, 131), + (92, 193, 61), + (78, 71, 183), + (172, 114, 82), + (255, 127, 14), # refrigerator + (91, 163, 138), + (153, 98, 156), + (140, 153, 101), + (158, 218, 229), # shower curtain + (100, 125, 154), + (178, 127, 135), + (120, 185, 128), + (146, 111, 194), + (44, 160, 44), # toilet + (112, 128, 144), # sink + (96, 207, 209), + (227, 119, 194), # bathtub + (213, 92, 176), + (94, 106, 211), + (82, 84, 163), # otherfurn + (100, 85, 144) + ] +color_palette = create_color_palette()[1:] + +semantics_cmap = { + 'living room': '#e6194b',#[230,25,75] + 'kitchen': '#3cb44b',#[60,180,75] + 'bedroom': '#ffe119',#[255,225,25] + 'bathroom': '#0082c8',#[0,130,200] + 'balcony': '#f58230',#[245,130,48] + 'corridor': '#911eb4',#[145,30,180] + 'dining room': '#46f0f0',#[70,240,240] + 'study': '#f032e6',#[240,50,230] + 'studio': '#d2f53c',#[210,245,60] + 'store room': '#fabebe',#[250,190,190] + 'garden': '#008080',#[0,128,128] + 'laundry room': '#e6beff',#[230,190,255] + 'office': '#aa6e28',#[170,110,40] + 'basement': '#fffac8',#[255,250,200] + 'garage': '#800000',#[128,0,0] + 'undefined': '#aaffc3',#[170,255,195] + 'door': '#808000',#[128,128,0] + 'window': '#ffd7b4',#[255,215,180] + 'outwall': '#000000',#[0,0,0] +} + +colormap_255 = [ + [230, 25, 75],#LivingRoom + [ 60, 180, 75],#MasterRoom + [170, 255, 195],#Kitchen + [ 0, 130, 200],#Bathroom + [245, 130, 48],#DiningRoom + [145, 30, 180],#ChildRoom + [ 70, 240, 240],#StudyRoom + [240, 50, 230],#SecondRoom + [210, 245, 60],#GuestRoom + [250, 190, 190],#Balcony + [ 0, 128, 128],#Entrance + [230, 190, 255],#Storage + [170, 110, 40],#Wall-in + [255, 255, 255],#External + [128, 0, 0],#ExteriorWall + [255, 225, 25],#FrontDoor + [128, 128, 128],#InteriorWall + [255, 255, 255],#InteriorDoor + #[255, 215, 180], + [ 0, 0, 128], + [128, 128, 0], + [255, 255, 255], + [ 0, 0, 0] +] + +cmaps = { + 'nyu40': color_palette, + 'semantics': semantics_cmap, + '255': colormap_255 +} + +category = [category for category in room_label if category[1] not in set(['External', + 'ExteriorWall', 'FrontDoor', 'InteriorWall', 'InteriorDoor'])] + +num_category = len(category) + +pixel2length = 18/256 + +def label2name(label=0): + if label < 0 or label > 17: + raise Exception("Invalid label!", label) + else: + return room_label[label][1] + + +def label2index(label=0): + if label < 0 or label > 17: + raise Exception("Invalid label!", label) + else: + return label + + +def index2label(index=0): + if index < 0 or index > 17: + raise Exception("Invalid index!", index) + else: + return index + + +def compute_centroid(mask): + sum_h = 0 + sum_w = 0 + count = 0 + shape_array = mask.shape + for h in range(shape_array[0]): + for w in range(shape_array[1]): + if mask[h, w] != 0: + sum_h += h + sum_w += w + count += 1 + return (sum_h//count, sum_w//count) + + +def log(file, msg='', is_print=True): + if is_print: + print(msg) + file.write(msg + '\n') + file.flush() + + +def collide2d(bbox1, bbox2, th=0): + return not( + (bbox1[0]-th > bbox2[2]) or + (bbox1[2]+th < bbox2[0]) or + (bbox1[1]-th > bbox2[3]) or + (bbox1[3]+th < bbox2[1]) + ) +# +# def rot90_2D(pts,k=1,cnt=np.array([127.5,127.5])): +# ang = k*np.pi/2 +# R = np.array([[np.cos(ang),np.sin(ang)],[-np.sin(ang),np.cos(ang)]]) +# return np.dot(pts-cnt,R)+cnt +# def fliplr_2D(pts,size=255): +# return np.stack([pts[:,0],size-pts[:,1]],1) +# +# def align_image(image,rot_old,rot_new=0): +# k = np.ceil((rot_old-rot_new+2*np.pi)%(2*np.pi)/(np.pi/4))//2 +# return np.rot90(image,k) +# +# def align_box(box,rot_old,rot_new=0): +# k = np.ceil((rot_old-rot_new+2*np.pi)%(2*np.pi)/(np.pi/4))//2 +# box = rot90_2D(box.reshape(-1,2),k).reshape(-1,4) +# return np.concatenate([np.minimum(box[:,:2],box[:,2:]),np.maximum(box[:,:2],box[:,2:])],-1)#.round().astype(int) +# +# def fliplr_box(box,size=255): +# box=fliplr_2D(box.reshape(-1,2),size=size).reshape(-1,4) +# return np.concatenate([np.minimum(box[:,:2],box[:,2:]),np.maximum(box[:,:2],box[:,2:])],-1)#.round().astype(int) + + +def rot90_2D(pts, k=1, cnt=np.array([127.5, 127.5])): + ang = k * np.pi / 2 + R = np.array([[np.cos(ang), np.sin(ang)], [-np.sin(ang), np.cos(ang)]]) + return np.dot(pts - cnt, R) + cnt + + +def fliplr_2D(pts, size=255): + return np.stack([pts[:, 0], size - pts[:, 1]], 1) + + +def align_image(image, rot_old, rot_new=0): + k = np.ceil((rot_old - rot_new + 2 * np.pi) % (2 * np.pi) / (np.pi / 4)) // 2 + return np.rot90(image, k) + + +def align_box(box, rot_old, rot_new=0): + k = np.ceil((rot_old - rot_new + 2 * np.pi) % (2 * np.pi) / (np.pi / 4)) // 2 + box = rot90_2D(box.reshape(-1, 2), k).reshape(-1, 4) + return np.concatenate([np.minimum(box[:, :2], box[:, 2:]), np.maximum(box[:, :2], box[:, 2:]) + 1], + -1).round().astype(int) + + +def align_points(points, rot_old, rot_new=0): + k = np.ceil((rot_old - rot_new + 2 * np.pi) % (2 * np.pi) / (np.pi / 4)) // 2 + points = rot90_2D(points, k) + return points.round().astype(int) + +def graph2labels(graph): + edges = graph.edges + return sorted([ + tuple(sorted((room_label[graph.nodes[u]['category']][1], + room_label[graph.nodes[v]['category']][1]))) + for u,v in edges + ]) + +def graph2labels_withtype(graph): + edges = graph.edges(data=True) + return sorted([ + ('acc' if d['type'] else 'adj', + *sorted( + (room_label[graph.nodes[u]['category']][1], + room_label[graph.nodes[v]['category']][1])) + ) + for u,v,d in edges + ]) + +def graph2functions(graph): + edges = graph.edges + return sorted([ + tuple(sorted((graph.nodes[u]['function'], + graph.nodes[v]['function']))) + for u,v in edges + ]) + +def graph2functions_withtype(graph): + edges = graph.edges(data=True) + return sorted([ + ('acc' if d['type'] else 'adj', + *sorted( + (graph.nodes[u]['function'], + graph.nodes[v]['function'])) + ) + for u,v,d in edges + ]) + +def counter2labels(counter): + return sorted({ + room_label[key][1]:value + for key,value in counter.items() + }.items()) + +def counter2functions(counter): + counter_new = { + room_label[key][1]:value + for key,value in counter.items() + } + counter_new['Bedroom']=0 + for key in counter: + if room_label[key][3]=='Bedroom': + counter_new['Bedroom']+=counter_new.pop(room_label[key][1]) + return sorted(counter_new.items()) + +def point_box_relation(u,vbox): + uy,ux = u + vy0, vx0, vy1, vx1 = vbox + + if (ux2: + b=b[:,:2] + b = np.concatenate((b,b[:1])) + nPoint = len(b)-1 + lineVec = b[1:]-b[:-1] + lineLength = np.linalg.norm(lineVec,axis=1) + + perimeter = lineLength.sum() + lineVec = lineVec/perimeter + lineLength = lineLength/perimeter + + angles = np.zeros(nPoint) + for i in range(nPoint): + z = np.cross(lineVec[i],lineVec[(i+1)%nPoint]) + sign = np.sign(z) + angles[i] = np.arccos(np.dot(lineVec[i],lineVec[(i+1)%nPoint]))*sign + + x = np.zeros(nPoint+1) + y = np.zeros(nPoint+1) + s = 0 + for i in range(1,nPoint+1): + x[i] = lineLength[i-1]+x[i-1] + y[i-1] = angles[i-1]+s + s = y[i-1] + y[-1] = s + return x,y + +def sample_tf(x,y,ndim=1000): + ''' + input: tf.x,tf.y, ndim + return: n-dim tf values + ''' + t = np.linspace(0,1,ndim) + return np.piecewise(t,[t>=xx for xx in x],y) + +class DataRetriever(): + def __init__(self,tf_train,centroids,clusters): + ''' + tf_train: tf of training data + centroids: tf cluster centroids of training data + clusters: data index for each cluster of training data + ''' + self.tf_train = tf_train + self.centroids = centroids + self.clusters = clusters + + def retrieve_bf(self,datum,k=20): + # compute tf for the data boundary + x,y = compute_tf(datum.boundary) + y_sampled = sample_tf(x,y,1000) + dist = np.linalg.norm(y_sampled-self.tf_train,axis=1) + if k>np.log2(len(self.tf_train)): + index = np.argsort(dist)[:k] + else: + index = np.argpartition(dist,k)[:k] + index = index[np.argsort(dist[index])] + return index + + def retrieve_cluster(self,datum,k=20,multi_clusters=False): + ''' + datum: test data + k: retrieval num + return: index for training data + ''' + # compute tf for the data boundary + x,y = compute_tf(datum.boundary) + y_sampled = sample_tf(x,y,1000) + # compute distance to cluster centers + dist = np.linalg.norm(y_sampled-self.centroids,axis=1) + + if multi_clusters: + # more candicates + c = int(np.max(np.clip(np.log2(k),1,5))) + cluster_idx = np.argsort(dist)[:c] + cluster = np.unique(self.clusters[cluster_idx].reshape(-1)) + else: + # only candicates + cluster_idx = np.argmin(dist) + cluster = self.clusters[cluster_idx] + + # compute distance to cluster samples + dist = np.linalg.norm(y_sampled-self.tf_train[cluster],axis=1) + index = cluster[np.argsort(dist)[:k]] + return index + +def retrieval(test_data,k,multi_clusters): + + retriever = DataRetriever(vw.tf_train,vw.centroids,vw.clusters) + datum = test_data + # vis_boundary(datum.boundary) + + t1 = time.clock() + index = retriever.retrieve_cluster(datum,k,multi_clusters) + t2 = time.clock() + print('cluster',t2-t1) + data_retrieval = vw.train_data[index] + # data_retrieval= trainNameList[index] + # vis_boundary(data_retrieval[0].boundary) + + # t1 = time() + # index = retriever.retrieve_bf(datum,k=10) + # t2 = time() + # print('bf',t2-t1) + # data_retrieval = train_data[index] + return index + +def vis_boundary(b): + import cv2 + import matplotlib.pyplot as plt + img = np.ones((256,256,3)) + img = cv2.line(img,tuple(b[0,:2]),tuple(b[1,:2]),(1.,1.,0.),thickness=2) + for i in range(1,len(b)-1): + img = cv2.line(img,tuple(b[i,:2]),tuple(b[i+1,:2]),(0.,0.,0.),thickness=2) + img = cv2.line(img,tuple(b[0,:2]),tuple(b[-1,:2]),(0.,0.,0.),thickness=2) + plt.imshow(img) + plt.show() + +if __name__ == "__main__": + import scipy.io as sio + import pickle + from time import time + import cv2 + import matplotlib.pyplot as plt + + def vis_boundary(b): + img = np.ones((256,256,3)) + img = cv2.line(img,tuple(b[0,:2]),tuple(b[1,:2]),(1.,1.,0.),thickness=2) + for i in range(1,len(b)-1): + img = cv2.line(img,tuple(b[i,:2]),tuple(b[i+1,:2]),(0.,0.,0.),thickness=2) + img = cv2.line(img,tuple(b[0,:2]),tuple(b[-1,:2]),(0.,0.,0.),thickness=2) + plt.imshow(img) + plt.show() + + #data_train = sio.loadmat('data_train70.mat',squeeze_me=True,struct_as_record=False)['data'] + #data_test = #sio.loadmat('data_test15.mat',squeeze_me=True,struct_as_record=False)['data'] + t1 = time() + train_data = pickle.load(open('data_train_converted.pkl','rb'))['data'] + t2 = time() + print('load train',t2-t1) + + t1 = time() + test_data = pickle.load(open('data_test_converted.pkl','rb')) + test_data, testNameList, trainNameList = test_data['data'], list(test_data['testNameList']), list( + test_data['trainNameList']) + t2 = time() + print('load test',t2-t1) + + t1 = time() + tf_train = np.load('tf_train.npy') + centroids = np.load('centroids_train.npy') + clusters = np.load('clusters_train.npy') + t2 = time() + print('load tf/centroids/clusters',t2-t1) + + retriever = DataRetriever(tf_train,centroids,clusters) + + datum = np.random.choice(test_data,1)[0] + vis_boundary(datum.boundary) + + t1 = time() + index = retriever.retrieve_cluster(datum,k=10,multi_clusters=False) + t2 = time() + print('cluster',t2-t1) + data_retrieval = train_data[index] + vis_boundary(data_retrieval[0].boundary) + + t1 = time() + index = retriever.retrieve_bf(datum,k=10) + t2 = time() + print('bf',t2-t1) + data_retrieval = train_data[index] + vis_boundary(data_retrieval[0].boundary) diff --git a/static/Btopenjs/bootstrap.min.css b/static/Btopenjs/bootstrap.min.css new file mode 100644 index 0000000000000000000000000000000000000000..812b2cb29a2f3f86be331a9c6bb94d494ffbb629 --- /dev/null +++ b/static/Btopenjs/bootstrap.min.css @@ -0,0 +1,10 @@ +/*! + * Bootstrap v3.3.4 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +/*! + * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=9ce37640c80569c9e07b) + * Config saved to config.json and https://gist.github.com/9ce37640c80569c9e07b + *//*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:hover,a:focus{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:normal;line-height:1;color:#777}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{background-color:#fcf8e3;padding:.2em}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:bold}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0;text-align:right}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-15px}.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after{content:" ";display:table}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed} \ No newline at end of file diff --git a/static/Btopenjs/font-awesome.min.css b/static/Btopenjs/font-awesome.min.css new file mode 100644 index 0000000000000000000000000000000000000000..abffda8ee09d0d10aa126c35f978ea0bacdb4648 --- /dev/null +++ b/static/Btopenjs/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('font/fontawesome-webfont.eot?v=4.3.0');src:url('font/fontawesome-webfont.eot?#iefix&v=4.3.0') format('embedded-opentype'),url('font/fontawesome-webfont.woff2?v=4.3.0') format('woff2'),url('font/fontawesome-webfont.woff?v=4.3.0') format('woff'),url('font/fontawesome-webfont.ttf?v=4.3.0') format('truetype'),url('font/fontawesome-webfont.svg?v=4.3.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;transform:translate(0, 0)}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-genderless:before,.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"} \ No newline at end of file diff --git a/static/Btopenjs/gooey.min.css b/static/Btopenjs/gooey.min.css new file mode 100644 index 0000000000000000000000000000000000000000..98605944cdfe570cce5ab06d5e331559de6bbde7 --- /dev/null +++ b/static/Btopenjs/gooey.min.css @@ -0,0 +1,154 @@ +.navimenu { + position: relative; + min-width: 50px; + min-height: 50px; + box-sizing: border-box; + text-align: left; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-transform-style: preserve-3d; + outline: transparent solid 5px; + transform-style: preserve-3d +} + +.navimenu .gooey-menu-item, .navimenu .open-button { + border-radius: 100%; + position: absolute; + color: #fff; + text-align: center; + font-size: .9em; + transform: translate3d(0, 0, 0); + -ms-transform: translate3d(0, 0, 0); + -moz-transform: translate3d(0, 0, 0); + -o-transform: translate3d(0, 0, 0); + -webkit-transform: translate3d(0, 0, 0); + transition: transform ease-out 200ms; + -ms-transition: transform ease-out 200ms; + -o-transition: transform ease-out 200ms; + -moz-transition: transform ease-out 200ms; + -webkit-transition: transform ease-out 200ms; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-transform-style: preserve-3d; + outline: transparent solid 5px; + transform-style: preserve-3d +} + +.navimenu .menu-open { + display: none +} + +.navimenu .burger { + background: #fff; + display: block; + position: absolute; + top: 50%; + left: 50%; + margin-top: -1.5px; + transition: transform 200ms; + -ms-transition: transform 200ms; + -moz-transition: transform 200ms; + -webkit-transition: transform 200ms; + -o-transition: transform 200ms +} + +.navimenu .burger-1 { + transform: translate3d(0, -8px, 0); + -moz-transform: translate3d(0, -8px, 0); + -o-transform: translate3d(0, -8px, 0); + -webkit-transform: translate3d(0, -8px, 0); + -ms-transform: translate3d(0, -8px, 0) +} + +.navimenu .burger-2 { + transform: translate3d(0, 0, 0); + -ms-transform: translate3d(0, 0, 0); + -moz-transform: translate3d(0, 0, 0); + -o-transform: translate3d(0, 0, 0); + -webkit-transform: translate3d(0, 0, 0) +} + +.navimenu .burger-3 { + transform: translate3d(0, 8px, 0); + -ms-transform: translate3d(0, 8px, 0); + -moz-transform: translate3d(0, 8px, 0); + -o-transform: translate3d(0, 8px, 0); + -webkit-transform: translate3d(0, 8px, 0) +} + +.navimenu .menu-open:checked + .open-button .burger-1 { + transform: translate3d(0, 0, 0) rotate(45deg); + -ms-transform: translate3d(0, 0, 0) rotate(45deg); + -moz-transform: translate3d(0, 0, 0) rotate(45deg); + -o-transform: translate3d(0, 0, 0) rotate(45deg); + -webkit-transform: translate3d(0, 0, 0) rotate(45deg) +} + +.navimenu .menu-open:checked + .open-button .burger-2 { + transform: translate3d(0, 0, 0) scale(.1, 1); + -ms-transform: translate3d(0, 0, 0) scale(.1, 1); + -moz-transform: translate3d(0, 0, 0) scale(.1, 1); + -o-transform: translate3d(0, 0, 0) scale(.1, 1); + -webkit-transform: translate3d(0, 0, 0) scale(.1, 1) +} + +.navimenu .menu-open:checked + .open-button .burger-3 { + transform: translate3d(0, 0, 0) rotate(-45deg); + -ms-transform: translate3d(0, 0, 0) rotate(-45deg); + -moz-transform: translate3d(0, 0, 0) rotate(-45deg); + -webkit-transform: translate3d(0, 0, 0) rotate(-45deg); + -o-transform: translate3d(0, 0, 0) rotate(-45deg) +} + +.navimenu .gooey-menu-item:hover { + /*background-color: #4682b4;*/ + /*color: #00bcd4*/ +} + +.navimenu .gooey-menu-item { + transition-duration: 180ms; + -moz-transition-duration: 180ms; + -webkit-transition-duration: 180ms; + -o-transition-duration: 180ms +} + +.navimenu .open-button { + z-index: 2; + transition-timing-function: cubic-bezier(.175, .885, .32, 1.275); + -ms-transition-timing-function: cubic-bezier(.175, .885, .32, 1.275); + -moz-transition-timing-function: cubic-bezier(.175, .885, .32, 1.275); + -webkit-transition-timing-function: cubic-bezier(.175, .885, .32, 1.275); + -o-transition-timing-function: cubic-bezier(.175, .885, .32, 1.275); + transition-duration: 400ms; + -ms-transition-duration: 400ms; + -o-transition-duration: 400ms; + -moz-transition-duration: 400ms; + -webkit-transition-duration: 400ms; + transform: scale(1.1, 1.1) translate3d(0, 0, 0); + -ms-transform: scale(1.1, 1.1) translate3d(0, 0, 0); + -o-transform: scale(1.1, 1.1) translate3d(0, 0, 0); + -moz-transform: scale(1.1, 1.1) translate3d(0, 0, 0); + -webkit-transform: scale(1.1, 1.1) translate3d(0, 0, 0); + cursor: pointer +} + +.navimenu .open-button:hover { + transform: scale(1.2, 1.2) translate3d(0, 0, 0); + -ms-transform: scale(1.2, 1.2) translate3d(0, 0, 0); + -moz-transform: scale(1.2, 1.2) translate3d(0, 0, 0); + -o-transform: scale(1.2, 1.2) translate3d(0, 0, 0); + -webkit-transform: scale(1.2, 1.2) translate3d(0, 0, 0) +} + +.navimenu .menu-open:checked + .open-button { + transition: 200ms linear; + -ms-transition: 200ms linear; + -webkit-transition: 200ms linear; + -moz-transition: 200ms linear; + -o-transition: 200ms linear; + transform: scale(.9, .9) translate3d(0, 0, 0); + -ms-transform: scale(.9, .9) translate3d(0, 0, 0); + -o-transform: scale(.9, .9) translate3d(0, 0, 0); + -webkit-transform: scale(.9, .9) translate3d(0, 0, 0); + -moz-transform: scale(.9, .9) translate3d(0, 0, 0) +} diff --git a/static/Btopenjs/gooey.min.js b/static/Btopenjs/gooey.min.js new file mode 100644 index 0000000000000000000000000000000000000000..1afba994c6b27e9fc2cd89479a5a22673030912a --- /dev/null +++ b/static/Btopenjs/gooey.min.js @@ -0,0 +1,230 @@ +(function (g) { + g.gooeymenu = function (h, q) { + var k = g(h); + k.addClass("navimenu"); + var b = this, a = b.options = g.extend({}, g.gooeymenu.defaults, q); + b.els = {item: k.find(".gooey-menu-item"), checkbox: k.find(".menu-open"), button: k.find(".open-button")}; + b.methods = { + setup: function () { + var d = {small: 1.4, medium: 1.8, large: 2.1}, f = {small: 1.3, medium: 1.6, large: 2.1}, c; + for (c in d) a.margin === c ? a.margin = d[c] : null; + for (var e in f) a.bounceLength === e ? a.bounceLength = f[e] : null; + b.methods.responsiveStyles(); + // b.els.item.hover(function () { + // a.currentBg = + // // b.els.item.css("background-color"); + // g(this).css("background-color", a.hover) + // }, function () { + // g(this).css("background-color", a.currentBg) + // }); + !0 === a.bounce && b.methods.bounce() + }, setEvents: function () { + ["open", "close"].forEach(function (d, b) { + k.on(d, function () { + a[d] && a[d].apply(this, arguments) + }) + }) + }, bounce: function () { + if (!0 === a.bounce) { + var d = b.els.item.css("transition-timing-function"); + b.els.checkbox.on("change", function () { + g(this).is(":checked") ? b.els.item.css({ + "transition-timing-function": "cubic-bezier(0.8, 0.84, 0.44, " + + a.bounceLength + ")", + "-o-transition-timing-function": "cubic-bezier(0.8, 0.84, 0.44, " + a.bounceLength + ")", + "-moz-transition-timing-function": "cubic-bezier(0.8, 0.84, 0.44, " + a.bounceLength + ")", + "-webkit-transition-timing-function": "cubic-bezier(0.8, 0.84, 0.44, " + a.bounceLength + ")", + "-ms-transition-timing-function": "cubic-bezier(0.8, 0.84, 0.44, " + a.bounceLength + ")" + }) : b.els.item.css({ + "transition-timing-function": d, + "-moz-transition-timing-function": d, + "-o-transition-timing-function": d, + "-webkit-transition-timing-function": d + }) + }) + } + }, + circle: function () { + k.trigger("open"); + var d, f, c, e, m, h, n, r = b.els.item.length, l = a.transitionStep, q = Math.PI, t = 360 / r, + p = t = 360 / r; + f = a.circle.radius; + b.els.item.each(function () { + b.els.checkbox.is(":checked") ? (d = q * p / 180, c = Math.abs(Math.cos(d)), e = f * c, m = Math.sqrt(f * f - e * e), h = b.methods.periodCalc(p).x, n = b.methods.periodCalc(p).y, g(this).css({ + transform: "translate3d(" + h + e + "px," + n + m + "px,0)", + "-o-transform": "translate3d(" + h + e + "px," + n + m + "px,0)", + "-webkit-transform": "translate3d(" + h + e + "px," + n + m + "px,0)", + "-moz-transform": "translate3d(" + + h + e + "px," + n + m + "px,0)", + "-ms-transform": "translate3d(" + h + e + "px," + n + m + "px,0)", + "transition-duration": l + "ms", + "-o-transition-duration": l + "ms", + "-webkit-transition-duration": l + "ms", + "-moz-transition-duration": l + "ms" + }), p += t, l += a.transitionStep) : (b.els.item.css({ + transform: "translate3d(0, 0, 0)", + "-moz-transform": "translate3d(0, 0, 0)", + "-webkit-transform": "translate3d(0, 0, 0)", + "-ms-transform": "translate3d(0, 0, 0)", + "-o-transform": "translate3d(0, 0, 0)" + }), p = 360 / r, l = a.transitionStep, k.trigger("close")) + }) + }, periodCalc: function (a) { + return { + x: 90 > + a || 270 < a ? "" : "-", y: 180 < a ? "" : "-" + } + }, linear: function (d) { + k.trigger("open"); + var f = "horizontal" === a.style ? a.horizontal.menuItemPosition : a.vertical.menuItemPosition, + c = d[f].init, e = a.transitionStep; + b.els.item.each(function () { + b.els.checkbox.is(":checked") ? "horizontal" === a.style ? (g(this).css({ + transform: "translate3d(" + c + "px, 0, 0)", + "-ms-transform": "translate3d(" + c + "px, 0, 0)", + "-o-transform": "translate3d(" + c + "px, 0, 0)", + "-moz-transform": "translate3d(" + c + "px, 0, 0)", + "-webkit-transform": "translate3d(" + c + "px, 0, 0)", + "transition-duration": e + "ms", + "-o-transition-duration": e + "ms", + "-webkit-transition-duration": e + "ms", + "-moz-transition-duration": e + "ms" + }), c += d[f].init, e += a.transitionStep) : "vertical" === a.style && (g(this).css({ + "transition-duration": e + "ms", + "-moz-transition-duration": e + "ms", + "-o-transition-duration": e + "ms", + "-webkit-transition-duration": e + "ms" + }), "down" === a.vertical.direction ? g(this).css({ + transform: "translate3d(0, " + c + "px, 0)", + "-moz-transform": "translate3d(0, " + c + "px, 0)", + "-o-transform": "translate3d(0, " + c + + "px, 0)", + "-webkit-transform": "translate3d(0, " + c + "px, 0)", + "-ms-transform": "translate3d(0, " + c + "px, 0)" + }) : "up" === a.vertical.direction && g(this).css({ + transform: "translate3d(0,-" + c + "px, 0)", + "-moz-transform": "translate3d(0,-" + c + "px, 0)", + "-webkit-transform": "translate3d(0,-" + c + "px, 0)", + "-o-transform": "translate3d(0,-" + c + "px, 0)", + "-ms-transform": "translate3d(0,-" + c + "px, 0)" + }), c += d[f].init, e += a.transitionStep) : (b.els.item.css({ + transform: "translate3d(0, 0, 0)", + "-moz-transform": "translate3d(0, 0, 0)", + "-webkit-transform": "translate3d(0, 0, 0)", + "-ms-transform": "translate3d(0, 0, 0)", + "-o-transform": "translate3d(0, 0, 0)" + }), c = d[f].init, e = a.transitionStep, k.trigger("close")) + }) + }, translate: function () { + var d = {glue: {init: a.size}, spaced: {init: a.size * a.margin}}; + b.els.checkbox.on("change", function () { + b._callbacks[a.style](d) + }) + }, createOn: function (a, b, c, e) { + b = document.createElementNS("http://www.w3.org/2000/svg", b); + for (var g in c) c.hasOwnProperty(g) && b.setAttribute(g, c[g]); + e && b.appendChild(document.createTextNode(e)); + return a.appendChild(b) + }, responsiveStyles: function () { + var d = + 0 < window.innerWidth ? window.innerWidth : screen.width; + 320 <= d && 480 >= d ? (a.size /= 1.4, a.circle.radius /= 1.2) : 480 < d && 768 >= d ? a.size /= 1.2 : 780 < d && 1024 >= d && (a.circle.radius /= 1.2, a.size /= 1.1); + b.els.item.css({ + width: a.size + "px", + height: a.size + "px", + color: a.contentColor, + // "background-color": a.bgColor, + "line-height": a.size + "px" + }); + b.els.button.css({ + width: a.size + "px", + height: a.size + "px", + // "background-color": a.bgColor, + "line-height": a.size + "px" + }); + k.find(".burger").css({ + "font-size": ".8em", width: a.size / 2 + "px", height: "3px", left: a.size / + 4 + "px" + }) + } + }; + b._callbacks = {vertical: b.methods.linear, horizontal: b.methods.linear, circle: b.methods.circle}; + b.init = function () { + var a = document.createElementNS("http://www.w3.org/2000/svg", "svg"), f = g(".navimenu").index(k); + a.setAttribute("id", "gooeySVG" + f); + a.setAttribute("class", "gooeySVG"); + k.append(a); + a = document.getElementById("gooeySVG" + f); + b.methods.createOn(a, "defs", {id: "defs" + f}); + a = document.getElementById("defs" + f); + b.methods.createOn(a, "filter", {id: "goo-shadow" + f, overflow: "hidden"}); + var c = document.getElementById("goo-shadow" + + f); + b.methods.createOn(c, "feGaussianBlur", {"in": "SourceGraphic", result: "blur", stdDeviation: "10"}); + b.methods.createOn(c, "feColorMatrix", { + "in": "blur", + mode: "matrix", + values: "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -8", + result: "goo" + }); + b.methods.createOn(c, "feGaussianBlur", {"in": "goo", stdDeviation: "2", result: "shadow"}); + b.methods.createOn(c, "feColorMatrix", { + "in": "shadow", + mode: "matrix", + values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", + result: "shadow" + }); + b.methods.createOn(c, "feOffset", { + "in": "shadow", dx: "1", + dy: "1", result: "shadow" + }); + b.methods.createOn(c, "feComposite", {in2: "shadow", "in": "goo", result: "goo"}); + b.methods.createOn(c, "feComposite", {in2: "goo", "in": "SourceGraphic", result: "mix"}); + b.methods.createOn(a, "filter", {id: "goo" + f}); + a = document.getElementById("goo" + f); + b.methods.createOn(a, "feGaussianBlur", {"in": "SourceGraphic", result: "blur", stdDeviation: "10"}); + b.methods.createOn(a, "feColorMatrix", { + "in": "blur", + mode: "matrix", + values: "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7", + result: "goo" + }); + b.methods.createOn(a, + "feComposite", {in2: "goo", "in": "SourceGraphic", result: "mix"}); + k.css({ + "-webkit-filter": "url('#goo-shadow" + f + "')", + filter: "url('#goo-shadow" + f + "')", + "-ms-filter": "url('#goo-shadow" + f + "')", + "-o-filter": "url('#goo-shadow" + f + "')" + }); + b.methods.setEvents(); + b.methods.setup(); + b.methods.translate.apply(this, arguments) + }; + b.init() + }; + g.gooeymenu.defaults = { + style: "horizontal", + size: 70, + margin: "medium", + // bgColor: "steelblue", + contentColor: "white", + transitionStep: 100, + bounce: !1, + bounceLength: "medium", + hover: "white", + circle: {radius: 80}, + horizontal: {menuItemPosition: "glue"}, + vertical: {menuItemPosition: "spaced", direction: "up"}, + open: function () { + }, + close: function () { + } + }; + g.fn.gooeymenu = function (h) { + void 0 === h && (h = {}); + if (h && "object" === typeof h) return this.each(function () { + new g.gooeymenu(this, h) + }) + } +})(jQuery); diff --git a/static/Btopenjs/jquery-2.1.3.min.js b/static/Btopenjs/jquery-2.1.3.min.js new file mode 100644 index 0000000000000000000000000000000000000000..25714ed29ab6fcf0355da4b45ac602fac0154efb --- /dev/null +++ b/static/Btopenjs/jquery-2.1.3.min.js @@ -0,0 +1,4 @@ +/*! jQuery v2.1.3 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.3",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)+1>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=hb(),z=hb(),A=hb(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},eb=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fb){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function gb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+rb(o[l]);w=ab.test(a)&&pb(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function hb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ib(a){return a[u]=!0,a}function jb(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function kb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function lb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function nb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function ob(a){return ib(function(b){return b=+b,ib(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pb(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=gb.support={},f=gb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=gb.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",eb,!1):e.attachEvent&&e.attachEvent("onunload",eb)),p=!f(g),c.attributes=jb(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=jb(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=jb(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(jb(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),jb(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&jb(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return lb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?lb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},gb.matches=function(a,b){return gb(a,null,null,b)},gb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return gb(b,n,null,[a]).length>0},gb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},gb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},gb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},gb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=gb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=gb.selectors={cacheLength:50,createPseudo:ib,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||gb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&gb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=gb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||gb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ib(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ib(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ib(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ib(function(a){return function(b){return gb(a,b).length>0}}),contains:ib(function(a){return a=a.replace(cb,db),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ib(function(a){return W.test(a||"")||gb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:ob(function(){return[0]}),last:ob(function(a,b){return[b-1]}),eq:ob(function(a,b,c){return[0>c?c+b:c]}),even:ob(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:ob(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:ob(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:ob(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function tb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ub(a,b,c){for(var d=0,e=b.length;e>d;d++)gb(a,b[d],c);return c}function vb(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wb(a,b,c,d,e,f){return d&&!d[u]&&(d=wb(d)),e&&!e[u]&&(e=wb(e,f)),ib(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ub(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:vb(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=vb(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=vb(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sb(function(a){return a===b},h,!0),l=sb(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sb(tb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wb(i>1&&tb(m),i>1&&rb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xb(a.slice(i,e)),f>e&&xb(a=a.slice(e)),f>e&&rb(a))}m.push(c)}return tb(m)}function yb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=vb(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&gb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ib(f):f}return h=gb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,yb(e,d)),f.selector=a}return f},i=gb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&pb(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&rb(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&pb(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=jb(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),jb(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||kb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&jb(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||kb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),jb(function(a){return null==a.getAttribute("disabled")})||kb(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),gb}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+K.uid++}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){return M.access(a,b,c) +},removeData:function(a,b){M.remove(a,b)},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthx",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,bb=/<([\w:]+)/,cb=/<|&#?\w+;/,db=/<(?:script|style|link)/i,eb=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/^$|\/(?:java|ecma)script/i,gb=/^true\/(.*)/,hb=/^\s*\s*$/g,ib={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ib.optgroup=ib.option,ib.tbody=ib.tfoot=ib.colgroup=ib.caption=ib.thead,ib.th=ib.td;function jb(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function kb(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function lb(a){var b=gb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function mb(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function nb(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function ob(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pb(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=ob(h),f=ob(a),d=0,e=f.length;e>d;d++)pb(f[d],g[d]);if(b)if(c)for(f=f||ob(a),g=g||ob(h),d=0,e=f.length;e>d;d++)nb(f[d],g[d]);else nb(a,h);return g=ob(h,"script"),g.length>0&&mb(g,!i&&ob(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(cb.test(e)){f=f||k.appendChild(b.createElement("div")),g=(bb.exec(e)||["",""])[1].toLowerCase(),h=ib[g]||ib._default,f.innerHTML=h[1]+e.replace(ab,"<$1>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=ob(k.appendChild(e),"script"),i&&mb(f),c)){j=0;while(e=f[j++])fb.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(ob(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&mb(ob(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(ob(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(ab,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ob(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(ob(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&eb.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(ob(c,"script"),kb),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,ob(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,lb),j=0;g>j;j++)h=f[j],fb.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(hb,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qb,rb={};function sb(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function tb(a){var b=l,c=rb[a];return c||(c=sb(a,b),"none"!==c&&c||(qb=(qb||n("